DeepSeek V4.1-Flash-1017: 77.0 on DeepSWE, MIT Weights and a 1M-Token Context

·11 min read·Evergreen Tools Team

On October 17, 2026, DeepSeek pushed DeepSeek-V4.1-Flash-1017 to Hugging Face and called it a minor update to DeepSeek-V4.1-Flash. Terminal-Bench 4.0 moves from 31.2 to 42.3, ProgramBench from 20.3 to 29.1, DeepSWE v1.1 from 74.2 to 77.0. That last number is above Claude Opus 5's 74.0 on the same benchmark. Meanwhile HLE without tools sits at 42.6 against Opus 5's 56.3, and ProgramBench still trails at 29.1 versus 37.0. So the honest reading is not that this is a frontier knowledge model. It is that DeepSeek shipped an agent-shaped model with MIT weights, a 1M-token context window, and API pricing that changes where you can afford to run it. Here is what the card says, and what to check before moving traffic.

Close-up of a circuit board

A minor version bump that moved Terminal-Bench 4.0 by eleven points is not a minor version bump.

What actually shipped

V4.1-Flash-1017 improves reasoning, coding, agentic tasks and visual benchmarks, and it reduces task execution time and output length, which the card frames as advancing the performance-cost Pareto frontier across multiple agent harnesses. Weights and repository are MIT licensed. The context window is 1M tokens, and the recommended sampling parameters are temperature 1.0 and top_p 1.0 with max_tokens of at least 256K.

The part most people will skip is the prompt encoding section. This release does not include a Jinja-format chat template. Instead there is an encoding folder containing a self-contained Python reference implementation with test cases for multi-turn conversations, tool calling, thinking mode, numeric reasoning effort, mid-conversation system messages and interleaved image content, plus deepseek-recipe: Rust libraries with Python bindings that convert Messages, Chat Completions and Responses API requests into the DeepSeek conversation format and parse output back into complete or streamed responses.

If your stack assumes a drop-in template, that assumption is now a migration task.

# Search & Fetch are now first-class tools on the DeepSeek API (api-docs.deepseek.com/guides/search)
import requests

API = "https://api.deepseek.com"
HEADERS = {"Authorization": "Bearer <DeepSeek API Key>", "Content-Type": "application/json"}

resp = requests.post(
    f"{API}/v1/tools/search",
    headers=HEADERS,
    json={
        "query": 'deepseek "release notes" site:huggingface.co OR site:github.com lang:en after:2026-09',
        "count": 5,
        "need_content": True,
    },
    timeout=60,
)
resp.raise_for_status()
for r in resp.json()["results"]:
    # rerank_score runs 0-3; the docs treat 1.5+ as high quality
    print(round(r["rerank_score"], 2), r["date"], r["title"], r["url"])

Reading the benchmark table without lying to yourself

The card compares against K3, GLM-5.3, GPT-5.6 Sol and Opus 5. Two footnotes matter more than the numbers: the HLE figures marked with a dagger are the text-only subset, and the runs use specific harnesses, Claude Code for Terminal-Bench 4.0 and Mini-SWE-V2 for DeepSWE v1.1. Harness choice moves agent scores by more than some of these deltas.

Sort by improvement instead of absolute score and a clear shape appears. The gains concentrate in execution: Terminal-Bench 4.0 up 11.1, ProgramBench up 8.8, Apex Bench up 7.5, Automation-Bench up 5.7. The knowledge benchmark barely moves, 36.8 to 42.6, and stays well behind Opus 5. That is a coherent product decision. DeepSeek is not trying to win trivia. It is trying to win the loop where a model runs commands, reads errors and tries again.

So when someone tells you this model beats Opus 5, ask which column. DeepSWE, yes, 77.0 to 74.0. ProgramBench, no. NL2Repo, no. A single headline is always a summary of a table you have not read yet.

# Read the model card table as deltas against the previous checkpoint, not as absolute truth.
CARD = {
    # benchmark: (v4.1-flash-1017, v4.1-flash, opus-5)
    "HLE w/o tools":        (42.6, 36.8, 56.3),
    "Terminal-Bench 4.0":   (42.3, 31.2, 51.8),
    "DeepSWE v1.1":         (77.0, 74.2, 74.0),
    "ProgramBench":         (29.1, 20.3, 37.0),
    "NL2Repo-Bench":        (68.2, 65.4, 75.3),
    "Automation-Bench":     (60.5, 54.8, 50.3),
    "Agents' Last Exam":    (35.4, 31.8, 28.6),
    "Apex Bench v1.1":      (72.1, 64.6, 65.8),
}

