- Qwen3.5-122B-A10B is a free model from Alibaba's Qwen team that you can run on your own computer. In our tests it's a solid all-rounder: 91 out of 100, #7 of 63.
- It solved 26 of 30 coding jobs and scored 96 on reading documents. On our hardest tasks it scored 79.
- 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. Qwen3.5-122B-A10B got 26 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.5-122B-A10B scored 96; the best model scored 100.
| Test | Score | Public questions | Secret questions |
|---|---|---|---|
| Coding | 87 | 86 | 87 |
| Reading documents | 96 | 98 | 96 |
| Decisions | 99 | 100 | 99 |
On the 18 hardest tasks (included in the scores above) it scored 79. 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 Qwen3.5-122B-A10B 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 | No | – |
| Mac, 128 GB | Yes | Q5 |
For normal-length chats. Long documents need more memory. Q4 or Q8? · How we worked this out
How to try it
Search for "Qwen3.5-122B-A10B 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.5-122B-A10B wrote for our public questions, word for word.
Coding: INI parser (hard) Didn't work
```python
import re
def parse_ini(text: str) -> dict[str, dict[str, str]]:
"""
Parses an INI dialect text into a dictionary of sections and key-values.
"""
sections = {}
defaults = {}
current_section = None
last_key = None
lines = text.splitlines()
# Helper to process value strings (handling quotes and comments)
def process_value(val_str: str, is_quoted_check: bool = True) -> str:
# Rule 2: Value stripped
val_str = val_str.strip()
if not val_str:
return ""
# Rule 5: Quoted value
if val_str.startswith('"'):
end_quote = val_str.find('"', 1)
if end_quote == -1:
raise ValueError("Unclosed quote in value")
# Content inside quotes, no stripping, no comment processing
return val_str[1:end_quote]
# Rule 4: Inline comment
# Starts at ` ;` or ` #`
idx_semi = val_str.find(' ;')
idx_hash = val_str.find(' #')
idx = -1
if idx_semi != -1 and idx_hash != -1:
idx = min(idx_semi, idx_hash)
elif idx_semi != -1:
idx = idx_semi
elif idx_hash != -1:
idx = idx_hash
if idx != -1:
return val_str[:idx].strip()
return val_str
# Helper to resolve interpolation
def resolve_interpolation(value: str, sec_name: str, key_name: str,
all_sections: dict, visited: set) -> str:
if (sec_name, key_name) in visited:
raise ValueError("Interpolation cycle detected")
# Optimization: check if interpolation is needed
if '${' not in value:
return value
# Create a new visited set for this branch to track current path
current_visited = visited | {(sec_name, key_name)}
# We need to resolve recursively.
# Since replacement can introduce new placeholders, we loop.
# But we must be careful not to infinite loop if logic is flawed.
# Using a loop to replace one placeholder at a time ensures we dive deep.
# Pattern for ${...}
# We want to find the *first* match to resolve it fully before moving to the next
# to handle nested dependencies correctly within the string.
while True:
match = re.search(r'\$\{([^}]+)\}', value)
if not match:
break
ref = match.group(1)
m_start, m_end = match.span()
# Determine target section and key
if ':' in ref:
target_sec, target_key = ref.split(':', 1)
else:
target_sec = sec_name
target_key = ref
# Lookup logic
# Priority: Same section > DEFAULT (if implicit)
# If explicit section, strict lookup.
resolved_val = None
found = False
if target_sec in all_sections:
if target_key in all_sections[target_sec]:
resolved_val = all_sections[target_sec][target_key]
found = True
# If explicit section provided but key missing -> KeyError below
# If not found in explicit target, and target_sec is implicit (same as current)
# Check DEFAULT
if not found and target_sec == sec_name:
if "DEFAULT" in all_sections:
if target_key in all_sections["DEFAULT"]:
resolved_val = all_sections["DEFAULT"][target_key]
found = True
if not found:
# Raise KeyError for missing key/section
if target_sec not in all_sections:
raise KeyError(f"Section '{target_sec}' not found")
else:
raise KeyError(f"Key '{target_key}' not found in section '{target_sec}'")
# Recursively resolve the found value
# Pass the same visited set logic to detect cycles
final_resolved = resolve_interpolation(resolved_val, target_sec, target_key, all_sections, current_visited)
# Replace the placeholder
value = value[:m_start] + final_resolved + value[m_end:]
# Safety break if no change happened (shouldn't happen with valid logic)
# But strictly speaking, we replaced a placeholder with its content.
# If content contained the same placeholder, it would recurse.
# If content was identical to placeholder (impossible as placeholder is ${...}),
# we are good.
return value
# Parsing Loop
for i, line in enumerate(lines):
# Rule 3: Empty lines or comment lines
stripped = line.lstrip()
if not stripped or stripped.startswith(';') or stripped.startswith('#'):
continue
# Rule 6: Continuation line
# "A line that starts with whitespace"
# Note: We already checked if lstrip is empty (Rule 3).
# So if we are here, line has content.
# If it starts with whitespace, it's a continuation.
if line[0].isspace():
if current_section is None or last_key is None:
raise ValueError(f"Line {i}: Continuation line without preceding key")
# Process content for comments (Rule 4)
# Rule 6: "stripped text is appended"
cont_text = line.strip()
# Apply comment stripping logic manually or via helper
# Helper assumes full value processing.
# For continuation, the whole line IS the value part.
# So we can reuse process_value logic roughly, but it expects a value string.
# Let's inline comment stripping for safety.
# Check quotes in continuation
if cont_text.startswith('"'):
end_q = cont_text.find('"', 1)
if end_q == -1:
raise ValueError(f"Line {i}: Unclosed quote in continuation")
cont_text = cont_text[1:end_q]
else:
# Check inline comment
idx_s = cont_text.find(' ;')
idx_h = cont_text.find(' #')
idx = -1
if idx_s != -1 and idx_h != -1:
idx = min(idx_s, idx_h)
elif idx_s != -1:
idx = idx_s
elif idx_h != -1:
idx = idx_h
if idx != -1:
cont_text = cont_text[:idx].strip()
# Append
sections[current_section][last_key] += "\n" + cont_text
continue
# Rule 1: Section Header
sec_match = re.match(r'^\s*\[\s*(.*?)\s*\]\s*$', line)
if sec_match:
sec_name = sec_match.group(1)
current_section = sec_name
last_key = None
if sec_name.upper() == "DEFAULT":
# Ensure defaults dict exists
if "DEFAULT" not in sections:
sections["DEFAULT"] = {}
else:
if sec_name not in sections:
sections[sec_name] = {}
continue
# Rule 2: Key=Value
if current_section is None:
raise ValueError(f"Line {i}: Key defined before section header")
# Find separator
eq_idx = line.find('=')
col_idx = line.find(':')
sep_idx = -1
sep_char = None
if eq_idx != -1 and col_idx != -1:
sep_idx = min(eq_idx, col_idx)
sep_char = line[sep_idx]
elif eq_idx != -1:
sep_idx = eq_idx
sep_char = '='
elif col_idx != -1:
sep_idx = col_idx
sep_char = ':'
else:
raise ValueError(f"Line {i}: Malformed line (no separator)")
key_part = line[:sep_idx]
val_part = line[sep_idx+1:]
# Key processing
key = key_part.strip().lower()
if not key:
raise ValueError(f"Line {i}: Empty key")
# Value processing
val = process_value(val_part)
# Store
if current_section.upper() == "DEFAULT":
# We store DEFAULT in sections["DEFAULT"] temporarily for resolution
sections["DEFAULT"][key] = val
else:
sections[current_section][key] = val
last_key = key
# Interpolation Phase
# Prepare a unified view for resolution
# Note: We need to separate DEFAULT from final output
default_keys = {}
if "DEFAULT" in sections:
default_keys = sections.pop("DEFAULT")
# Re-add temporarily for resolution logic if needed,
# but my resolve_interpolation logic handles "DEFAULT" key in lookup.
# So I need to pass default_keys into the resolution context.
# To make resolution easier, I'll reconstruct the 'all_sections' dict
# containing DEFAULT for the lookup phase, then remove it from result.
all_sections = dict(sections)
if default_keys:
all_sections["DEFAULT"] = default_keys
# Resolve all values
final_sections = {}
for sec_name, sec_dict in all_sections.items():
resolved_sec = {}
for k, v in sec_dict.items():
try:
resolved_sec[k] = resolve_interpolation(v, sec_name, k, all_sections, set())
except KeyError:
# Propagate KeyError for missing interpolation targets
raise
except ValueError:
# Propagate ValueError for cycles
raise
final_sections[sec_name] = resolved_sec
# Remove DEFAULT from final result (Rule 8)
final_sections.pop("DEFAULT", None)
return final_sections
``` Coding: TTL LRU (hard) Worked
```python
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 in self._cache:
# Update existing key: refresh expiry and recency
self._cache.move_to_end(key)
self._cache[key] = (value, now + self._ttl)
else:
# Insert new key
# Check if capacity would be exceeded
if len(self._cache) >= self._capacity:
# First remove all expired entries
expired_keys = [k for k, (_, exp) in self._cache.items() if now >= exp]
for k in expired_keys:
del self._cache[k]
self._stats["expirations"] += 1
# If still full, evict LRU
if len(self._cache) >= self._capacity:
self._cache.popitem(last=False)
self._stats["evictions"] += 1
# Insert the new key
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()
if key not in self._cache:
self._stats["misses"] += 1
return default
val, exp = self._cache[key]
if now >= exp:
# Entry expired
del self._cache[key]
self._stats["expirations"] += 1
self._stats["misses"] += 1
return default
# Entry valid
self._cache.move_to_end(key)
self._stats["hits"] += 1
return val
def __len__(self):
now = self._clock()
count = 0
for _, (_, exp) in self._cache.items():
if now < exp:
count += 1
return count
def stats(self):
return dict(self._stats)
``` Coding: Parse duration Worked
```python
import re
def parse_duration(s: str) -> int:
"""
Parse a duration string into total seconds.
Args:
s: Duration string (e.g., "1h30m", "45s", "1h 5m 10s")
Returns:
Total number of seconds as an integer
Raises:
ValueError: For invalid input formats
"""
# Check if string is empty or blank
if not s or not s.strip():
raise ValueError("Empty or blank string")
# Strip leading/trailing whitespace
s = s.strip()
# Pattern to match number followed by unit (with optional whitespace between)
pattern = r'(\d+)\s*([hmsHMS])'
# Find all matches
matches = re.findall(pattern, s)
# Check if there are any matches
if not matches:
raise ValueError("No valid duration parts found")
# Verify that the entire string (without spaces) consists only of matched parts
# Build what we expect from matches
expected_parts = ''.join(f'{num}{unit}' for num, unit in matches)
# Compare with original string without spaces
s_normalized = s.replace(' ', '')
if expected_parts != s_normalized:
raise ValueError("Invalid characters or format")
# Define unit multipliers
unit_multipliers = {'h': 3600, 'm': 60, 's': 1}
unit_order = ['h', 'm', 's']
seen_units = set()
last_unit_index = -1
total_seconds = 0
for num_str, unit in matches:
# Convert unit to lowercase
unit_lower = unit.lower()
# Check if unit is valid
if unit_lower not in unit_multipliers:
raise ValueError(f"Unknown unit: {unit}")
# Check if unit was already seen
if unit_lower in seen_units:
raise ValueError(f"Repeated unit: {unit}")
# Check order
current_index = unit_order.index(unit_lower)
if current_index < last_unit_index:
raise ValueError("Units out of order")
seen_units.add(unit_lower)
last_unit_index = current_index
# Add to total seconds
total_seconds += int(num_str) * unit_multipliers[unit_lower]
return total_seconds
``` Coding: Summarize ranges Worked
```python
def summarize_ranges(nums: list[int]) -> str:
if not nums:
return ""
# Sort and remove duplicates
unique_nums = sorted(set(nums))
parts = []
start = unique_nums[0]
prev = unique_nums[0]
for num in unique_nums[1:]:
if num == prev + 1:
prev = num
else:
# End of a consecutive run
if prev - start >= 2:
parts.append(f"{start}..{prev}")
elif prev - start == 1:
parts.append(f"{start},{prev}")
else:
parts.append(str(start))
start = num
prev = num
# Handle the final run
if prev - start >= 2:
parts.append(f"{start}..{prev}")
elif prev - start == 1:
parts.append(f"{start},{prev}")
else:
parts.append(str(start))
return ",".join(parts)
``` Coding: Top customers Worked
```python
def top_customers(rows: list[dict], n: int) -> list[tuple[str, float]]:
def parse_amount(s: str) -> float | None:
if not isinstance(s, str):
return None
# Remove currency symbols and thousands separators, then trim whitespace
cleaned = s.replace('$', '').replace(',', '').strip()
if not cleaned:
return None
try:
return float(cleaned)
except ValueError:
return None
# Dictionary to aggregate totals per customer
# Key: normalized name (lowercase)
# Value: (canonical_name_from_first_valid_row, accumulated_total)
customers: dict[str, tuple[str, float]] = {}
for row in rows:
if not isinstance(row, dict):
continue
raw_name = row.get("customer")
raw_amount = row.get("amount")
# Validate customer name exists and is a string
if not isinstance(raw_name, str):
continue
trimmed_name = raw_name.strip()
if not trimmed_name:
continue
# Parse amount
amount = parse_amount(raw_amount)
if amount is None:
continue
# Determine group key (case-insensitive)
norm_name = trimmed_name.lower()
# Initialize if first valid encounter for this customer
if norm_name not in customers:
customers[norm_name] = (trimmed_name, 0.0)
# Update total
current_name, current_total = customers[norm_name]
customers[norm_name] = (current_name, current_total + amount)
# Prepare output list with rounded totals
result = [(name, round(total, 2)) for name, total in customers.values()]
# Sort by total descending, then name ascending
result.sort(key=lambda x: (-x[1], x[0]))
# Return top n
return result[:n]
``` Coding: Slugify Worked
```python
import unicodedata
import re
def slugify(title: str, max_len: int = 50) -> str:
# 1. Transliterate accented characters to ASCII and drop other non-ASCII
# NFKD decomposes characters (e.g., é -> e + combining accent)
# encode('ascii', 'ignore') removes non-ASCII chars (including combining accents)
text = unicodedata.normalize('NFKD', title)
text = text.encode('ascii', 'ignore').decode('ascii')
# 2. Lowercase
text = text.lower()
# 3. Replace runs of non-alphanumeric characters with a single '-' and strip edges
# [^a-z0-9] matches anything that is not a lowercase letter or digit
slug = re.sub(r'[^a-z0-9]+', '-', text)
slug = slug.strip('-')
# 4. Truncate if longer than max_len
if len(slug) <= max_len:
return slug
# If slug became empty during processing
if not slug:
return ""
parts = slug.split('-')
# If the first word is already longer than max_len, hard-cut it
if len(parts[0]) > max_len:
return parts[0][:max_len]
# Keep the longest prefix of whole words within max_len
current_len = 0
kept_parts = []
for i, part in enumerate(parts):
# Length includes the separator '-' if it's not the first word
needed = len(part) + (1 if i > 0 else 0)
if current_len + needed <= max_len:
current_len += needed
kept_parts.append(part)
else:
break
return '-'.join(kept_parts)
``` 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_update_time = clock()
def _refill(self):
now = self._clock()
elapsed = now - self._last_update_time
if elapsed > 0:
# Calculate new token count based on elapsed time
self._tokens = min(
self._capacity,
self._tokens + elapsed * self._refill_per_sec
)
# Update the last update time to prevent double-counting
self._last_update_time = now
@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
``` 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) 100% right
{
"severity": {
"0": 0.0,
"1": 0.99,
"2": 0.01,
"3": 0.0
},
"page": {
"true": 0.01,
"false": 0.99
}
} 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.01,
"false": 0.99
},
"action_needed": {
"true": 0.01,
"false": 0.99
}
} Decisions: Meeting slot (hard) 100% right
{
"slot": {
"A": 0.0,
"B": 1.0,
"C": 0.0,
"D": 0.0
},
"raj_last": {
"true": 1.0,
"false": 0.0
}
} Decisions: Review mixed (hard) 100% right
{
"hardware": {
"true": 0.99,
"false": 0.01
},
"support": {
"true": 0.99,
"false": 0.01
}
} Decisions: Support checkout down 100% right
{
"department": {
"billing": 0.05,
"technical": 0.95,
"account": 0.0,
"shipping": 0.0,
"sales": 0.0
},
"urgency": {
"0": 0.0,
"1": 0.0,
"2": 0.01,
"3": 0.99
},
"outage": {
"true": 0.99,
"false": 0.01
}
} Decisions: Refund wrong plan 100% right
{
"department": {
"billing": 0.99,
"technical": 0.0,
"account": 0.0,
"shipping": 0.0,
"sales": 0.01
},
"refund": {
"true": 1.0,
"false": 0.0
},
"tone": {
"frustrated": 0.0,
"calm": 1.0
}
} Decisions: Moderation doxxing 100% right
{
"policy": {
"none": 0.01,
"harassment": 0.98,
"hate": 0.01,
"spam": 0.0,
"self_harm": 0.0
},
"personal_info": {
"true": 0.99,
"false": 0.01
}
} Decisions: Route calendar 100% right
{
"tool": {
"web_search": 0.0,
"calculator": 0.0,
"calendar": 0.98,
"email": 0.02,
"none": 0.0
},
"confirm": {
"true": 0.98,
"false": 0.02
}
} 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": 0.99,
"false": 0.01
},
"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.01,
"financial": 0.0,
"government_id": 0.99,
"health": 0.0
},
"sensitive": {
"true": 1.0,
"false": 0.0
}
} Decisions: Review mixed 100% right
{
"sentiment": {
"positive": 0.01,
"neutral": 0.04,
"negative": 0.95
},
"defect": {
"true": 0.98,
"false": 0.02
},
"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.00,
"monthly_rent_from_2025_06_01": 2236.00,
"late_fee_from_2025_06_01": 111.80,
"security_deposit": 2150.00,
"total_pet_deposits": 800.00,
"total_monthly_payment_july_2025": 2306.00,
"move_in_payment": 4700.00
} 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: 125B parameters. First tested OCT 11.
Comments
Sign in with GitHub to comment. Spam and abuse are hidden automatically.