print(f"{'benchmark':22}{'delta vs prev':>14}{'vs Opus 5':>12}")
for name, (new, prev, opus) in sorted(CARD.items(), key=lambda kv: kv[1][0] - kv[1][1], reverse=True):
    print(f"{name:22}{new - prev:+14.1f}{new - opus:+12.1f}")

# Read: +11.1 on Terminal-Bench 4.0 is a real jump for agent work.
# But ProgramBench and NL2Repo still trail, so "beats Opus 5" is only true on DeepSWE.

The cost sheet is the actual feature

The API model id is deepseek-flash, context 1M, maximum output 384K, concurrency limit 2,500. Pricing per million tokens: cache-hit input is $0.003 off-peak and $0.006 at peak, cache-miss input is $0.15 off-peak and $0.30 at peak, output is $0.60 off-peak and $1.20 at peak. Peak hours are 01:00 to 04:00 and 06:00 to 10:00 UTC, Monday through Friday, excluding Chinese public holidays. Everything else, including weekends, is off-peak at half price.

A run that burns 1M cache-miss input tokens and 1M output tokens costs $1.50 inside a peak window and $0.75 outside one. Same tokens, same model, half the bill. Most batch pipelines I have seen run their nightly jobs at exactly the wrong hour because nobody looked up the definition of peak. One line of scheduler code, 50% off.

# Peak hours are published, so cost per run is a scheduling decision, not a constant.
from datetime import datetime, timezone

# deepseek-flash, USD per 1M tokens (api-docs.deepseek.com/quick_start/pricing)
PRICE = {
    "cache_hit":  {"off": 0.003, "peak": 0.006},
    "cache_miss": {"off": 0.15,  "peak": 0.30},
    "output":     {"off": 0.60,  "peak": 1.20},
}
PEAK_BLOCKS = [(1, 4), (6, 10)]  # UTC, Monday-Friday, excluding Chinese public holidays

def is_peak(ts: datetime) -> bool:
    if ts.weekday() >= 5:
        return False
    return any(start <= ts.hour < end for start, end in PEAK_BLOCKS)

def run_cost(in_miss, out, ts, in_hit=0.0):
    band = "peak" if is_peak(ts) else "off"
    total = (
        in_hit / 1e6 * PRICE["cache_hit"][band]
        + in_miss / 1e6 * PRICE["cache_miss"][band]
        + out / 1e6 * PRICE["output"][band]
    )
    return band, round(total, 4)

now = datetime(2026, 10, 19, 2, 0, tzinfo=timezone.utc)   # inside the 01:00-04:00 UTC window
print(run_cost(1_000_000, 1_000_000, now))                 # ('peak', 1.5)
later = now.replace(hour=12)
print(run_cost(1_000_000, 1_000_000, later))               # ('off', 0.75)
# Same tokens, half the bill. Batch jobs should not run at 02:00 UTC on a Tuesday.
Server racks in a data centre

Half price outside the published peak windows. The scheduler is the cheapest optimisation available.

Output length is a pricing feature, not a footnote

The card leads with shorter outputs and faster task completion, and in an agent loop that is where the money is. Input tokens get cheaper every quarter; output tokens do not, because the model has to generate them one at a time. A model that finishes a task in 3,500 output tokens instead of 4,200 is roughly 17% cheaper per task before you count the wall-clock savings from fewer turns.

Measure output tokens per completed task, not per request: a short answer that needs three retries costs more than a chatty one that lands first time. The gate below couples pass rate with verbosity and retries.

# A migration gate: same harness, same tasks, old checkpoint vs new checkpoint.
import statistics

def score_run(results):
    done = [r for r in results if r["completed"]]
    return {
        "pass_rate": len(done) / len(results),
        "tool_call_valid": sum(r["valid_tool_calls"] for r in results) / max(1, sum(r["tool_calls"] for r in results)),
        "out_tokens_per_task": statistics.mean(r["output_tokens"] for r in results),
        "retries_per_task": statistics.mean(r["retries"] for r in results),
    }

def gate(baseline, candidate, min_pass=0.0, max_out_ratio=1.0):
    verdict = {
        "pass_rate_ok": candidate["pass_rate"] >= baseline["pass_rate"] + min_pass,
        "tool_calls_ok": candidate["tool_call_valid"] >= baseline["tool_call_valid"] - 0.005,
        "verbosity_ok": candidate["out_tokens_per_task"] <= baseline["out_tokens_per_task"] * max_out_ratio,
        "retries_ok": candidate["retries_per_task"] <= baseline["retries_per_task"],
    }
    verdict["ship"] = all(verdict.values())
    return verdict

# Fill these with your own 20-task suite, run through the identical harness
# (the card itself uses the Claude Code harness for Terminal-Bench and Mini-SWE-V2 for DeepSWE).
print(gate({"pass_rate": 0.55, "tool_call_valid": 0.98, "out_tokens_per_task": 4200, "retries_per_task": 0.6},
           {"pass_rate": 0.65, "tool_call_valid": 0.99, "out_tokens_per_task": 3500, "retries_per_task": 0.4}))

Search and Fetch are now part of the tool surface

DeepSeek also opened Search and Fetch under /v1/tools, using the same bearer auth as the main API. Search takes a query of 1 to 128 characters, an optional count between 1 and 50, an optional original question to improve query understanding, an optional localization IP, and a need_content flag. Results carry a rerank_score from 0 to 3, and the docs treat 1.5 and above as high quality. Fetch retrieves a page directly.

The query syntax supports operators I actually want: site: and -site:, exact phrases with quotes, lang:, and after:/before: date filters. That means you can write a retrieval step like the one in the first snippet without paying a search vendor, and you inherit DeepSeek's own ranking instead of building one.

# The card ships no Jinja chat template. Use the reference encoder or deepseek-recipe,
# and keep your own prompt regression tests, because token ids for tool calls can move.
import subprocess, json

def encode_with_reference(conversation_path: str) -> dict:
    out = subprocess.run(
        ["python3", "encoding/encoding.py", "--input", conversation_path],
        capture_output=True, text=True, check=True,
    )
    return json.loads(out.stdout)

def assert_tool_call_roundtrip(payload: dict):
    ids = payload["token_ids"]
    assert len(ids) > 0, "empty encoding"
    # multi-turn, tool calling, thinking mode and interleaved images are all covered
    # by the shipped test cases; run them against the exact revision you pin.
    return {"tokens": len(ids), "model": payload.get("model"), "revision": payload.get("revision")}

print(assert_tool_call_roundtrip({"token_ids": [1, 2, 3], "model": "deepseek-v4.1-flash-1017", "revision": "main"}))
Terminal output on a dark screen

Agent gains show up in execution benchmarks, not in knowledge benchmarks.

What I would gate before switching

Pin a revision: the weights you download today are the weights you should hash and store. Then run your own twenty tasks through the identical harness on old and new checkpoints, and compare four numbers: pass rate, tool-call validity, output tokens per task, and retries per task.

Two things will bite first. The encoding change, because anything that assumes a Jinja template will produce subtly wrong token sequences for tool calls. And context length behaviour, because a 1M window invites you to paste an entire repository in, and long contexts are where attention budgets, latency and cost all move in the wrong direction at once.

Neither of those is a reason not to try it. MIT weights plus an API at $0.15 per million input tokens off-peak is a combination that changes what you can afford to run continuously. Just do not let a benchmark table make the decision for you.

📌 Frequently Asked Questions

Is DeepSeek-V4.1-Flash-1017 a new model?

The model card describes it as a minor update to DeepSeek-V4.1-Flash with improvements in reasoning, coding, agentic tasks and visual benchmarks, plus shorter outputs and faster task completion. It ships as new weights, not as a different architecture family.

Does the MIT licence mean I can ship it in a product?

The repository and model weights are MIT licensed according to the card. That is permissive, but it is still your compliance review: check the technical report and the licence file in the repository against your own distribution model.

Is the 1M-token context usable in practice?

The card recommends max_tokens of at least 256K and the API allows up to 384K of output, so the window is real. Whether it is economical depends on your cache hit rate and latency budget, both of which you should measure rather than assume.

Why is it ahead of Claude Opus 5 on DeepSWE but behind on ProgramBench?

The benchmarks measure different things and the runs use different harnesses. The card uses Claude Code for Terminal-Bench 4.0 and Mini-SWE-V2 for DeepSWE v1.1. Treat every comparison as harness-specific until you have rerun it on your own tasks.

Do I need a custom chat template?

Yes, or at least you cannot rely on a Jinja template because the release does not include one. DeepSeek ships encoding.py as a reference implementation plus deepseek-recipe, a Rust toolkit with Python bindings that handles multi-turn, tool calls, thinking mode and interleaved images.