<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: hc_xshh</title>
    <description>The latest articles on DEV Community by hc_xshh (@hcxshh).</description>
    <link>https://dev.to/hcxshh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4143807%2F2c438934-1857-446a-a83d-47b92f428b29.jpg</url>
      <title>DEV Community: hc_xshh</title>
      <link>https://dev.to/hcxshh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hcxshh"/>
    <language>en</language>
    <item>
      <title>Measuring error in support AI, and what accuracy hides</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:01:48 +0000</pubDate>
      <link>https://dev.to/hcxshh/measuring-error-in-support-ai-and-what-accuracy-hides-9nc</link>
      <guid>https://dev.to/hcxshh/measuring-error-in-support-ai-and-what-accuracy-hides-9nc</guid>
      <description>&lt;p&gt;The way I test a support or ticketing system is against a task book, not a demo. The number people&lt;br&gt;
ask for afterwards is accuracy, and I have stopped handing that over on its own: on every realistic&lt;br&gt;
test set I have built, the average was the least informative thing in the results file.&lt;/p&gt;

&lt;p&gt;What follows is the method I use now, plus a run on public data so the numbers can be checked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Five failure modes, five different bills
&lt;/h2&gt;

&lt;p&gt;A support model fails in at least five ways, and they do not cost the same.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fabrication.&lt;/strong&gt; The reply asserts something absent from the policy or the record: a fee that does&lt;br&gt;
not exist, a wrong refund window, an invented delivery date. This is what people call&lt;br&gt;
hallucination. The taxonomy I lean on, OlaBench, splits it into four sub-types — factual&lt;br&gt;
hallucination, misuse of retrieved results, relevance hallucination, and logical inconsistency&lt;br&gt;
(&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench, arXiv:2510.22143v3&lt;/a&gt;, §3.1; v1 25 Oct 2025, v3 25&lt;br&gt;
May 2026, retrieved 9 Oct 2026). A sentence that reads well and is false costs money, and the cost&lt;br&gt;
usually arrives months later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Missed detection.&lt;/strong&gt; The ticket contains a legal threat, a regulator's name, a safety issue or a&lt;br&gt;
fraud signal, and the system files it as routine. Nothing false was said. The failure is that no&lt;br&gt;
human ever reads it. On a scoring sheet this often counts as a correct classification, because the&lt;br&gt;
model did pick a queue that exists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Misrouting.&lt;/strong&gt; The reply is reasonable and the queue is wrong, so a differently-skilled team&lt;br&gt;
applies its own policy. The measurable cost is time to resolution and repeat contact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Out-of-scope answers.&lt;/strong&gt; The system answers a question it was never built for — tax, legal&lt;br&gt;
advice, another provider's product. The content may be accurate. The exposure is regulatory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refusal.&lt;/strong&gt; The system declines, loops, or hands off. There is no answer, so there is no error to&lt;br&gt;
score. This one gets tracked as a transfer or containment rate and drops out of the error count&lt;br&gt;
entirely.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure&lt;/th&gt;
&lt;th&gt;What is wrong&lt;/th&gt;
&lt;th&gt;Where the cost lands&lt;/th&gt;
&lt;th&gt;Commonly reported as&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fabrication&lt;/td&gt;
&lt;td&gt;the content&lt;/td&gt;
&lt;td&gt;refunds, goodwill, disputes&lt;/td&gt;
&lt;td&gt;hallucination rate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Missed detection&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;escalation, legal, safety&lt;/td&gt;
&lt;td&gt;almost nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Misrouting&lt;/td&gt;
&lt;td&gt;the destination&lt;/td&gt;
&lt;td&gt;time to resolution, repeats&lt;/td&gt;
&lt;td&gt;accuracy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Out of scope&lt;/td&gt;
&lt;td&gt;the remit&lt;/td&gt;
&lt;td&gt;compliance&lt;/td&gt;
&lt;td&gt;rarely measured&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Refusal&lt;/td&gt;
&lt;td&gt;nothing&lt;/td&gt;
&lt;td&gt;churn, containment&lt;/td&gt;
&lt;td&gt;transfer rate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One accuracy figure blends these five at whatever ratio the sample happens to contain, and that&lt;br&gt;
ratio is set by whoever picked the sample.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I ran
&lt;/h2&gt;

&lt;p&gt;I needed a routing task with a ground-truth label and text a model would really see. Public&lt;br&gt;
complaint data has both.&lt;/p&gt;

&lt;p&gt;The US CFPB publishes its Consumer Complaint Database under CC0 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB Consumer Complaint&lt;br&gt;
Database&lt;/a&gt;, retrieved 9 Oct&lt;br&gt;
2026); the search API's own metadata reports 18,274,023 records, license CC0, last updated&lt;br&gt;
2026-10-08 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;no_aggs=true" rel="noopener noreferrer"&gt;API metadata&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026).&lt;/p&gt;

&lt;p&gt;The JSON search endpoint returns 15 fields and no narrative (&lt;a href="https://cfpb.github.io/api/ccdb/fields.html" rel="noopener noreferrer"&gt;field&lt;br&gt;
reference&lt;/a&gt;, retrieved 9 Oct 2026). That is new: the&lt;br&gt;
September 2026 release removed complaint narratives from the database (&lt;a href="https://cfpb.github.io/api/ccdb/release-notes.html" rel="noopener noreferrer"&gt;CFPB release&lt;br&gt;
notes&lt;/a&gt;, Release 24, September 2026). So I took&lt;br&gt;
the text from a Hugging Face mirror instead, &lt;code&gt;BEE-spoke-data/consumer-finance-complaints&lt;/code&gt; (&lt;a href="https://huggingface.co/datasets/BEE-spoke-data/consumer-finance-complaints" rel="noopener noreferrer"&gt;dataset&lt;br&gt;
card&lt;/a&gt;, CC0, mirror last&lt;br&gt;
modified 29 Dec 2025); its &lt;code&gt;has-text&lt;/code&gt; config holds 1,689,573 rows (&lt;a href="https://datasets-server.huggingface.co/size?dataset=BEE-spoke-data%2Fconsumer-finance-complaints" rel="noopener noreferrer"&gt;split&lt;br&gt;
sizes&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026).&lt;/p&gt;

&lt;p&gt;Task: read the narrative only, predict the product queue. Ground truth: the product label the CFPB&lt;br&gt;
publishes. I drew 3,894 complaints from it. The oldest is dated 2015-03-20, the newest&lt;br&gt;
2024-01-04.&lt;/p&gt;

&lt;h2&gt;
  
  
  The label set changed under the data
&lt;/h2&gt;

&lt;p&gt;The raw product field holds 17 distinct strings in this sample, and they are not 17 products. The&lt;br&gt;
date ranges are what give it away.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product string as published&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Received, first → last in this sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting, credit repair services, or other personal consumer reports&lt;/td&gt;
&lt;td&gt;1,892&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-08-24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;547&lt;/td&gt;
&lt;td&gt;2015-03-20 → 2023-12-18&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;267&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-08-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;td&gt;2015-04-21 → 2023-11-02&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting or other personal consumer reports&lt;/td&gt;
&lt;td&gt;245&lt;/td&gt;
&lt;td&gt;2023-08-26 → 2024-01-04&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;195&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-11-21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;92&lt;/td&gt;
&lt;td&gt;2015-04-17 → 2017-04-09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;2015-10-06 → 2023-11-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer, virtual currency, or money service&lt;/td&gt;
&lt;td&gt;79&lt;/td&gt;
&lt;td&gt;2017-08-08 → 2023-11-13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-11-01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card&lt;/td&gt;
&lt;td&gt;68&lt;/td&gt;
&lt;td&gt;2015-04-06 → 2023-12-22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan, title loan, or personal loan&lt;/td&gt;
&lt;td&gt;41&lt;/td&gt;
&lt;td&gt;2017-04-24 → 2023-06-20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bank account or service&lt;/td&gt;
&lt;td&gt;34&lt;/td&gt;
&lt;td&gt;2015-04-04 → 2017-01-31&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consumer Loan&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;2015-03-25 → 2017-02-03&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;2015-10-08 → 2017-02-24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfers&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2015-07-17 → 2015-12-05&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payday loan, title loan, personal loan, or advance loan&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2023-09-20 → 2023-10-06&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Read the dates down the table. &lt;code&gt;Bank account or service&lt;/code&gt; last appears here on 2017-01-31, and&lt;br&gt;
&lt;code&gt;Checking or savings account&lt;/code&gt; first appears on 2017-04-24. Three more strings end in 2017 and their&lt;br&gt;
successors start the same year. The three credit-reporting strings sit end to end: 2015 to 2017,&lt;br&gt;
2017 to 2023, 2023 into 2024. Same category, renamed.&lt;/p&gt;

&lt;p&gt;I collapsed the 17 strings into 9 categories before scoring anything. The mapping is in the script,&lt;br&gt;
applied in one place rather than inferred per row. Without that step an accuracy compares a 2016&lt;br&gt;
row and a 2023 row whose label sets do not line up, and the model gets credit or blame for the&lt;br&gt;
taxonomy.&lt;/p&gt;

&lt;p&gt;The live database disagrees with the mirror about which names are current. Aggregated over&lt;br&gt;
complaints received in 2026, the CFPB API returns 11 product strings, with &lt;code&gt;Credit card&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;Prepaid card&lt;/code&gt; listed separately and no &lt;code&gt;Credit card or prepaid card&lt;/code&gt; at all (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;aggs=product&amp;amp;date_received_min=2026-01-01" rel="noopener noreferrer"&gt;product aggregation,&lt;br&gt;
complaints received 2026-01-01&lt;br&gt;
onward&lt;/a&gt;,&lt;br&gt;
retrieved 9 Oct 2026). I did not reconcile the two taxonomies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways one accuracy figure misleads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Imbalance.&lt;/strong&gt; After collapsing, the sample looks like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Collapsed category&lt;/th&gt;
&lt;th&gt;Rows&lt;/th&gt;
&lt;th&gt;Share&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;2,229&lt;/td&gt;
&lt;td&gt;57.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;547&lt;/td&gt;
&lt;td&gt;14.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;td&gt;8.6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;248&lt;/td&gt;
&lt;td&gt;6.4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;229&lt;/td&gt;
&lt;td&gt;5.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;85&lt;/td&gt;
&lt;td&gt;2.2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer or virtual currency&lt;/td&gt;
&lt;td&gt;81&lt;/td&gt;
&lt;td&gt;2.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;75&lt;/td&gt;
&lt;td&gt;1.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal / payday / title loan&lt;/td&gt;
&lt;td&gt;65&lt;/td&gt;
&lt;td&gt;1.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A predictor that answers "credit reporting" for every ticket scores 0.572 here. That is a&lt;br&gt;
constant, and it sits close enough to a real model's score to be mistaken for one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample selection.&lt;/strong&gt; Same data, same model, two splits:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Split&lt;/th&gt;
&lt;th&gt;Test n&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Macro F1&lt;/th&gt;
&lt;th&gt;Credit-reporting share of test set&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Random 60/40&lt;/td&gt;
&lt;td&gt;1,558&lt;/td&gt;
&lt;td&gt;0.756&lt;/td&gt;
&lt;td&gt;0.428&lt;/td&gt;
&lt;td&gt;57.1%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chronological, split at 2022-04-21&lt;/td&gt;
&lt;td&gt;1,560&lt;/td&gt;
&lt;td&gt;0.832&lt;/td&gt;
&lt;td&gt;0.377&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The chronological run scores 7.6 points higher while getting worse on most categories. The test&lt;br&gt;
set became more concentrated in the biggest class, and the dominant-class predictor rides that&lt;br&gt;
concentration up. Anyone reporting the 0.832 as an improvement after retraining would be reading&lt;br&gt;
their own sampling as progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coverage.&lt;/strong&gt; Sort the test set by the model's confidence and score only the top slice:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;th&gt;Tickets scored&lt;/th&gt;
&lt;th&gt;Accuracy on that slice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;td&gt;1,558&lt;/td&gt;
&lt;td&gt;0.756&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;75%&lt;/td&gt;
&lt;td&gt;1,168&lt;/td&gt;
&lt;td&gt;0.854&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;779&lt;/td&gt;
&lt;td&gt;0.904&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;25%&lt;/td&gt;
&lt;td&gt;389&lt;/td&gt;
&lt;td&gt;0.900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10%&lt;/td&gt;
&lt;td&gt;155&lt;/td&gt;
&lt;td&gt;0.897&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The 0.904 at half coverage is a real number computed on held-out data, and published alone it&lt;br&gt;
misdescribes the system by 15 points. Note where it stops moving: from 50% coverage down to 10%,&lt;br&gt;
accuracy drifts from 0.904 to 0.897, so the model's confidence and its correctness come apart on&lt;br&gt;
the tail. That tail is where the misroutes are.&lt;/p&gt;

&lt;p&gt;Underneath the average, the per-category picture on the random split:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Test n&lt;/th&gt;
&lt;th&gt;Recall&lt;/th&gt;
&lt;th&gt;Precision&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Credit reporting&lt;/td&gt;
&lt;td&gt;889&lt;/td&gt;
&lt;td&gt;0.879&lt;/td&gt;
&lt;td&gt;0.886&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Debt collection&lt;/td&gt;
&lt;td&gt;225&lt;/td&gt;
&lt;td&gt;0.631&lt;/td&gt;
&lt;td&gt;0.637&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credit card or prepaid card&lt;/td&gt;
&lt;td&gt;129&lt;/td&gt;
&lt;td&gt;0.760&lt;/td&gt;
&lt;td&gt;0.430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mortgage&lt;/td&gt;
&lt;td&gt;114&lt;/td&gt;
&lt;td&gt;0.860&lt;/td&gt;
&lt;td&gt;0.737&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Checking or savings account&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;td&gt;0.548&lt;/td&gt;
&lt;td&gt;0.607&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Money transfer or virtual currency&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Student loan&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;0.276&lt;/td&gt;
&lt;td&gt;0.889&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vehicle loan or lease&lt;/td&gt;
&lt;td&gt;29&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Personal / payday / title loan&lt;/td&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;td&gt;0.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three categories are never once predicted correctly. Accuracy is 0.756. Macro F1 is 0.428. The&lt;br&gt;
distance between those two is the honest headline for this model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pulling the high-risk errors out of the average
&lt;/h2&gt;

&lt;p&gt;The useful published idea here is OlaBench's risk axis, Critical Business Risk Rate. It counts only&lt;br&gt;
responses that inappropriately assert one of four things, each a high-stakes failure that may&lt;br&gt;
trigger compliance exposure, user disputes or reputational damage: admitting platform liability&lt;br&gt;
(618 test cases), misidentifying the ICS role (228), overcommitting (138), and disparaging&lt;br&gt;
individuals or merchants (16) (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench,&lt;br&gt;
arXiv:2510.22143v3&lt;/a&gt;, §3.1 and Table 1; 25 May 2026, retrieved 9&lt;br&gt;
Oct 2026). It is reported as its own number, beside the hallucination rate.&lt;/p&gt;

&lt;p&gt;The shape of that is what to copy: choose failures whose consequences differ in kind, count them&lt;br&gt;
separately, publish both, and never average them.&lt;/p&gt;

&lt;p&gt;For the routing test I defined three tiers, and I chose them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;critical&lt;/strong&gt; — a complaint about funds or account access (checking or savings, money transfer)
lands in the reporting queue&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;material&lt;/strong&gt; — a credit or loan dispute lands in a different loan or reporting family&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;routine&lt;/strong&gt; — a misroute inside the same family&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Random split&lt;/th&gt;
&lt;th&gt;Chronological split&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Critical&lt;/td&gt;
&lt;td&gt;7 / 1,558 = 0.4%&lt;/td&gt;
&lt;td&gt;11 / 1,560 = 0.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Material&lt;/td&gt;
&lt;td&gt;43 / 1,558 = 2.8%&lt;/td&gt;
&lt;td&gt;28 / 1,560 = 1.8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routine&lt;/td&gt;
&lt;td&gt;330 / 1,558 = 21.2%&lt;/td&gt;
&lt;td&gt;223 / 1,560 = 14.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two splits swap places depending on which tier you read: the one with the prettier accuracy&lt;br&gt;
hides a critical error rate almost twice as high.&lt;/p&gt;

&lt;p&gt;Published work also shows how far the same named metric can move when the population changes. The&lt;br&gt;
OlaBench paper reports a critical business risk rate of 8.7% for its OlaMind-Stage-2 model on the&lt;br&gt;
benchmark, and a critical business risk rate below 0.05% in daily manual annotation of live&lt;br&gt;
dialogues after launch (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench, arXiv:2510.22143v3&lt;/a&gt;, §5.2 and&lt;br&gt;
Table 3; 25 May 2026, retrieved 9 Oct 2026). Both are the same metric, measured on different&lt;br&gt;
populations with different annotators. Quoting one without the other is a choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  How these numbers were produced
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data.&lt;/strong&gt; 3,894 complaints from a CC0 mirror of the CFPB Consumer Complaint Database (config
&lt;code&gt;has-text&lt;/code&gt;, 1,689,573 rows). The draw is 40 pages of 100 consecutive rows at random offsets in
the split, deduplicated by complaint ID, seed 20261009. Rows with a narrative under 80 characters
were dropped. It is a clustered draw, not an independent one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Label hygiene.&lt;/strong&gt; 17 published product strings collapsed to 9 categories using the date ranges
in the table above. The mapping is written out in the script, not inferred per row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model.&lt;/strong&gt; Multinomial naive Bayes over word unigrams and bigrams, Laplace smoothing α = 1,
features seen at least twice in training. Python standard library only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Splits.&lt;/strong&gt; Random 60/40 with seed 7; chronological split at the 60th percentile of the
received-date order, which lands on 2022-04-21.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Selective accuracy.&lt;/strong&gt; Test rows sorted by the model's maximum posterior probability, top k
scored.&lt;/li&gt;
&lt;li&gt;Every number in the tables above is printed by that one script over that sample.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deliberate choice.&lt;/strong&gt; No language model appears in this pipeline, including the scoring. A
measurement instrument that is itself a model carries its own error, and OlaBench publishes the
size of its own: its judge agrees with human annotators at 91.7% on risk identification and 82.6%
on hallucination detection, over 5,000 human-annotated instances (&lt;a href="https://arxiv.org/abs/2510.22143v3" rel="noopener noreferrer"&gt;OlaBench,
arXiv:2510.22143v3&lt;/a&gt;, §3.3 and Table 2; 25 May 2026, retrieved
9 Oct 2026). A judge that disagrees with people 8% of the time on the risk axis puts a floor under
everything else you can measure with it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Whether the mirror's product strings match the live database's current taxonomy. The mirror's
rows use names the live API no longer returns for complaints received in 2026. I collapsed by date
range and did not reconcile the two.&lt;/li&gt;
&lt;li&gt;Whether the mirror is complete. Its files are timestamped December 2025 and the newest complaint
in my 3,894-row sample is dated 2024-01-04. I did not verify whether later records exist in it.&lt;/li&gt;
&lt;li&gt;Whether complaints published with a narrative are representative of all complaints. Narratives
appeared only with consent, so every number above sits on that consenting subset.&lt;/li&gt;
&lt;li&gt;Whether this sample's class mix reflects the population. Credit reporting is 57.2% of the sample
and about 82% of the live database: 14,978,722 of 18,274,023 records carry one of the three
credit-reporting strings (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;aggs=product" rel="noopener noreferrer"&gt;product
aggregation&lt;/a&gt;,
retrieved 9 Oct 2026). Most of the gap is the mirror's date cap.&lt;/li&gt;
&lt;li&gt;How noisy the product label is. It is chosen by the consumer at submission, not assigned by an
expert, and I treated it as ground truth because companies route on it. If it is noisy, my error
rates are a floor, not a ceiling.&lt;/li&gt;
&lt;li&gt;Whether a published tiering exists for complaint-routing consequences. I could not find one, so
the three tiers are mine. The OlaBench categories are a different kind of thing: they score what
a reply asserts about liability and commitment, and they do not classify misroutes.&lt;/li&gt;
&lt;li&gt;Whether error rates vary with company size, state, or complaint age. A stratified rate is the
next thing to compute.&lt;/li&gt;
&lt;li&gt;The naive Bayes model is a floor. A trained transformer would score higher on the same task. The
run was to see the shape of the error distribution, and a better model changes the level without
changing the shape.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>The Chinese open-weights release timeline, dated</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:01:16 +0000</pubDate>
      <link>https://dev.to/hcxshh/the-chinese-open-weights-release-timeline-dated-44ie</link>
      <guid>https://dev.to/hcxshh/the-chinese-open-weights-release-timeline-dated-44ie</guid>
      <description>&lt;p&gt;This is a list of dates, not an argument. Every row is a model whose weights were&lt;br&gt;
published for anyone to download, with a date I could trace back to the organization that&lt;br&gt;
published it, a parameter count as stated by that organization, and a link.&lt;/p&gt;

&lt;p&gt;Most timelines of this kind are written from memory or from coverage, which is why they drift.&lt;br&gt;
I built this one from vendor pages, papers and Hugging Face's API. This revision leaves the table&lt;br&gt;
at 67 rows: seven are new, one of them because an entry I dropped in the first pass turns out to&lt;br&gt;
be open-weights after all. The dropped list at the end has eleven entries, each with a reason.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read this timeline
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Open weights only.&lt;/strong&gt; A release is in the table only if the organization published&lt;br&gt;
downloadable weights. API-only and hosted-only models are out, including flagship ones:&lt;br&gt;
Qwen3-Max, the served ERNIE models, Kimi's hosted variants, DeepSeek's API-only models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A date I could trace.&lt;/strong&gt; The Date column has two kinds of value. A plain date is the date on&lt;br&gt;
the organization's own dated page — a research blog post, a news post, a release page, an&lt;br&gt;
accepted paper. A date marked &lt;code&gt;†&lt;/code&gt; is the Hugging Face repository creation timestamp in UTC,&lt;br&gt;
read from the Hugging Face API on 2026-10-09, used when the vendor had no dated page I could&lt;br&gt;
read. The two can differ by a few days, and that is usually preparation rather than a&lt;br&gt;
different release: DeepSeek-V3's weights repository was created 2024-12-25 12:52 UTC and the&lt;br&gt;
announcement is dated 2024-12-26.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter counts come from the vendor, or they are blank.&lt;/strong&gt; Total parameters with activated&lt;br&gt;
parameters for mixture-of-experts models, as stated on the announcement, the model card, or&lt;br&gt;
the repository holding the same weights. A family that ships many sizes gets a range. Where&lt;br&gt;
none of the vendor's pages for that release states a count, the cell is &lt;code&gt;—&lt;/code&gt; rather than a&lt;br&gt;
number carried over from a sibling model, even when the lineage makes the number obvious.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One link per row, to the vendor or the weight repository.&lt;/strong&gt; No aggregator links, no&lt;br&gt;
news coverage. That makes most rows single-source by construction: each cites the vendor's own&lt;br&gt;
statement about its own release, and every &lt;code&gt;†&lt;/code&gt; row is single-source by definition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope.&lt;/strong&gt; Language models and the flagship multimodal lines that carry the same brand and&lt;br&gt;
weights: Qwen3-VL, Qwen3-Omni, MiniMax-M3. Pure image, video and speech models are out of scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  When the vendor's page is client-rendered
&lt;/h2&gt;

&lt;p&gt;Two of the labs whose releases I date most often publish announcements as a page that ships no&lt;br&gt;
text to a plain HTTP request. z.ai's blog returns a 598-byte HTML shell that loads a JavaScript&lt;br&gt;
bundle; qwen.ai's blog returns a shell that fetches its content after load. Both render fine in&lt;br&gt;
a browser, which is what a reader clicking the link sees.&lt;/p&gt;

&lt;p&gt;For z.ai I did read something: the date string sits inside the page's own bundle, so the bundle&lt;br&gt;
is where I checked &lt;a href="https://z.ai/blog/glm-4.5" rel="noopener noreferrer"&gt;GLM-4.5&lt;/a&gt; (2025-07-28),&lt;br&gt;
&lt;a href="https://z.ai/blog/glm-4.6" rel="noopener noreferrer"&gt;GLM-4.6&lt;/a&gt; (2025-09-30), &lt;a href="https://z.ai/blog/glm-4.7" rel="noopener noreferrer"&gt;GLM-4.7&lt;/a&gt;&lt;br&gt;
(2025-12-22) and &lt;a href="https://z.ai/blog/glm-5" rel="noopener noreferrer"&gt;GLM-5&lt;/a&gt; (2026-02-12). That is weaker evidence than a&lt;br&gt;
rendered page, and it is why &lt;a href="https://z.ai/blog/glm-5.2" rel="noopener noreferrer"&gt;GLM-5.2&lt;/a&gt; stays out: its bundle contains&lt;br&gt;
2026-06-16 and no other date, and a date visible only inside a build artifact is not one I will&lt;br&gt;
print as sourced.&lt;/p&gt;

&lt;p&gt;Qwen's older blog carries the full text and a date; the newer qwen.ai copies do not. So the&lt;br&gt;
three Qwen3-* rows dated from qwen.ai in the first pass now cite the weight repository and&lt;br&gt;
carry a &lt;code&gt;†&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2023
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1–Q2
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM-6B&lt;/td&gt;
&lt;td&gt;2023-03-13 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;6.2B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm-6b" rel="noopener noreferrer"&gt;THUDM/chatglm-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baichuan-7B&lt;/td&gt;
&lt;td&gt;2023-06-13 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/baichuan-inc/Baichuan-7B" rel="noopener noreferrer"&gt;baichuan-inc/Baichuan-7B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM2-6B&lt;/td&gt;
&lt;td&gt;2023-06-24 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm2-6b" rel="noopener noreferrer"&gt;THUDM/chatglm2-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q3–Q4
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen-7B&lt;/td&gt;
&lt;td&gt;2023-08-03 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen-7B-Chat" rel="noopener noreferrer"&gt;Qwen/Qwen-7B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Baichuan 2&lt;/td&gt;
&lt;td&gt;2023-09-19&lt;/td&gt;
&lt;td&gt;7B, 13B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2309.10305" rel="noopener noreferrer"&gt;arXiv:2309.10305&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ChatGLM3-6B&lt;/td&gt;
&lt;td&gt;2023-10-25 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/THUDM/chatglm3-6b" rel="noopener noreferrer"&gt;THUDM/chatglm3-6b&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Yi-34B&lt;/td&gt;
&lt;td&gt;2023-11-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;34B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/01-ai/Yi-34B" rel="noopener noreferrer"&gt;01-ai/Yi-34B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek Coder&lt;/td&gt;
&lt;td&gt;2023-11-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.3B–33B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/deepseek-coder-33b-instruct" rel="noopener noreferrer"&gt;deepseek-ai/deepseek-coder-33b-instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek LLM&lt;/td&gt;
&lt;td&gt;2023-11-29 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;7B, 67B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-LLM-67B-Chat" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-LLM-67B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Baichuan 2 is the only 2023 row dated by a paper: &lt;code&gt;arXiv:2309.10305&lt;/code&gt; is dated 2023-09-19,&lt;br&gt;
while its weight repositories appear earlier, on 2023-08-30 &lt;code&gt;†&lt;/code&gt;, so the paper is the single&lt;br&gt;
source for that date. The two ChatGLM cells are blank because their cards do not state a count:&lt;br&gt;
6.2 billion is on the ChatGLM-6B card and not on the cards of its two successors, and copying&lt;br&gt;
it across is the habit that makes timelines of this kind drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  2024
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen1.5&lt;/td&gt;
&lt;td&gt;2024-02-04&lt;/td&gt;
&lt;td&gt;0.5B–110B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen1.5/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Qwen1.5 was in the dropped list in the first pass, because I looked for it on the new blog and&lt;br&gt;
found nothing readable. The old blog still carries the post, dated 2024-02-04, listing six&lt;br&gt;
sizes plus 110B.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q2–Q3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2&lt;/td&gt;
&lt;td&gt;2024-05-07&lt;/td&gt;
&lt;td&gt;236B total, 21B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2405.04434" rel="noopener noreferrer"&gt;arXiv:2405.04434&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4-9B&lt;/td&gt;
&lt;td&gt;2024-06-04 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;9B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/zai-org/GLM-4-9B-Chat" rel="noopener noreferrer"&gt;zai-org/GLM-4-9B-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2&lt;/td&gt;
&lt;td&gt;2024-06-07&lt;/td&gt;
&lt;td&gt;0.5B–72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2-VL&lt;/td&gt;
&lt;td&gt;2024-08-29&lt;/td&gt;
&lt;td&gt;2B, 7B, 72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2-vl/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2.5&lt;/td&gt;
&lt;td&gt;2024-09-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V2.5" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V2.5&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5&lt;/td&gt;
&lt;td&gt;2024-09-19&lt;/td&gt;
&lt;td&gt;0.5B–72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen2.5/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q4
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan-Large&lt;/td&gt;
&lt;td&gt;2024-10-22 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;389B total, 52B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/tencent/Hunyuan-Large" rel="noopener noreferrer"&gt;tencent/Hunyuan-Large&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-Coder&lt;/td&gt;
&lt;td&gt;2024-11-06 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;0.5B–32B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen2.5-Coder-32B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen2.5-Coder-32B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V2.5-1210&lt;/td&gt;
&lt;td&gt;2024-12-10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news1210" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3&lt;/td&gt;
&lt;td&gt;2024-12-26&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-v3/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DeepSeek point releases inside a model line are dated on DeepSeek's own changelog, one page&lt;br&gt;
per release. Hunyuan-Large's 389B/52B comes from&lt;br&gt;
&lt;a href="https://arxiv.org/abs/2411.02265" rel="noopener noreferrer"&gt;arXiv:2411.02265&lt;/a&gt;, submitted 2024-11-04, three weeks after&lt;br&gt;
the weights appeared.&lt;/p&gt;

&lt;h2&gt;
  
  
  2025
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-Text-01&lt;/td&gt;
&lt;td&gt;2025-01-14&lt;/td&gt;
&lt;td&gt;456B total, 45.9B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2501.08313" rel="noopener noreferrer"&gt;arXiv:2501.08313&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1&lt;/td&gt;
&lt;td&gt;2025-01-20&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-r1/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen2.5-VL&lt;/td&gt;
&lt;td&gt;2025-01-27 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;3B, 7B, 72B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen2.5-VL-72B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen2.5-VL-72B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;QwQ-32B&lt;/td&gt;
&lt;td&gt;2025-03-06&lt;/td&gt;
&lt;td&gt;32B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwq-32b/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3-0324&lt;/td&gt;
&lt;td&gt;2025-03-25&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news250325" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The two DeepSeek updates here do not restate a parameter count on their own changelog pages;&lt;br&gt;
the cells carry the count from the repositories that hold those weights, which are the V3 and&lt;br&gt;
R1 repositories listed above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q2
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3&lt;/td&gt;
&lt;td&gt;2025-04-29&lt;/td&gt;
&lt;td&gt;0.6B–235B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen3/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-R1-0528&lt;/td&gt;
&lt;td&gt;2025-05-28&lt;/td&gt;
&lt;td&gt;671B total, 37B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news250528" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniCPM4-8B&lt;/td&gt;
&lt;td&gt;2025-06-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;8B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/openbmb/MiniCPM4-8B" rel="noopener noreferrer"&gt;openbmb/MiniCPM4-8B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M1&lt;/td&gt;
&lt;td&gt;2025-06-16&lt;/td&gt;
&lt;td&gt;456B total, 45.9B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2506.13585" rel="noopener noreferrer"&gt;arXiv:2506.13585&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan-A13B&lt;/td&gt;
&lt;td&gt;2025-06-27&lt;/td&gt;
&lt;td&gt;80B total, 13B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/Tencent-Hunyuan/Hunyuan-A13B" rel="noopener noreferrer"&gt;Tencent-Hunyuan/Hunyuan-A13B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ERNIE 4.5&lt;/td&gt;
&lt;td&gt;2025-06-30&lt;/td&gt;
&lt;td&gt;0.3B–300B; flagship 300B/47B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ernie.baidu.com/blog/posts/ernie4.5/" rel="noopener noreferrer"&gt;ERNIE blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2&lt;/td&gt;
&lt;td&gt;2025-07-11 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2-Instruct" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Coder&lt;/td&gt;
&lt;td&gt;2025-07-22&lt;/td&gt;
&lt;td&gt;480B total, 35B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://qwenlm.github.io/blog/qwen3-coder/" rel="noopener noreferrer"&gt;Qwen blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.5&lt;/td&gt;
&lt;td&gt;2025-07-28&lt;/td&gt;
&lt;td&gt;355B total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.5" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step-3&lt;/td&gt;
&lt;td&gt;2025-07-31&lt;/td&gt;
&lt;td&gt;321B total, 38B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://chat.stepfun.com/research/zh/step3" rel="noopener noreferrer"&gt;StepFun research&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Seed-OSS-36B&lt;/td&gt;
&lt;td&gt;2025-08-21&lt;/td&gt;
&lt;td&gt;36B&lt;/td&gt;
&lt;td&gt;&lt;a href="https://seed.bytedance.com/en/blog/seed-oss-open-source-models-release" rel="noopener noreferrer"&gt;ByteDance Seed blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.1&lt;/td&gt;
&lt;td&gt;2025-08-21 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V3.1" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V3.1&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-Flash-Chat&lt;/td&gt;
&lt;td&gt;2025-08-29 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;560B total, 18.6B–31.3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-Flash-Chat" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-Flash-Chat&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Next-80B-A3B&lt;/td&gt;
&lt;td&gt;2025-09-09 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;80B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-Next-80B-A3B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-Next-80B-A3B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ling-flash-2.0&lt;/td&gt;
&lt;td&gt;2025-09-17 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;100B total, 6.1B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/inclusionAI/Ling-flash-2.0" rel="noopener noreferrer"&gt;inclusionAI/Ling-flash-2.0&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-Omni-30B-A3B&lt;/td&gt;
&lt;td&gt;2025-09-20 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;30B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-Omni-30B-A3B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-Omni-30B-A3B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3-VL&lt;/td&gt;
&lt;td&gt;2025-09-22 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;235B total, 22B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3-VL-235B-A22B-Instruct" rel="noopener noreferrer"&gt;Qwen/Qwen3-VL-235B-A22B-Instruct&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.2-Exp&lt;/td&gt;
&lt;td&gt;2025-09-29&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/v3-2-exp/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.6&lt;/td&gt;
&lt;td&gt;2025-09-30&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.6" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q4
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Ling-1T&lt;/td&gt;
&lt;td&gt;2025-10-02 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 50B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/inclusionAI/Ling-1T" rel="noopener noreferrer"&gt;inclusionAI/Ling-1T&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-OCR&lt;/td&gt;
&lt;td&gt;2025-10-17 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-OCR" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-OCR&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-Flash-Omni&lt;/td&gt;
&lt;td&gt;2025-10-23 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;560B total, 27B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-Flash-Omni" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-Flash-Omni&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2&lt;/td&gt;
&lt;td&gt;2025-10-27&lt;/td&gt;
&lt;td&gt;230B total, 10B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/blog/minimax-m2-en-1748600000" rel="noopener noreferrer"&gt;MiniMax blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2 Thinking&lt;/td&gt;
&lt;td&gt;2025-11-04 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2-Thinking" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2-Thinking&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V3.2&lt;/td&gt;
&lt;td&gt;2025-12-01&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/deepseek-v3-2/" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.7&lt;/td&gt;
&lt;td&gt;2025-12-22&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-4.7" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2026
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Q1
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GLM-4.7-Flash&lt;/td&gt;
&lt;td&gt;2026-01-19 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;30B total, 3B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/zai-org/GLM-4.7-Flash" rel="noopener noreferrer"&gt;zai-org/GLM-4.7-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step-3.5-Flash&lt;/td&gt;
&lt;td&gt;2026-02-01 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;196B total, 11B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/stepfun-ai/Step-3.5-Flash" rel="noopener noreferrer"&gt;stepfun-ai/Step-3.5-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2.5&lt;/td&gt;
&lt;td&gt;2026-02-12&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/news/minimax-m25" rel="noopener noreferrer"&gt;MiniMax news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GLM-5&lt;/td&gt;
&lt;td&gt;2026-02-12&lt;/td&gt;
&lt;td&gt;744B total, 40B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://z.ai/blog/glm-5" rel="noopener noreferrer"&gt;z.ai blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.5-397B-A17B&lt;/td&gt;
&lt;td&gt;2026-02-16&lt;/td&gt;
&lt;td&gt;397B total, 17B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://home.alibabagroup.com/en-US/document-1960233590314762240" rel="noopener noreferrer"&gt;Alibaba press release&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Q2
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M2.7&lt;/td&gt;
&lt;td&gt;2026-04-09 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/MiniMaxAI/MiniMax-M2.7" rel="noopener noreferrer"&gt;MiniMaxAI/MiniMax-M2.7&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kimi K2.6&lt;/td&gt;
&lt;td&gt;2026-04-14 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1T total, 32B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/moonshotai/Kimi-K2.6" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2.6&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4 (Pro and Flash)&lt;/td&gt;
&lt;td&gt;2026-04-24&lt;/td&gt;
&lt;td&gt;Pro 1.6T total / 49B active; Flash 284B total / 13B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.deepseek.com/en/news/v4-preview" rel="noopener noreferrer"&gt;DeepSeek news&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step 3.7 Flash&lt;/td&gt;
&lt;td&gt;2026-05-23 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;198B total, 11B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/stepfun-ai/Step-3.7-Flash" rel="noopener noreferrer"&gt;stepfun-ai/Step-3.7-Flash&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MiniMax-M3&lt;/td&gt;
&lt;td&gt;2026-06-01&lt;/td&gt;
&lt;td&gt;~428B total, ~23B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.minimax.io/blog/minimax-m3" rel="noopener noreferrer"&gt;MiniMax blog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The DeepSeek-V4 row now carries both counts; the Flash model's 284B total is stated on the same&lt;br&gt;
announcement page as the Pro figures. Step 3.7 Flash moved to a &lt;code&gt;†&lt;/code&gt; date: its GitHub repository&lt;br&gt;
has no releases, and the page I first cited carries a creation timestamp rather than a release&lt;br&gt;
date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Q3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;th&gt;Parameters&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LongCat-2.0&lt;/td&gt;
&lt;td&gt;2026-07-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1.6T total, ~48B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/meituan-longcat/LongCat-2.0" rel="noopener noreferrer"&gt;meituan-longcat/LongCat-2.0&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hunyuan Hy3&lt;/td&gt;
&lt;td&gt;2026-07-06&lt;/td&gt;
&lt;td&gt;295B total, 21B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.tencent.com/tencent-hunyuan-officially-releases-hy3-advancing-agent-capabilities-and-deeper-product-integration/" rel="noopener noreferrer"&gt;Tencent newsroom&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Qwen3.8&lt;/td&gt;
&lt;td&gt;2026-08-05 &lt;code&gt;†&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;27B; 2.4T total / 95B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://huggingface.co/Qwen/Qwen3.8-2.4T-A95B" rel="noopener noreferrer"&gt;Qwen/Qwen3.8-2.4T-A95B&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4-Pro GA&lt;/td&gt;
&lt;td&gt;2026-08-13&lt;/td&gt;
&lt;td&gt;1.6T total, 49B active&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260813" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4-Flash-Vision-Exp&lt;/td&gt;
&lt;td&gt;2026-08-21&lt;/td&gt;
&lt;td&gt;&lt;code&gt;—&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260821" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek-V4.1-Flash&lt;/td&gt;
&lt;td&gt;2026-09-10&lt;/td&gt;
&lt;td&gt;552B total; 8B active for input, 16B for output&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/news/news260910" rel="noopener noreferrer"&gt;DeepSeek changelog&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hy3 is the entry whose status changed in this revision. In the first pass I dropped it because&lt;br&gt;
the page I found did not say whether weights were published. It does: Hy3 is open-sourced under&lt;br&gt;
Apache 2.0 with weights on Hugging Face, and Tencent's newsroom names the parameter counts. The&lt;br&gt;
V4-Pro GA row repeats the count from the April preview announcement, because the GA page does&lt;br&gt;
not restate it. The Qwen3.8 row is dated by the repository of the 27B model, the first of the&lt;br&gt;
line to appear; the flagship 2.4T-A95B repository was created three days later, and Qwen's own&lt;br&gt;
announcement page is one of the client-rendered ones described above.&lt;/p&gt;

&lt;p&gt;Two rows elsewhere disagree with third-party timelines for the same reason: LongCat-2.0's&lt;br&gt;
repository was created 2026-07-05 while secondary coverage puts its launch at the end of June,&lt;br&gt;
and DeepSeek-V4-Flash's repository was created 2026-04-22, two days before the announcement&lt;br&gt;
page.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed in this revision
&lt;/h2&gt;

&lt;p&gt;Seven rows were added and three cells changed, listed because the edit history is the point of&lt;br&gt;
a table like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Qwen1.5 promoted into 2024 Q1&lt;/strong&gt; (2024-02-04), after finding the dated post on the old
Qwen blog.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hunyuan Hy3 promoted into 2026 Q3&lt;/strong&gt; (2026-07-06). It was dropped in error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four DeepSeek point releases added&lt;/strong&gt;: V2.5-1210 (2024-12-10), V3-0324 (2025-03-25),
R1-0528 (2025-05-28) and V4-Pro GA (2026-08-13). All four are dated on DeepSeek's own
changelog, the same source as V3.2 and V3.2-Exp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen3.8 added&lt;/strong&gt; (2026-08-05 &lt;code&gt;†&lt;/code&gt;), which the first pass omitted entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two ChatGLM parameter cells blanked.&lt;/strong&gt; The ChatGLM2-6B and ChatGLM3-6B cards do not state
a count, so the cells are now &lt;code&gt;—&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek-V4-Flash gained its total.&lt;/strong&gt; The row said "Flash 13B active" and dropped the
284B total, which the same page states.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Three rows changed source or date type for the same structural reason: the Qwen3-Next,&lt;br&gt;
Qwen3-Omni and Step 3.7 Flash rows now cite a weight repository with a &lt;code&gt;†&lt;/code&gt;, because the pages&lt;br&gt;
cited in the first pass were either unreadable to a plain request or carried no release date.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this table gets updated
&lt;/h2&gt;

&lt;p&gt;The table is edited in place. Every quarter I re-read the vendor pages for the labs already&lt;br&gt;
listed, then check Hugging Face for new repositories under those organizations. A release&lt;br&gt;
enters the table when it has a date and a link; until then it sits in the dropped list, with&lt;br&gt;
the reason.&lt;/p&gt;

&lt;p&gt;Two rules keep it stable: I do not replace a &lt;code&gt;†&lt;/code&gt; date with a later one unless the vendor&lt;br&gt;
publishes its own dated page, and I do not remove a superseded row.&lt;/p&gt;

&lt;p&gt;The current table has 67 entries. Twenty-three of them fall in the twelve months from October&lt;br&gt;
2025 to October 2026. That is a count of this table and nothing more; it is not a claim about&lt;br&gt;
the field, because the table only holds releases I could date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Eleven candidates were dropped&lt;/strong&gt;, for these reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Yi-1.5&lt;/strong&gt; (May 2024) — the weight repository
(&lt;a href="https://huggingface.co/01-ai/Yi-1.5-34B" rel="noopener noreferrer"&gt;01-ai/Yi-1.5-34B&lt;/a&gt;) was created 2024-05-11 &lt;code&gt;†&lt;/code&gt;, and
I could not date the announcement itself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;InternLM2 and InternLM2.5&lt;/strong&gt; — the weight repositories
(&lt;a href="https://huggingface.co/internlm/internlm2-7b" rel="noopener noreferrer"&gt;internlm/internlm2-7b&lt;/a&gt;,
&lt;a href="https://huggingface.co/internlm/internlm2_5-7b-chat" rel="noopener noreferrer"&gt;internlm/internlm2_5-7b-chat&lt;/a&gt;) look
like renamed predecessors, so the creation timestamp does not mark the release.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MiniCPM 1 and 2&lt;/strong&gt; — same problem; the repository
(&lt;a href="https://huggingface.co/openbmb/MiniCPM-2B-sft-bf16" rel="noopener noreferrer"&gt;openbmb/MiniCPM-2B-sft-bf16&lt;/a&gt;) is now a
monorepo for a whole series.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Baichuan 3 and 4&lt;/strong&gt; — I could not find weight artifacts at all.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ERNIE 5.0&lt;/strong&gt; and &lt;strong&gt;ERNIE 5.1&lt;/strong&gt; — Baidu's blog dates them 2026-02-06 and 2026-05-09
(&lt;a href="https://ernie.baidu.com/blog/posts/ernie5.0/" rel="noopener noreferrer"&gt;ERNIE 5.0&lt;/a&gt;,
&lt;a href="https://ernie.baidu.com/blog/posts/ernie-5.1-0508-release/" rel="noopener noreferrer"&gt;ERNIE 5.1&lt;/a&gt;) and describes
ERNIE 5.0 as a 2.4-trillion-parameter model, but I found no open-weight repository for
either.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kimi K3&lt;/strong&gt; — the model card (&lt;a href="https://huggingface.co/moonshotai/Kimi-K3" rel="noopener noreferrer"&gt;moonshotai/Kimi-K3&lt;/a&gt;)
carries the full architecture table (2.8T total, 104B activated) and a license, but no
release date. The repository timestamp is 2026-06-13 and secondary coverage says mid-July. I
could not reconcile that.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kimi K2.5&lt;/strong&gt; — same shape of problem: the weight repository timestamp
(&lt;a href="https://huggingface.co/moonshotai/Kimi-K2.5" rel="noopener noreferrer"&gt;moonshotai/Kimi-K2.5&lt;/a&gt;) is 2026-01-01 while the
launch is reported at the end of January.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GLM-5.2&lt;/strong&gt; — the post is client-rendered and the only date string in its bundle is
2026-06-16. I am not willing to print that as the release date.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek-V3.1-Terminus&lt;/strong&gt; — a mid-cycle rename with a 2025-09-22 &lt;code&gt;†&lt;/code&gt; repository
(&lt;a href="https://huggingface.co/deepseek-ai/DeepSeek-V3.1-Terminus" rel="noopener noreferrer"&gt;deepseek-ai/DeepSeek-V3.1-Terminus&lt;/a&gt;);
including it would have double-counted the V3.1 line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen-Image-2.1&lt;/strong&gt; — a dated weight repository
(&lt;a href="https://huggingface.co/Qwen/Qwen-Image-2.1" rel="noopener noreferrer"&gt;Qwen/Qwen-Image-2.1&lt;/a&gt;, 2026-09-14 &lt;code&gt;†&lt;/code&gt;) but an
image model, outside this table's scope by the rule stated at the top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Step-Audio line&lt;/strong&gt; — the same, for speech. The repositories exist
(&lt;a href="https://huggingface.co/stepfun-ai/Step-Audio-R1.1" rel="noopener noreferrer"&gt;stepfun-ai/Step-Audio-R1.1&lt;/a&gt;) and I did
not date them, because they would need their own table with its own scope.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Three other limits on this table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;†&lt;/code&gt; timestamps are preparation times, not launch times.&lt;/strong&gt; A repository can be created
days before the announcement, and from outside I cannot see whether weights were uploaded
at once or in pieces. Where the gap is large I noted it above the table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The dates are the vendor's own.&lt;/strong&gt; No release date here has been confirmed by a second
source, and one link per release is deliberate: a vendor correcting itself is the signal
worth keeping, and coverage repeating the vendor is not a second source.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License differences are not reflected.&lt;/strong&gt; Apache-2.0, MIT and the vendor-specific
licenses Kimi, MiniMax and LongCat ship under are not the same permission. Each row links to
the page that states its license, and for Hy3 that page states Apache 2.0.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>machinelearning</category>
      <category>llm</category>
    </item>
    <item>
      <title>Why there is no Western mini program</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:37 +0000</pubDate>
      <link>https://dev.to/hcxshh/why-there-is-no-western-mini-program-pem</link>
      <guid>https://dev.to/hcxshh/why-there-is-no-western-mini-program-pem</guid>
      <description>&lt;p&gt;The usual version of this question is about super apps, and it has been answered at length by people with better data than mine; mine is narrower. A mini program is a small bundle of code that runs inside somebody else's application, and the question is which parts of that arrangement exist in what Apple and Google ship.&lt;/p&gt;

&lt;p&gt;I read the vendors' own documentation for WeChat Mini Programs, Apple App Clips, Google Play Instant and Progressive Web Apps, and compared them on five dimensions: install and size, identity, payment, entry point, distribution and review.&lt;/p&gt;

&lt;p&gt;One mechanism carries most of the answer. A mini program brings neither its own user account nor its own checkout. It borrows both from the application hosting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The five dimensions, side by side
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;WeChat Mini Program&lt;/th&gt;
&lt;th&gt;Progressive web app&lt;/th&gt;
&lt;th&gt;Native app (iOS/Android)&lt;/th&gt;
&lt;th&gt;App Clip (iOS)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Install and size&lt;/td&gt;
&lt;td&gt;Runs inside WeChat. Main package 2 MB maximum, all packages together 30 MB (20 MB for programs developed by a third-party provider) (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/framework/subpackages.html" rel="noopener noreferrer"&gt;WeChat subpackages&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;No package at all. Chrome offers install after HTTPS plus a manifest carrying a name, 192 px and 512 px icons, &lt;code&gt;start_url&lt;/code&gt; and a display mode (&lt;a href="https://web.dev/articles/install-criteria" rel="noopener noreferrer"&gt;web.dev install criteria&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Full binary from the store. Apple states the App Store limits the size of apps installable over a mobile connection and gives no number on that page (&lt;a href="https://developer.apple.com/documentation/xcode/reducing-your-app-s-size" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09); Google states a compressed download limit of 200 MB for apps published as app bundles (&lt;a href="https://developer.android.com/topic/performance/reduce-apk-size" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;10 MB uncompressed on iOS 15 and earlier, 15 MB on iOS 16 and earlier, 100 MB on iOS 17 or later under conditions (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;openid&lt;/code&gt; per mini program, &lt;code&gt;unionid&lt;/code&gt; shared across every app under one Open Platform account. Both come from &lt;code&gt;wx.login&lt;/code&gt; plus &lt;code&gt;code2Session&lt;/code&gt;, which WeChat documents as returning the UnionID "without user authorization" (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/login.html" rel="noopener noreferrer"&gt;login&lt;/a&gt;, &lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html" rel="noopener noreferrer"&gt;UnionID&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Each origin keeps its own account. The browser hands nothing to a site silently&lt;/td&gt;
&lt;td&gt;Account per app. Sign in with Apple where the app offers it, as an explicit step by the user (&lt;a href="https://developer.apple.com/sign-in-with-apple" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Sign in with Apple, the keychain and CloudKit, shared with the parent app (&lt;a href="https://developer.apple.com/documentation/appclip/sharing-data-between-your-app-clip-and-your-full-app" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Payment&lt;/td&gt;
&lt;td&gt;WeChat Pay. A merchant number is bound to the mini program's AppID, at most 50 AppIDs per merchant number (&lt;a href="https://pay.weixin.qq.com/doc/v3/merchant/4013287504" rel="noopener noreferrer"&gt;WeChat Pay&lt;/a&gt;, page updated 2025-07-02). Virtual goods must go through the mini program virtual-payment channel, where "the platform charges a technical service fee" (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Card and wallet APIs called by the merchant, on the merchant's own acquiring contract&lt;/td&gt;
&lt;td&gt;In-app purchase is required to unlock features. Apple's guidelines name QR codes among the mechanisms that may not be used instead (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Apple's in-app purchase. Hosts in the Mini Apps Partner Program "earn 85% of qualifying In-App Purchase sales within qualifying mini apps" (&lt;a href="https://developer.apple.com/programs/mini-apps-partner/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry&lt;/td&gt;
&lt;td&gt;Documented entry paths carry IDs: 1011 scan a QR code, 1012 long-press an image, 1013 pick a code from the album, 1023 the Android home-screen icon, 1005 and 1006 search boxes, 1026 the nearby list, 1183 search inside the PC WeChat mini program panel (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html" rel="noopener noreferrer"&gt;WeChat scene values&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;A URL, a search result, or a home-screen shortcut the user adds by hand. On iOS there is no install prompt at all (&lt;a href="https://web.dev/learn/pwa/installation/" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Store search and the home-screen icon&lt;/td&gt;
&lt;td&gt;An App Clip Code, NFC tag or QR code at a physical location; Siri suggestions; Maps; Messages; a Smart App Banner on a website in Safari (&lt;a href="https://developer.apple.com/documentation/appclip/configuring-the-launch-experience-of-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Distribution and review&lt;/td&gt;
&lt;td&gt;Published from the mini program console. Virtual payment requires a certified entity: an enterprise, institution or individual merchant (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;td&gt;Nothing is submitted. It is HTTPS plus a manifest&lt;/td&gt;
&lt;td&gt;Store review against the App Review Guidelines&lt;/td&gt;
&lt;td&gt;No listing of its own. It ships inside a full app, which the App Clip documentation says "must include the same functionality as the App Clip" (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09); Guideline 2.5.16(a) adds that all App Clip features must be in the main app binary and that "App Clips cannot contain advertising" (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The Android equivalent is gone. "Starting December 2025, Instant Apps cannot be published through Google Play, and all Google Play services Instant APIs will no longer work" (&lt;a href="https://developer.android.com/topic/google-play-instant" rel="noopener noreferrer"&gt;Google Play Instant&lt;/a&gt;, last updated 2026-06-24).&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity arrives before the first screen
&lt;/h2&gt;

&lt;p&gt;The WeChat login flow is one call on the client and one on the server. &lt;code&gt;wx.login&lt;/code&gt; returns a code, the server exchanges it through &lt;code&gt;code2Session&lt;/code&gt;, and the reply carries an &lt;code&gt;openid&lt;/code&gt; for that user in that mini program, a &lt;code&gt;unionid&lt;/code&gt; and a session key (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/login.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;The UnionID page says what this costs the user: the developer gets it "without user authorization" (&lt;a href="https://developers.weixin.qq.com/miniprogram/en/dev/framework/open-ability/union-id.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). No phone number, no password, no consent sheet. The &lt;code&gt;openid&lt;/code&gt; is scoped to one mini program; the &lt;code&gt;unionid&lt;/code&gt; is identical across every mobile app, website app, official account and mini program bound to the same Open Platform account, so a merchant with several surfaces sees one person.&lt;/p&gt;

&lt;p&gt;Compare the alternatives. On the web, an origin cannot read an identifier out of the browser; it asks the user to sign in and then owns a credential to protect. On iOS, an App Clip can use Sign in with Apple and the keychain, which Apple's documentation points developers at sharing with the full app, but those are technologies an app opts into rather than an identifier handed over at launch.&lt;/p&gt;

&lt;p&gt;That is why the first screen of a mini program is the product. Onboarding was paid for at install time, by the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Payment is a binding between two accounts
&lt;/h2&gt;

&lt;p&gt;Paying inside a mini program does not mean the mini program holds money. WeChat Pay binds a merchant number to the mini program's AppID. One merchant number can bind at most 50 AppIDs, the binding starts on the merchant side and is confirmed from the mini program side, and once established cannot be unbound (&lt;a href="https://pay.weixin.qq.com/doc/v3/merchant/4013287504" rel="noopener noreferrer"&gt;WeChat Pay&lt;/a&gt;, page updated 2025-07-02). The rail and the code package are joined by an account relationship neither side can create alone.&lt;/p&gt;

&lt;p&gt;Virtual goods get a second layer. The documentation is blunt: virtual currency, unlocked features, subscription content, paid services, tips and virtual gifts "require integration with Mini Program's virtual payment system for both purchase and payment", and the platform charges a technical service fee on the amount (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). Physical goods go through ordinary WeChat Pay; digital ones go through the host's channel, with the host's cut.&lt;/p&gt;

&lt;p&gt;Apple's version of that cut is recent and documented. The Mini Apps Partner Program was announced on 13 November 2025 (&lt;a href="https://developer.apple.com/news/?id=xcz1s7cz" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, 2025-11-13): host apps carrying mini apps can join, earn 85 percent of qualifying in-app purchase sales inside those mini apps, and must use Apple's in-app purchase system for them (&lt;a href="https://developer.apple.com/programs/mini-apps-partner/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). Admission is not automatic: a host needs the Advanced Commerce API and a manifest describing itself and its mini apps.&lt;/p&gt;

&lt;p&gt;Apple's App Review Guidelines still require in-app purchase to unlock features or functionality, and the same sentence lists QR codes among the mechanisms that cannot substitute for it (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). What changed, on 1 May 2025, is that Guidelines 3.1.1, 3.1.1(a), 3.1.3 and 3.1.3(a) were updated for the United States storefront after a court decision about buttons and external links (&lt;a href="https://developer.apple.com/news/?id=9txfddzf" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, 2025-05-01).&lt;/p&gt;

&lt;h2&gt;
  
  
  The QR code is the offline switch
&lt;/h2&gt;

&lt;p&gt;A WeChat developer does not have to choose between a printed code and a link shared in a chat. The official scene-value table lists both, with IDs: 1011 scanning a QR code, 1012 long-pressing an image, 1013 picking one from the album, 1023 the Android home-screen icon, 1005 and 1006 the two search boxes, 1026 the nearby list, 1183 search inside the PC WeChat mini program panel (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/reference/scene-list.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;Two details make the printed code cheap. Codes come from an API, and WeChat states every generated mini program code is permanently valid. The "one item, one code" interface has no quantity limit and needs a smaller printed area than an ordinary URL code (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/qr-code.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). A code already printed and pointing at a URL can be configured to open a chosen page inside a mini program (&lt;a href="https://developers.weixin.qq.com/miniprogram/introduction/qrcode.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09). The person scans what is already on the table.&lt;/p&gt;

&lt;p&gt;App Clips have the same physical invocations: an App Clip Code, an NFC tag, a QR code at a physical location (&lt;a href="https://developer.apple.com/documentation/appclip/configuring-the-launch-experience-of-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). Two constraints pull the other way.&lt;/p&gt;

&lt;p&gt;The first constraint trades size against physical entry. The 100 MB ceiling for iOS 17 or later carries four conditions: the App Clip "only supports digital invocations" and not "physical invocations such as App Clip Codes, QR codes, or NFC tags" (&lt;a href="https://developer.apple.com/documentation/appclip/choosing-the-right-functionality-for-your-app-clip" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09), it is used where a reliable connection is likely, and it "doesn't support iOS 16 and earlier". The exception Apple names is a demo link from App Store Connect, which may use the 100 MB limit and still support App Clip Codes, NFC tags and QR codes. An App Clip with a counter code stays inside the smaller limit.&lt;/p&gt;

&lt;p&gt;The second concerns persistence. Apple's Human Interface Guidelines say App Clips "remain on the device for a limited amount of time", and that only App Clip Codes produced in App Store Connect or with Apple's own code generator are approved for use (&lt;a href="https://developer.apple.com/design/human-interface-guidelines/app-clips" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). A merchant cannot print its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who reviews it, and who is answerable
&lt;/h2&gt;

&lt;p&gt;Reviews differ in kind as well as in strictness. A PWA is submitted to nobody; Chrome installs it once its manifest and HTTPS satisfy the criteria (&lt;a href="https://web.dev/articles/install-criteria" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;, read 2026-10-09). An App Clip cannot be submitted alone. It ships inside a full app that must already contain the same functionality, cannot carry advertising, and is reviewed as part of that app (&lt;a href="https://developer.apple.com/app-store/review/guidelines/" rel="noopener noreferrer"&gt;Apple&lt;/a&gt;, read 2026-10-09). A mini program is submitted inside WeChat, and the entity requirement arrives with payment rather than with the code: to sell virtual items, the program must be a certified enterprise, institution or individual merchant (&lt;a href="https://developers.weixin.qq.com/miniprogram/dev/platform-capabilities/en/business-capabilities/virtual-payment.html" rel="noopener noreferrer"&gt;WeChat&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;The commercial consequence: in the App Store, a mini-app-like product reaches users through a host that already has an account relationship and a reviewed app behind it. In WeChat, the party needing the corporate identity, the certification and the merchant binding is the mini program author.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the four pieces sit
&lt;/h2&gt;

&lt;p&gt;In WeChat, four things a mini program depends on sit with one owner: the identity system, the payment rail, the entry surface (camera, search, chat, home screen, desktop) and the distribution channel with its review. An author plugs into a host that has all four and pays for the parts it uses.&lt;/p&gt;

&lt;p&gt;In the Apple and Google stacks those four are held by parties with different interests. The operating system vendor issues the platform account and runs the payment system; the browser owns the entry surface for web content; the stores own distribution and review; the rail for physical goods sits with the merchant's bank relationship.&lt;/p&gt;

&lt;p&gt;No single party there can hand all four to a third party. Apple's answer in November 2025 was to supply the missing pieces itself and keep the commission: hosts get Apple's in-app purchase for mini apps and keep 85 percent of qualifying sales, in exchange for approval, a manifest and Apple's review.&lt;/p&gt;

&lt;p&gt;The mechanism that makes mini programs work is therefore an ownership arrangement rather than a technology gap. Reproducing it outside WeChat means finding one party that holds the users, the identity, the payment rail and the entry surface, and getting it to open all four to third-party code at a price the third party will pay. In the West, the two parties that hold those pieces chose to be the host themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I checked this
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every figure and quote comes from documentation published by the vendor it describes: WeChat's mini program and WeChat Pay docs, Apple's developer documentation, guidelines and news items, Google's Android documentation, and web.dev.&lt;/li&gt;
&lt;li&gt;All pages were fetched on 2026-10-09. Most carry no publication date, so a date beside a source is my reading date unless the page states its own: the WeChat Pay AppID binding page (updated 2025-07-02), the Google Play Instant page (last updated 2026-06-24), Apple's updated-guidelines news item (2025-05-01), Apple's Mini Apps Partner Program news item (2025-11-13) and Tencent's write-up of the 2022 WeChat Open Class (2022-01-07).&lt;/li&gt;
&lt;li&gt;Size limits and scene-value IDs come from the pages that state them: WeChat's subpackage page for 2 MB and 30 MB, Apple's App Clip page for the 10/15/100 MB table, WeChat's scene-value table for the ID numbers.&lt;/li&gt;
&lt;li&gt;Google Play Instant's page would not load in a plain HTTP client, so I read it through the Internet Archive and then through a rendering fetch that returned the live page.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The fee WeChat charges on virtual payments.&lt;/strong&gt; The page says a fee is charged on the payment amount and gives no percentage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The standard WeChat Pay rate for physical goods.&lt;/strong&gt; I found no official rate page; what I reached describes partner fee schemes and refers to a merchant rate without the base number.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The extra steps for virtual payment on iOS.&lt;/strong&gt; WeChat's virtual payment page says iOS "requires additional activation and adaptation procedures" and points at separate documentation; I could not load that page, so I do not know whether an Apple-side charge is involved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether an App Clip can take payment for physical goods through Apple Pay.&lt;/strong&gt; Not stated on the App Clip pages I read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any current count of mini programs or mini-program daily users.&lt;/strong&gt; Tencent's release discloses Weixin monthly accounts and names Mini Games and Mini Shops, but carries no mini-program count. The most recent first-party figure I found: more than 450 million daily users at the end of 2021, from Tencent's write-up of the 2022 WeChat Open Class (&lt;a href="https://www.tencent.com/zh-cn/articles/2201267.html" rel="noopener noreferrer"&gt;Tencent&lt;/a&gt;, 2022-01-07). That figure is four years old.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What "certified Mini Program" requires in practice&lt;/strong&gt;, beyond the entity types named on the virtual payment page, and how long WeChat's review takes.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mobile</category>
      <category>apple</category>
      <category>javascript</category>
    </item>
    <item>
      <title>From token prices to task prices: three jobs, seven APIs</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:13 +0000</pubDate>
      <link>https://dev.to/hcxshh/from-token-prices-to-task-prices-three-jobs-seven-apis-3jah</link>
      <guid>https://dev.to/hcxshh/from-token-prices-to-task-prices-three-jobs-seven-apis-3jah</guid>
      <description>&lt;p&gt;Every vendor publishes a price per million tokens. Almost none publishes what a job costs; the number that decides whether a feature ships is left as arithmetic homework.&lt;/p&gt;

&lt;p&gt;I did that arithmetic for three jobs, from first-party price lists read on 2026-10-09. The token counts are assumptions, not measurements, and they are written out below. The money is the vendor's standard-tier list price, in USD.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three jobs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Job 1, label 1,000 support messages.&lt;/strong&gt; Each call sends a 400-token instruction block and a 120-token message, and asks for a 15-token answer. Billed per run: 520,000 input tokens, of which 400,000 are the instruction block sent 1,000 times, plus 15,000 output tokens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Job 2, turn a 100,000-word draft into a structured summary.&lt;/strong&gt; Google's token guide says a token is about 4 characters and 100 tokens about 60-80 English words, putting 100,000 words at 125,000-167,000 tokens (&lt;a href="https://ai.google.dev/gemini-api/docs/tokens" rel="noopener noreferrer"&gt;Google token counting guide&lt;/a&gt;, read 2026-10-09). I used 140,000:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One shot:&lt;/strong&gt; 140,000 tokens in, a 3,000-token structured summary out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Map-reduce:&lt;/strong&gt; 14 chunk calls of about 10,000 tokens each, 700 tokens of notes per chunk, then a 9,800-token reduce call. Totals: 149,800 in, 12,800 out.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Job 3, one round of tool-assisted bug fixing.&lt;/strong&gt; Eight turns against a 20,000-token repository: system prompt, tool schemas, three files. The prompt grows about 2,500 tokens per turn as tool results come back, from 20,000 to 37,500. Cumulative billed input is 230,000 tokens, of which 192,500 are a re-sent prefix a cache can serve; output is 6,000 tokens including reasoning.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I read the price lists
&lt;/h2&gt;

&lt;p&gt;Five first-party pages, all read on 2026-10-09; the Gemini API page builds its tables client-side, so I read it in a browser.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI API pricing&lt;/a&gt; — standard tier&lt;/li&gt;
&lt;li&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini API pricing&lt;/a&gt; — paid tier, introductory rates through 2026-12-31&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek Models &amp;amp; Pricing&lt;/a&gt; — off-peak column&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rules I applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Standard tier only&lt;/strong&gt;, unless a row says batch: the real-time tier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache reads are billed at the published cached-input rate. Cache writes are billed where a vendor publishes a write price.&lt;/strong&gt; OpenAI and Anthropic do: $2.50 per million tokens for the two frontier models here, $0.125 for the two small ones, on Anthropic's 5-minute cache. Google, DeepSeek and xAI publish no write price, so I charged zero, which flatters those three in every cached row.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In the cached rows I charge a write for every input token that is not a cache read:&lt;/strong&gt; 400 tokens in Job 1, 37,500 in Job 3.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reasoning tokens are output tokens.&lt;/strong&gt; OpenAI states they are billed as output tokens (&lt;a href="https://developers.openai.com/api/docs/guides/reasoning" rel="noopener noreferrer"&gt;OpenAI reasoning guide&lt;/a&gt;, read 2026-10-09). Gemini's output price is quoted "including thinking tokens" and DeepSeek's V4.1-Flash runs in thinking mode by default.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tool and search fees sit outside the main table.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No prompt hits a long-context threshold&lt;/strong&gt;; the prompt-length step that does apply, Anthropic's 100,000-token rule, is below.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No aggregator site was used&lt;/strong&gt; for any price.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What each job costs
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Task&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Estimated cost&lt;/th&gt;
&lt;th&gt;Assumptions&lt;/th&gt;
&lt;th&gt;Price source (read 2026-10-09)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$1.19&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.060&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$1.19&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.060&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.45&lt;/td&gt;
&lt;td&gt;standard tier, introductory rate; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.087&lt;/td&gt;
&lt;td&gt;off-peak window; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, no cache&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$1.13&lt;/td&gt;
&lt;td&gt;standard tier; 520k in / 15k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;400k of input served from cache; one 400-token write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.024&lt;/td&gt;
&lt;td&gt;400k of input served from cache; one 400-token write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;400k cached, 5-minute TTL and write&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.024&lt;/td&gt;
&lt;td&gt;400k cached, prompts under 100k, 5-minute TTL&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.18&lt;/td&gt;
&lt;td&gt;400k of input served from cache, storage excluded&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.028&lt;/td&gt;
&lt;td&gt;off-peak; cache hits free of a write fee&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 1, instructions cached&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.53&lt;/td&gt;
&lt;td&gt;400k of input served from cache&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.31&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.016&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.31&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.078&lt;/td&gt;
&lt;td&gt;140k in / 3k out, above the 100k step&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;140k in / 3k out, one call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;off-peak; 140k in / 3k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, one shot&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;140k in / 3k out, under the 200k threshold&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call; 149.8k in / 12.8k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.021&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.43&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.11&lt;/td&gt;
&lt;td&gt;15 calls, each prompt under 100k&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.030&lt;/td&gt;
&lt;td&gt;off-peak window, 15 calls&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 2, map-reduce&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.38&lt;/td&gt;
&lt;td&gt;14 chunk calls + 1 reduce call&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k read, 37.5k written) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.013&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k read, 37.5k written) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.25&lt;/td&gt;
&lt;td&gt;8 turns; 192.5k reads and 37.5k 5-minute writes / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.013&lt;/td&gt;
&lt;td&gt;8 turns, every prompt under 100k&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.065&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k cached) / 6k out, storage excluded&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.0098&lt;/td&gt;
&lt;td&gt;off-peak window; 8 turns&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Job 3, agent loop&lt;/td&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.21&lt;/td&gt;
&lt;td&gt;8 turns; 230k in (192.5k cached) / 6k out&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two pairs of rows are identical on purpose. gpt-6.1-sol and Claude Sonnet 5.5 both list $2.00 per million input tokens, $10.00 output, $0.10 cached input and a $2.50 cache write (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09); gpt-6-luna and Claude Haiku 5.5 both list $0.10, $0.50, $0.01 and $0.125.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same jobs with each vendor's biggest discount
&lt;/h2&gt;

&lt;p&gt;Batch is the biggest published discount: 50% on OpenAI (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09), on Anthropic ("a 50% discount on both input and output tokens", &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09) and on Google's paid tier ("Batch API (50% cost reduction)", &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;, read 2026-10-09), where the Batch column halves the cached-input rate too. Not everywhere: xAI's batch section gives 20% to grok-4.3 and the grok-4.20 variants and says unlisted models have no batch discount, which leaves Grok 4.7 at full price (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09). DeepSeek publishes no batch tier.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Job 1, cached&lt;/th&gt;
&lt;th&gt;Job 2, one shot&lt;/th&gt;
&lt;th&gt;Job 3, loop&lt;/th&gt;
&lt;th&gt;Batch discount&lt;/th&gt;
&lt;th&gt;Price source (read 2026-10-09)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gpt-6.1-sol&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;gpt-6-luna&lt;/td&gt;
&lt;td&gt;$0.012&lt;/td&gt;
&lt;td&gt;$0.0078&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Sonnet 5.5&lt;/td&gt;
&lt;td&gt;$0.22&lt;/td&gt;
&lt;td&gt;$0.16&lt;/td&gt;
&lt;td&gt;$0.12&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Haiku 5.5&lt;/td&gt;
&lt;td&gt;$0.012&lt;/td&gt;
&lt;td&gt;$0.039&lt;/td&gt;
&lt;td&gt;$0.0067&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemini 3.8 Flash&lt;/td&gt;
&lt;td&gt;$0.088&lt;/td&gt;
&lt;td&gt;$0.058&lt;/td&gt;
&lt;td&gt;$0.033&lt;/td&gt;
&lt;td&gt;50%&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DeepSeek V4.1-Flash&lt;/td&gt;
&lt;td&gt;$0.028&lt;/td&gt;
&lt;td&gt;$0.023&lt;/td&gt;
&lt;td&gt;$0.0098&lt;/td&gt;
&lt;td&gt;none published&lt;/td&gt;
&lt;td&gt;&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grok 4.7&lt;/td&gt;
&lt;td&gt;$0.53&lt;/td&gt;
&lt;td&gt;$0.30&lt;/td&gt;
&lt;td&gt;$0.21&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Claude Haiku 5.5's one-shot cell is the outlier: batch brings it to $0.039, still three times its cached Job 1 of $0.012, because a 140,000-token prompt sits above the 100,000-token step (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;h2&gt;
  
  
  What the numbers say
&lt;/h2&gt;

&lt;p&gt;Every figure here is arithmetic on the rows above, whose rates are linked (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;, &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Google&lt;/a&gt;, &lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek&lt;/a&gt;, &lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Output tokens decide the bill at the frontier tier.&lt;/strong&gt; In cached Job 1 on gpt-6.1-sol, $0.15 of the $0.43 is output (35%) and $0.04 is cached instruction tokens (9%).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The spread inside one vendor is bigger than the spread between vendors.&lt;/strong&gt; Cached Job 1 is $0.024 on gpt-6-luna and Haiku 5.5, $0.43 on gpt-6.1-sol and Sonnet 5.5, $0.53 on Grok 4.7: 22x from dearest to cheapest, 18x between the cheap pair and the frontier pair. At the same tier, the vendor changes nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The agent loop is the cheapest job here.&lt;/strong&gt; Job 3 is $0.25 on gpt-6.1-sol against $0.31 for the one-shot summary and $1.19 for Job 1. On the small models the order flips: Haiku 5.5 pays $0.078 for the one-shot summary against $0.013 for the loop, because 140,000 tokens crosses its threshold and 230,000 over eight turns does not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Map-reduce costs more than one shot.&lt;/strong&gt; On the two $2/$10 models it is $0.43 against $0.31, and the gap is all output tokens, 12,800 against 3,000. Chunking buys reliability and pays for it in output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The absolute numbers are small.&lt;/strong&gt; The main table spans $0.0098 to $1.19; batch pulls the floor to $0.0067. At this scale, a cost problem is usually a volume problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The variables that break the conversion
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Thresholds re-price the whole request.&lt;/strong&gt; Claude Haiku 5.5 charges $0.10 and $0.50 per million input and output tokens up to 100,000 prompt tokens, and $0.50 and $2.50 above it; the length counts cache reads and writes, and each request is priced on its own (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). Job 2's 140,000-token prompt pays the higher rate on everything: $0.078, against $0.016 under the cap. xAI applies long-context rates to all tokens in a request once the prompt passes 200,000, where Grok 4.7 costs $4.00 input, $1.00 cached input and $12.00 output instead of $2.00, $0.50 and $6.00 (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09). Gemini 3.1 Pro Preview steps from $2.00 to $4.00 input per million above 200,000 tokens (&lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;, read 2026-10-09). OpenAI's line is 272,000 input tokens, published in a column tooltip; above it gpt-6.1-sol's input doubles to $4.00 and its output to $15.00 (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt;, read 2026-10-09). All three jobs stay under every line; Job 2 is halfway to OpenAI's.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tokenizer differences.&lt;/strong&gt; Anthropic notes that Claude 4.7 and later use a tokenizer that "produces approximately 30% more tokens for the same text" (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). A comparison built on one words-per-token ratio, as mine is, is off by that much for Anthropic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Time of day.&lt;/strong&gt; DeepSeek's peak hours are 01:00-04:00 and 06:00-10:00 UTC, Monday to Friday, excluding Chinese public holidays; every other hour is off-peak at half price (&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek Models &amp;amp; Pricing&lt;/a&gt;, read 2026-10-09). Job 1 uncached is $0.087 off-peak and $0.174 at peak. Off-peak is 133 of the week's 168 hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cache economics differ in shape as well as in rate.&lt;/strong&gt; Anthropic's cache hit is 10% of the input price on most models, 5% on Opus 5.5 and Sonnet 5.5, 2.5% on Fable 5.1 and Mythos 5.1; writes cost 1.25x for five minutes and 2x for an hour, so a 5-minute cache pays for itself after one read (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). DeepSeek's hit price is $0.003 per million against $0.15 for a miss (&lt;a href="https://api-docs.deepseek.com/quick_start/pricing" rel="noopener noreferrer"&gt;DeepSeek pricing&lt;/a&gt;, read 2026-10-09). Google charges no write fee but charges storage, $0.50 per million tokens per hour on Gemini 3.8 Flash through 2026-12-31 and $4.50 on Gemini 3.1 Pro (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt; and &lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;, read 2026-10-09); a cached 140,000-token document runs $0.07 an hour on Flash and $0.63 on Pro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Platform fees sit outside the token meter.&lt;/strong&gt; OpenAI bills $2.50 per 1,000 tool calls and $10 per 1,000 web searches, so Job 3's eight calls cost $0.02, and Anthropic's tool-use prompt adds 286 to 675 tokens (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). xAI bills X search per item fetched: $5 per 1,000 posts, $10 per 1,000 profiles (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency and geography carry multipliers.&lt;/strong&gt; OpenAI's Fast mode, renamed from Priority on 2026-07-30, is 2x, and data-residency endpoints add 10%; Gemini 3.8 Flash's Priority tier is $1.35 input against $0.75 standard (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt; and &lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;, read 2026-10-09). xAI's priority tier is 2x and its US regional endpoint 1.1x; Anthropic charges 1.1x for US-only inference; regional Bedrock and Google Cloud endpoints carry a 10% premium (&lt;a href="https://docs.x.ai/developers/pricing" rel="noopener noreferrer"&gt;xAI pricing&lt;/a&gt; and &lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The list price itself moves.&lt;/strong&gt; Google quotes its Flash prices "through December 31, 2026" and doubles them on 2027-01-01: $0.75 to $1.50 input, $3.75 to $7.50 output, $0.075 to $0.15 cached input, $0.50 to $1.00 per million tokens per hour of cache storage (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;, read 2026-10-09). Anthropic's $2/$10 for Claude Sonnet 5 was launched as introductory pricing through 2026-08-31 and is now standard, with the rise to $3/$15 cancelled (&lt;a href="https://platform.claude.com/docs/en/about-claude/pricing" rel="noopener noreferrer"&gt;Anthropic pricing&lt;/a&gt;, read 2026-10-09). OpenAI's promotional pricing for GPT-5.6 Sol runs at least through 2026-11-21, on a model not in this table (&lt;a href="https://developers.openai.com/api/docs/pricing" rel="noopener noreferrer"&gt;OpenAI pricing&lt;/a&gt;, read 2026-10-09). Any per-task table is a snapshot with an expiry date.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;I ran none of these three jobs.&lt;/strong&gt; The token counts are stated assumptions, and a measured run would move every number here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-write pricing for Google, DeepSeek and xAI.&lt;/strong&gt; I found no published write price for any of the three, so I charged zero. xAI's prompt-caching page lists a billing rate per token type with no write line; Google's context-caching price is a read rate plus hourly storage; DeepSeek's table has only a hit price and a miss price. If any of them does charge for writes, its cached rows are too low.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether the Haiku 5.5 threshold behaves as written at the boundary.&lt;/strong&gt; The page counts cache reads and writes in a prompt's length, which would mean a cached 140,000-token document pays the higher rate. I did not test it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real token counts.&lt;/strong&gt; I used Google's words-per-token ratio across every vendor; Anthropic's own note about a 30% tokenizer difference is a warning that this is approximate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Volume, committed-use and negotiated enterprise pricing, free tiers and trial credits&lt;/strong&gt; are outside this table. The price on the page is the list price.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resale through cloud platforms.&lt;/strong&gt; Anthropic bills Bedrock and Google Cloud in consumption units, and Gemini Enterprise Agent Platform prices are not the Gemini API prices: on 2026-10-09 the two pages disagreed on cache storage, $0.50 per million tokens per hour (&lt;a href="https://ai.google.dev/gemini-api/docs/pricing" rel="noopener noreferrer"&gt;Gemini pricing&lt;/a&gt;) against $1.00 for one model (&lt;a href="https://cloud.google.com/gemini-enterprise-agent-platform/generative-ai/pricing" rel="noopener noreferrer"&gt;Gemini Enterprise Agent Platform pricing&lt;/a&gt;). The route you buy through can change the number.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>llm</category>
      <category>programming</category>
    </item>
    <item>
      <title>How an eval set is built, and why it rots</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:00:02 +0000</pubDate>
      <link>https://dev.to/hcxshh/how-an-eval-set-is-built-and-why-it-rots-1a6</link>
      <guid>https://dev.to/hcxshh/how-an-eval-set-is-built-and-why-it-rots-1a6</guid>
      <description>&lt;p&gt;An eval set is an instrument. It has a purpose, a scoring rule, a calibration record and an expiry date. Treating it as a file you download is where the trouble starts: the file already made those decisions without writing them down.&lt;/p&gt;

&lt;p&gt;What follows is the build document I would want for a set I have to defend six months from now. Each rule is traceable to a public source or marked as my practice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the samples come from
&lt;/h2&gt;

&lt;p&gt;Four sources, each importing a different bias.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;What you get&lt;/th&gt;
&lt;th&gt;The bias it imports&lt;/th&gt;
&lt;th&gt;Documented where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A public benchmark, reused&lt;/td&gt;
&lt;td&gt;Comparability with published numbers&lt;/td&gt;
&lt;td&gt;Contamination; a task choice someone else made&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2310.18018" rel="noopener noreferrer"&gt;Sainz et al., 2023-10-27&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public administrative data&lt;/td&gt;
&lt;td&gt;A real distribution, a public licence, dated updates&lt;/td&gt;
&lt;td&gt;A sample of who reports, not of who is affected&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cfpb.github.io/api/ccdb/index.html" rel="noopener noreferrer"&gt;CFPB, read 2026-10-09&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Synthetic generation&lt;/td&gt;
&lt;td&gt;Volume you can afford, labels you control&lt;/td&gt;
&lt;td&gt;Generator artifacts in the surface form&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aclanthology.org/N18-2017.pdf" rel="noopener noreferrer"&gt;Gururangan et al., ACL 2018&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your own production traffic&lt;/td&gt;
&lt;td&gt;Items that match what you ship&lt;/td&gt;
&lt;td&gt;A convenience sample that drifts with the product&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1802.03916" rel="noopener noreferrer"&gt;Lipton et al., ICML 2018&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reuse.&lt;/strong&gt; LiveBench's authors state the problem in one sentence: test set contamination "can quickly render benchmarks obsolete" (&lt;a href="https://arxiv.org/abs/2406.19314" rel="noopener noreferrer"&gt;LiveBench, v2 2025-04-18&lt;/a&gt;). Their answer was questions from recent competitions, arXiv papers and news, added monthly. LiveCodeBench did the same for code: 400 problems published between May 2023 and February 2024 (&lt;a href="https://arxiv.org/abs/2403.07974" rel="noopener noreferrer"&gt;LiveCodeBench, arXiv v1 2024-03-12&lt;/a&gt;). A static benchmark decays without anyone editing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Administrative data.&lt;/strong&gt; The CFPB complaint database is the best example I know. Its search API reported 18,274,023 records under a CC0 licence on 2026-10-09, with a &lt;code&gt;last_updated&lt;/code&gt; of 2026-10-08 (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/search/api/v1/?size=0&amp;amp;no_aggs=true" rel="noopener noreferrer"&gt;CFPB complaint search API, read 2026-10-09&lt;/a&gt;). Complaints are published only after the company responds or after 15 days, whichever comes first, and complaints referred to other regulators, including depository institutions with less than $10 billion in assets, are not published (&lt;a href="https://cfpb.github.io/api/ccdb/index.html" rel="noopener noreferrer"&gt;CFPB API docs, read 2026-10-09&lt;/a&gt;). The agency states that the database "is not a statistical sample of consumers' experiences in the marketplace" (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB database page, read 2026-10-09&lt;/a&gt;). A random draw samples the people who complained and whose complaints survived publication, not the people who were harmed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Synthetic.&lt;/strong&gt; Gururangan et al. showed that a text classifier reading only the hypothesis, with the premise deleted, reached about 67% on SNLI and 53% on MultiNLI (&lt;a href="https://aclanthology.org/N18-2017.pdf" rel="noopener noreferrer"&gt;Annotation Artifacts in NLI Data, ACL 2018&lt;/a&gt;). The label was recoverable from phrasing because the crowd workers followed a template. Every generator has one. My test: delete the part of the input meant to carry the answer, score again, and compare with the majority-class rate. If accuracy stays well above it, the set is partly measuring its own generator. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production sampling.&lt;/strong&gt; This is the source I trust least and use most. It produces items that match what the system sees, and its composition changes when the product does. Label shift is the named version of that failure: the marginal &lt;code&gt;p(y)&lt;/code&gt; moves while &lt;code&gt;p(x|y)&lt;/code&gt; does not, measurable without test labels using Black Box Shift Estimation (&lt;a href="https://arxiv.org/abs/1802.03916" rel="noopener noreferrer"&gt;Lipton et al., ICML 2018&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  What counts as correct
&lt;/h2&gt;

&lt;p&gt;The match rule is a parameter, and the same predictions score differently under each choice. Four conventions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Convention&lt;/th&gt;
&lt;th&gt;The rule, as the source writes it&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Exact match against any reference&lt;/td&gt;
&lt;td&gt;Counts predictions that match any one ground truth answer exactly; punctuation and articles ignored&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partial credit against the best reference&lt;/td&gt;
&lt;td&gt;F1 over bags of tokens, "the maximum F1 over all of the ground truth answers", then averaged&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A threshold on localisation&lt;/td&gt;
&lt;td&gt;IoU thresholds &lt;code&gt;[.5:.05:.95]&lt;/code&gt;, 10 of them; 101 recall thresholds; &lt;code&gt;maxDets&lt;/code&gt; of 1, 10 and 100&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocotools/cocoeval.py" rel="noopener noreferrer"&gt;cocoapi &lt;code&gt;cocoeval.py&lt;/code&gt;, read 2026-10-09&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A graded rubric with an exclusion flag&lt;/td&gt;
&lt;td&gt;0–3 for how well specified the issue is; 0–3 for whether the tests are scoped to it; a separate 0/1 question for any other reason the sample should not be used; a 1–5 confidence rating&lt;/td&gt;
&lt;td&gt;&lt;a href="https://cdn.openai.com/introducing-swe-bench-verified/swe-b-annotation-instructions.pdf" rel="noopener noreferrer"&gt;SWE-bench annotation instructions, undated&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three things this table taught me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Say who the ceiling belongs to.&lt;/strong&gt; In SQuAD the human number comes from the data: the second annotator's answer is treated as the prediction, the rest as ground truth, and humans score 77.0 exact match and 86.8 F1 on the test set (&lt;a href="https://arxiv.org/abs/1606.05250" rel="noopener noreferrer"&gt;SQuAD, arXiv v3 2016-10-11&lt;/a&gt;). A ceiling quoted without its rule says as much about the rule as about the task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One published number can hide ten thresholds.&lt;/strong&gt; COCO's headline detection score averages over ten IoU thresholds, three detection budgets and a recall grid. A single figure without a named threshold is a claim nobody can re-check. Pick the threshold, publish it, keep it fixed across versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The exclusion question is the part people skip.&lt;/strong&gt; SWE-bench Verified is 500 samples "verified to be non-problematic by our human annotators", drawn from 1,699 randomly sampled items that 93 Python developers annotated, with the full annotation set and the rubric released alongside (&lt;a href="https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;OpenAI, updated 2025-02-24&lt;/a&gt;). The rubric's last section asks, 0/1, whether there is any other reason not to use the sample. My handling of it, &lt;em&gt;(my practice)&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A closed list of exclusion reason codes, decided before annotation starts.&lt;/li&gt;
&lt;li&gt;Two raters must agree before an item is dropped; one rater's "unclear" is not an exemption.&lt;/li&gt;
&lt;li&gt;The dropped item stays in the file with its reason code, so the exemption stays auditable.&lt;/li&gt;
&lt;li&gt;The exemption rate is reported next to every result; if it moves between releases, the set changed even when the questions did not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ground truth built by pooling is biased by construction.&lt;/strong&gt; Test collections are built by judging only a sample of documents. When the pool is small relative to the collection, the judgment set "can be biased in that they favor relevant documents that contain topic title words", a bias that depends on collection size and not on the number of relevant documents (&lt;a href="https://www.nist.gov/publications/bias-and-limits-pooling-large-collections" rel="noopener noreferrer"&gt;Buckley et al., NIST, 2007-07-17&lt;/a&gt;). In a modern eval, if your labels come from checking only what your current systems returned, the label set favours items phrased the way those systems phrase things. My rule: draw a random slice from outside the candidate pool. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Whether the raters agree
&lt;/h2&gt;

&lt;p&gt;Agreement is a property of the pair and the setup.&lt;/p&gt;

&lt;p&gt;Krippendorff's alpha is &lt;code&gt;1 − Do/De&lt;/code&gt;, and it accepts any number of coders and missing entries (&lt;a href="https://www.asc.upenn.edu/sites/default/files/2021-03/Computing%20Krippendorff%27s%20Alpha-Reliability.pdf" rel="noopener noreferrer"&gt;Krippendorff, 2011-01-25&lt;/a&gt;). For two raters, Cohen's kappa, with Landis and Koch's 1977 bands (&lt;a href="https://www.ncbi.nlm.nih.gov/books/NBK52665/table/ch3.t5" rel="noopener noreferrer"&gt;reproduced in an AHRQ report, 2010&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Here is why the setup has to travel with the number. First-turn results on MT-bench, in the paper's two setups: S1 counts ties and inconsistent votes, S2 excludes ties (&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pair (first turn)&lt;/th&gt;
&lt;th&gt;S1, R = 33%&lt;/th&gt;
&lt;th&gt;S2, R = 50%&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4 pairwise vs human expert&lt;/td&gt;
&lt;td&gt;66% (n = 1,343)&lt;/td&gt;
&lt;td&gt;85% (n = 859)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPT-4 single-answer vs human expert&lt;/td&gt;
&lt;td&gt;60% (n = 1,280)&lt;/td&gt;
&lt;td&gt;85% (n = 739)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human vs human&lt;/td&gt;
&lt;td&gt;63% (n = 721)&lt;/td&gt;
&lt;td&gt;81% (n = 479)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The headline everyone quotes is 85% against 81% for human agreement (&lt;a href="https://arxiv.org/abs/2306.05685" rel="noopener noreferrer"&gt;MT-Bench, v4 2023-12-24&lt;/a&gt;). The same table holds 66% for the same judge under the other tie rule. An agreement figure quoted without its setup is not comparable to anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure agreement per stratum, not pooled.&lt;/strong&gt; ChaosNLI collected 464,500 annotations, 100 per example, over 3,113 SNLI and MNLI examples and 1,532 ANLI examples (&lt;a href="https://arxiv.org/abs/2010.03532" rel="noopener noreferrer"&gt;Nie et al., 2020-10-08&lt;/a&gt;). Models are near-perfect where humans agree and "can barely beat a random guess" where they do not, and those low-agreement items are most of the errors. A pooled score mixes two measurement regimes whose mixing ratio changes as models improve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sizing.&lt;/strong&gt; For an agreement study, start from the kappa minimum-sample-size tables (&lt;a href="https://riviste.unimi.it/index.php/ebph/article/view/17614" rel="noopener noreferrer"&gt;Bujang and Baharum, EBPH 14(2), 2017&lt;/a&gt;). For comparing two systems, use a power analysis: Miller gives the MDE formula and shows that raising answers per question from 1 to 10 moved the MDE from 13.2% to 7.5%; in his illustrative table, HumanEval's 164 questions at a fictional 83.6% carry a standard error of 3.2% (&lt;a href="https://arxiv.org/abs/2411.00640" rel="noopener noreferrer"&gt;Miller, 2024-11-01&lt;/a&gt;). If items arrive in groups, such as the same document or template, compute clustered standard errors (&lt;a href="https://arxiv.org/abs/2411.00640" rel="noopener noreferrer"&gt;Miller, 2024-11-01&lt;/a&gt;, Table 4); on DROP they were 1.34 against 0.44 naive.&lt;/p&gt;

&lt;p&gt;My rule: size the calibration slice for the agreement interval I need, the scoring slice for the effect I want to detect, and refuse to report a gap below the MDE. &lt;em&gt;(my practice)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the set rots
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Decay&lt;/th&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;What I do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Contamination&lt;/td&gt;
&lt;td&gt;One set improves while unrelated sets do not&lt;/td&gt;
&lt;td&gt;Keep item dates and prompt hashes; treat a stale set as historical&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label shift&lt;/td&gt;
&lt;td&gt;Scores move while the system did not&lt;/td&gt;
&lt;td&gt;Estimate the label mix and re-weight or re-cut the slice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taxonomy and rubric drift&lt;/td&gt;
&lt;td&gt;The same question means something different&lt;/td&gt;
&lt;td&gt;Version the labels; freeze the rubric text per set version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Saturation&lt;/td&gt;
&lt;td&gt;Every system sits near the ceiling&lt;/td&gt;
&lt;td&gt;Retire the set and say when&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Contamination's extent "is unknown, as it is not straightforward to measure", and where it exists it overestimates performance (&lt;a href="https://arxiv.org/abs/2310.18018" rel="noopener noreferrer"&gt;Sainz et al., 2023-10-27&lt;/a&gt;). I cannot audit training corpora, so I keep the dates and admit the exposure window.&lt;/p&gt;

&lt;p&gt;Saturation is not a failure of the set: GLUE's performance passed the level of non-expert humans, which is what prompted SuperGLUE (&lt;a href="https://arxiv.org/abs/1905.00537" rel="noopener noreferrer"&gt;Wang et al., 2019-05-02&lt;/a&gt;). Benchmark choice is fragile on its own: swapping tasks can reorder methods even when the methods do not change (&lt;a href="https://arxiv.org/abs/2107.07002" rel="noopener noreferrer"&gt;Dehghani et al., 2021-07-14&lt;/a&gt;). Both are reasons to keep more than one set.&lt;/p&gt;

&lt;p&gt;Taxonomy drift has a public paper trail. The CFPB's September 2026 release notes record that "consumers' complaint narratives ... have been removed from the database"; the June 2026 release removed "Consumer disputed" and "Consumer consent provided" from exports, long after those filters went away; the May 2023 release re-based the ZIP code field on 2019 census estimates (&lt;a href="https://cfpb.github.io/api/ccdb/release-notes.html" rel="noopener noreferrer"&gt;CFPB release notes, read 2026-10-09&lt;/a&gt;). A set built on those narratives became unmaintainable one month ago. The same page explains the general rule: the database shows "the consumer's original products, sub-products, issues, and sub-issues selections consistent with the options available on the form at the time the consumer submitted the complaint" (&lt;a href="https://www.consumerfinance.gov/data-research/consumer-complaints/" rel="noopener noreferrer"&gt;CFPB database page, read 2026-10-09&lt;/a&gt;). Labels frozen at submission, against a taxonomy that keeps moving.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keeping it maintainable
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(my practice, with borrowed pieces named)&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version the set.&lt;/strong&gt; Additions and exclusions get a version bump and a changelog line; never edit an item silently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two slices.&lt;/strong&gt; A frozen slice for comparability, a rolling window for freshness. LiveCodeBench and LiveBench are public versions of the same idea.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write the metadata down.&lt;/strong&gt; A datasheet (&lt;a href="https://arxiv.org/abs/1803.09010" rel="noopener noreferrer"&gt;Gebru et al., 2018, CACM 2021&lt;/a&gt;) or a Croissant record (&lt;a href="https://arxiv.org/abs/2403.19546" rel="noopener noreferrer"&gt;2024-03-28&lt;/a&gt;), so the next person need not infer the collection process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A desensitisation rule that is checkable.&lt;/strong&gt; The CFPB's ZIP rule is a good template: publish the 5-digit ZIP unless the census area has under 20,000 people, then 3 digits only if the wider area has more than 20,000, otherwise nothing (&lt;a href="https://cfpb.github.io/api/ccdb/fields.html" rel="noopener noreferrer"&gt;CFPB field reference, read 2026-10-09&lt;/a&gt;). A rule stated as a threshold can be re-run; "we removed personal information" cannot.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch the exemption rate.&lt;/strong&gt; It is the leading indicator that the rubric stopped fitting the data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State the blind spot in one line.&lt;/strong&gt; SQuAD's metrics ignore punctuation and articles; COCO's headline averages ten thresholds. Both are fine as long as the limitation is on the label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan the retirement.&lt;/strong&gt; When a set saturates or its taxonomy moves, freeze it, date it, and build the next one from the same seed items.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What I could not check
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How many SWE-bench samples were dropped to leave 500.&lt;/strong&gt; The announcement gives the annotated total (1,699) and the rubric, not the drop count (&lt;a href="https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;OpenAI, updated 2025-02-24&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether any specific model saw any specific benchmark item.&lt;/strong&gt; Sainz et al. state the extent is unknown; I have no method that resolves this, so I do not claim cleanliness.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB's narrative scrubbing standard.&lt;/strong&gt; The field is gone and the current field reference no longer documents it; I could not reach a dated scrubbing specification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB's historical &lt;code&gt;consumer_consent_provided&lt;/code&gt; semantics.&lt;/strong&gt; The export field and its filter are gone; I found the removal dates, not the values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Krippendorff's own minimum sample size for alpha.&lt;/strong&gt; I have the kappa tables from Bujang and Baharum; I found no equivalent table for alpha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Landis and Koch bands at first hand.&lt;/strong&gt; I read a reproduction of the 1977 table in a 2010 AHRQ report, not the original.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiveCodeBench's current item count.&lt;/strong&gt; The 400 figure comes from the March 2024 paper; the platform's live count may differ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The OpenAI SWE-bench Verified page at first hand.&lt;/strong&gt; The live page served an automated-client challenge when I read it; the same version is readable in an &lt;a href="https://web.archive.org/web/20260101233457/https://openai.com/index/introducing-swe-bench-verified/" rel="noopener noreferrer"&gt;Internet Archive copy&lt;/a&gt;, and the annotation instruction PDF is undated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CFPB record count above is a live API value&lt;/strong&gt; and changes daily. It was 18,274,023 when I read it on 2026-10-09.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>machinelearning</category>
      <category>datascience</category>
    </item>
    <item>
      <title>DeepSeek filled in the Ascend column: six repos, one technical report, and my machine with no NPU</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Wed, 30 Sep 2026 08:35:30 +0000</pubDate>
      <link>https://dev.to/hcxshh/deepseek-filled-in-the-ascend-column-six-repos-one-technical-report-and-my-machine-with-no-npu-4d15</link>
      <guid>https://dev.to/hcxshh/deepseek-filled-in-the-ascend-column-six-repos-one-technical-report-and-my-machine-with-no-npu-4d15</guid>
      <description>&lt;p&gt;DeepSeek released a batch of things over the last two days: six repositories plus one technical report in Chinese, all around Huawei's Ascend. I cloned all six, read them, and tried them on my own machine.&lt;/p&gt;

&lt;p&gt;This post covers three things: what was actually released, whether those impressive-looking numbers hold up, and exactly where my machine gave up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Six components were open sourced this time, and every one of them has a CUDA counterpart: a language for writing kernels, matrix multiply, multi-card communication, a general kernel library, an attention kernel, and a TopK kernel. Together that's filling in a column, not shipping a new model.&lt;/li&gt;
&lt;li&gt;The numbers are strong: dense matrix multiply at 99.8% of the hardware limit, sparse attention prefill at 95%, multi-card communication at 90–95% of physical bandwidth. But every one of these was measured on PoC hardware Huawei provided to DeepSeek. The commercial version is planned for mid-October. No third party can reproduce them today.&lt;/li&gt;
&lt;li&gt;The most valuable thing in this release isn't the announcement — it's the Chinese technical report published alongside it. It lays out the trade-offs of writing a sparse attention kernel on Ascend step by step: why one AI Core handles 64 heads, why you must pair each CUBE with two vector cores, and why one layout conversion has to detour through UB. I've copied the parts I found most revealing below.&lt;/li&gt;
&lt;li&gt;What I could verify locally: all six repos cloned, I read and counted the code, and I could not run a single kernel. The build dies at line 7 of &lt;code&gt;setup.py&lt;/code&gt;. That section is below too — it's measured, not paraphrased.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFvaTD1Jo8_uGlYPAuaMj93O1HXCL" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFvaTD1Jo8_uGlYPAuaMj93O1HXCL" width="1827" height="871"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Component-by-component comparison. The rightmost column is the size of the Ascend-specific part, which I counted from the source.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Getting this layer straight first
&lt;/h2&gt;

&lt;p&gt;Between a large model and the silicon sits a software layer: it tells the chip how to compute (kernels) and how to move data between cards (communication libraries). NVIDIA's version of that layer is CUDA; Huawei's is CANN. Anyone can write the model on top, but this layer has to be rewritten in the chip vendor's own language.&lt;/p&gt;

&lt;p&gt;When DeepSeek V4 launched in April, Huawei got there first with Ascend support — but that delivery was about making it &lt;em&gt;run&lt;/em&gt;. The pieces that matter most for performance were still open sourced only in their CUDA form. One media analysis at the time put it this way: moving from CUDA to CANN involves rewriting kernels at scale, restructuring the training pipeline, and re-tuning accuracy and performance — not less work than training the model itself. (Source: 21st Century Business Herald, 2026-04-24, in Chinese.)&lt;/p&gt;

&lt;p&gt;That is exactly what this release is. Which is why the sentence in the announcement — "all components correspond one-to-one with the previously open-sourced NVIDIA-platform components" — is the most substantive line in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The six components, one by one
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;TileLang's Ascend 950 backend.&lt;/strong&gt; TileLang is a language for writing high-performance kernels in Python, led by a team at Peking University and used for most of the kernels in DeepSeek V4's training. This time it landed in the main repository, not as a bolt-on adapter: PR #3308, "Introduce Ascend 950 backend", merged on 30 September and shipped with v0.1.15. The official support matrix lists Ascend 950 as Supported, built from source with &lt;code&gt;USE_ASCEND=ON USE_CUDA=OFF&lt;/code&gt;, requiring CANN, the bisheng compiler and torch_npu on the machine, with &lt;code&gt;torch.npu.is_available()&lt;/code&gt; returning True.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepGEMM-Ascend (matrix multiply).&lt;/strong&gt; Same package name and same API as the NVIDIA version, so existing code keeps working. The repo was created on 29 September.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepEP-Ascend (multi-card communication).&lt;/strong&gt; MoE dispatch and combine — sending tokens to the right expert and collecting them back. The kernels are all header-only &lt;code&gt;.hpp&lt;/code&gt; files sitting on Huawei's HCCL/HCOMM, UBMEM and URMA. It's also the only Ascend repo in this batch that was built from scratch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TileKernels v2.0.0 (general kernel library).&lt;/strong&gt; The small but frequently used kernels: MoE routing, quantization, Engram, hyper-connections. The README's own words: "added an Ascend backend, selected automatically at runtime; the same Python API runs on both NVIDIA GPUs and Huawei NPUs."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlashMLA (attention).&lt;/strong&gt; Sparse attention prefill and decode.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DeepSelect (TopK).&lt;/strong&gt; The step that picks compressed blocks in sparse attention, plus the TopK the sampler needs. The authors claim 2–20× faster than native &lt;code&gt;torch.topk&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The numbers are strong — read them together with their sources
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFpc4NGsrTRzsdOv7xayIkvUZw_5s" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFpc4NGsrTRzsdOv7xayIkvUZw_5s" width="1744" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Performance figures and where they come from. Bar length = the fraction of that hardware's theoretical limit; further right means closer to the ceiling.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The key readings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dense matrix multiply at 99.8% of the hardware limit (BF16, FP8 and FP4 all land around there). FP4 measured 1701 TFLOPS on a 4096×7168×16384 shape against a 1730 TFLOPS hardware limit, on Ascend 950DT with CANN 9.2.0.&lt;/li&gt;
&lt;li&gt;Sparse attention prefill at 410 TFLOPS, 95% of the theoretical limit; decode at 360 TFLOPS, 83%.&lt;/li&gt;
&lt;li&gt;MoE communication: at EP8, dispatch 373–375 GB/s and combine 345–347 GB/s; within 32 cards it reaches 90–95% of physical bandwidth. At EP128 dispatch drops to 313–320 and combine to 272–278, with the vendor saying combine is still being optimized.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sourcing point has to be stated plainly: DeepEP-Ascend's own README says these numbers were measured on a PoC HDK that Huawei provided to DeepSeek, with additional manual configuration that is not part of any public release. Huawei's commercial HDK for Atlas 850E is planned for mid-October. A third party cannot get the same hardware and configuration right now, so this set of numbers should be read as a vendor claim, not as a reproducible public benchmark.&lt;/p&gt;

&lt;p&gt;Two pieces of hardware background, both from official statements: the Atlas 950 supernode supports 8192 950DT chips across 160 cabinets (128 compute, 32 interconnect), with an FP8 rating of 8 EFLOPS and FP4 up to 16 EFLOPS, and interconnect bandwidth of 16 PB/s — Huawei's own line being that this single product's total interconnect bandwidth is more than ten times the peak bandwidth of today's entire global internet. Availability is Q4 2026. A single Ascend 950DT is planned at 144 GB of memory, 4 TB/s of memory bandwidth and 2 TB/s of interconnect bandwidth.&lt;/p&gt;

&lt;p&gt;That is the basis for the fine print on DeepSeek's side: the pricing cadence of V4-Pro is tied to the shipping schedule of the Ascend 950 supernode.&lt;/p&gt;

&lt;p&gt;Further out, there are moves in progress: Bloomberg reported on 4 September (relayed by Guancha) that DeepSeek plans to deploy at least 160,000 Ascend 950DT chips for inference in an Inner Mongolia data center, with a gigawatt-scale power plan and full delivery possibly taking more than a year. Huawei announced the Ascend 960 supernode at its 17 September Connect conference — 4096 cards, replacing 48,000 800G optical modules with 5,500 in-house optical engines. Both of these are third-party reporting or vendor announcements; I have no independent verification to offer.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. That Chinese technical report is the part worth reading
&lt;/h2&gt;

&lt;p&gt;FlashMLA shipped with a paper called 《Ascend Sparse Attention Forward 算法与优化技术简析》("A brief analysis of the Ascend sparse attention forward algorithm and optimization"), in Chinese, released today and sitting in the repo. It lays out line by line &lt;em&gt;why&lt;/em&gt; the kernel is written the way it is. A few of the parts that stuck with me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One AI Core handling 64 query heads is not arbitrary.&lt;/strong&gt; The report puts the compute-to-memory ratio on the table: with CUBE saturated (4096 multiply-adds per cycle), L2 total bandwidth of 5 TB/s, 32 cores and 1.65 GHz, the head count has to be 64 or more before compute becomes the bottleneck. At the same time, 128 heads would overflow L0C (a 128×512 float32 output is 256 KB), forcing the accumulator onto the vector core, which is extremely hard to write; empirically M=64 is already enough to saturate CUBE.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every CUBE needs two vector cores; one is not enough.&lt;/strong&gt; All three reasons are hard constraints: a single vector core can have at most 16 MTE2 copy requests in flight; decode has to read FP8 or FP4 KV and dequantize to bf16, and one vector core can't dequantize fast enough to keep up with CUBE; and FixPipe writes out at only 128 bytes per cycle per core, which by the formula forces B_TOPK above 73 — in practice they use 96 or 128. Taken together, the second vector core is not redundancy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A purely arithmetic optimization saves a large round of data movement.&lt;/strong&gt; The conventional online softmax approach may need to rescale the accumulated output against a new maximum every iteration. Their approach sets a threshold of 6 for "does this need rescaling" and skips the rescale when it isn't triggered — a trick they call skip-scale. The payoff is bigger on an NPU than a GPU, because on an NPU scale-O has to touch three units: FixPipe, UB and SIMD. The hard part is that the signal is produced by the vector core while the consumers are the CUBE and the vector core, which they solved with an SS buffer plus a cross-core flag.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The KV layout conversion goes the long way around because of a memory access pitfall.&lt;/strong&gt; CUBE wants NZ layout while KV sits in memory as ND. The normal approach is to convert in flight during the copy, but they fetch KV sparsely token by token, which rules that out. They tried copying tokens as 32-byte blocks directly, and found that the BIU inside the AI Core only coalesces accesses when &lt;em&gt;both&lt;/em&gt; source and destination are contiguous — "source contiguous only" doesn't coalesce, L2 bandwidth utilization drops, and the bottleneck becomes L2 bandwidth itself. In the end the path is: memory to UB, SIMD vector instructions to convert into NZ, then into L1.&lt;/p&gt;

&lt;p&gt;The report also covers gather2 (fusing two token copies into one to work around the MTE2 queue depth of 16), L1 dual-bank balancing (splitting each tensor into two, one per bank), and the fact that icache is only 32K (CUBE), 16K (VECTOR) and 8K (SIMD VF) — which is why they use hardware loop instructions instead of unrolled loops. None of these details appears in the announcement.&lt;/p&gt;

&lt;p&gt;One companion piece worth reading: Huawei's Ascend community has a long tutorial on TileLang from beginner to expert (published 2026-05-08) that lists the expert-mode conversion as six steps — split the requested cache by architecture into UB, L1 and L0C; insert workspace (because UB and L1/L0C cannot be copied between directly, so data used in both places has to exist twice); separate CV cores; synchronize across cores; have the two vector cores each handle half the data; and finally deal with the hardware special case that L0C cannot be written to directly. One of their measurements: hand-written FlashAttention at 44.9 microseconds, 22.28 microseconds after manually pipelining it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. How third parties see it
&lt;/h2&gt;

&lt;p&gt;The most systematic external tracking comes from SemiAnalysis's InferenceX. They've been recording DeepSeek V4 1.6T inference performance across hardware from Day 0 through Day 43, with a dedicated section analyzing inference on Ascend 950DT — how compute and communication overlap, and which compute streams Huawei used. One of their own observations is worth keeping: on the CUDA side, vLLM and SGLang were usable the day the model shipped, while ROCm wasn't usable on day one and only caught up on day 26, with an improvement of more than 100×. The ecosystem maturity gap is the genuinely hard part of this road. The same piece also notes that V4's fused MegaMoE kernel carries a theoretical 1.92× speedup over a naive implementation — working backwards, a naive implementation spends close to half its time in dispatch and combine.&lt;/p&gt;

&lt;p&gt;Two industry figures as reference points: IDC's data has NVIDIA's share of China's AI accelerator market falling from 95% two years ago to 55% in 2025, with domestic chips passing 41% for the first time — Ascend alone shipping 812,000 cards. China Mobile's April AI supernode tender this year was for 6,208 cards at roughly 2.06 billion RMB, requiring CANN-ecosystem solutions across the board. Both are third-party figures; I'd re-check them before using them.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. How far I got on my own machine
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFndRVjzgRU4tRQ0t8OgQ3rOZyrTS" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFndRVjzgRU4tRQ0t8OgQ3rOZyrTS" width="1615" height="819"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(After cloning the six repos locally, where the chain breaks. Green is what worked; red is what this machine doesn't have.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The environment first: Python 3.11.15; &lt;code&gt;/usr/local/Ascend&lt;/code&gt; does not exist; &lt;code&gt;import torch_npu&lt;/code&gt; raises ModuleNotFoundError; &lt;code&gt;which bisheng ccec&lt;/code&gt; finds nothing; there is no &lt;code&gt;nvidia-smi&lt;/code&gt; command; and &lt;code&gt;lspci&lt;/code&gt; shows zero NVIDIA, Ascend or Huawei devices. This is a pure CPU machine.&lt;/p&gt;

&lt;p&gt;Then I actually tried to build DeepGEMM-Ascend. The system python3 doesn't even have setuptools, so it stopped at line 5. I made a clean venv, installed the dependencies, and the second attempt got to line 7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="nb"&gt;ModuleNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;No&lt;/span&gt; &lt;span class="n"&gt;module&lt;/span&gt; &lt;span class="n"&gt;named&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;torch&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ascend part only starts at line 28: &lt;code&gt;ASCEND_HOME&lt;/code&gt; defaults to &lt;code&gt;/usr/local/Ascend/ascend-toolkit/latest&lt;/code&gt;, then it imports &lt;code&gt;torch_npu&lt;/code&gt; and links against ascendcl, hccl_fwk, torch_npu, opapi and dl. DeepEP-Ascend's CMakeLists is even more direct — under &lt;code&gt;${ASCEND_HOME_PATH}/aarch64-linux/&lt;/code&gt; alone there are dozens of header directories, plus the clang/15.0.5 headers from the bisheng compiler.&lt;/p&gt;

&lt;p&gt;The conclusion is that I never even reached the doorstep. Reproducing any of this yourself starts with owning an Ascend machine with CANN installed.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. What's in the code but not in the announcement
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFoul2LJkQWF9q38VrpFqg_pTIkOT" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Farticle-images.zsxq.com%2FFoul2LJkQWF9q38VrpFqg_pTIkOT" width="1747" height="853"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Distribution of Ascend-specific kernels in TileKernels. The &lt;code&gt;quant&lt;/code&gt; directory is the largest: 12 files, 3,491 lines.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TileKernels writes two implementations for each kernel.&lt;/strong&gt; Take SwiGLU: the directory contains both &lt;code&gt;swiglu_forward_kernel.py&lt;/code&gt; and &lt;code&gt;swiglu_forward_asc.py&lt;/code&gt;. The Ascend one starts with &lt;code&gt;import tilelang.ascend.language as T&lt;/code&gt; and &lt;code&gt;from tilelang.ascend.language import simd as S&lt;/code&gt;, and its pipeline synchronization is hand-written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ascend_set_flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MTE3_MTE2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ascend_wait_flag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;MTE2_V&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;input_slot&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The README's "backend selected automatically at runtime" is true, and the mechanism is an &lt;code&gt;is_ascend()&lt;/code&gt; check in &lt;code&gt;tile_kernels/config.py&lt;/code&gt;; the package can carry both backends in one environment. But a backend is not automatic translation — the Ascend implementation is written by hand. I counted the Ascend-specific files: &lt;code&gt;quant&lt;/code&gt; 12 files / 3,491 lines, &lt;code&gt;mhc&lt;/code&gt; 9 / 2,375, &lt;code&gt;engram&lt;/code&gt; 6 / 969, &lt;code&gt;moe&lt;/code&gt; 4 / 652, plus one each in &lt;code&gt;transform&lt;/code&gt; and &lt;code&gt;rand&lt;/code&gt; — 33 files, 7,729 lines in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints scattered through asserts.&lt;/strong&gt; A few: per-token quantization only supports groups of 32 channels; the E4M3 scale factor is CUDA-only; SwiGLU only supports &lt;code&gt;sf_block=(1, 32)&lt;/code&gt;; hyper-connections only support 4 streams; &lt;code&gt;randn&lt;/code&gt; doesn't support graph capture on NPU because replay would reuse the same random seed; and &lt;code&gt;norm&lt;/code&gt;'s hidden dimension must be divisible by 128. The announcement's line that "every TileLang kernel has a corresponding high-performance implementation" holds — the price is that list of shapes and preconditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FlashMLA's Ascend kernel covers only 6 combinations.&lt;/strong&gt; Under &lt;code&gt;csrc/ascend_kernels/prefill/sparse&lt;/code&gt; there are 4 headers plus 6 script-generated instantiation files: V4.1, h64, prefill and decode, with and without sink, fp8 and fp4. Change the shape and you regenerate. This is not a general-purpose library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There is no kernel file in DeepEP-Ascend with "ascend" in its name.&lt;/strong&gt; Its kernels are organized by function under &lt;code&gt;csrc/kernels/ep&lt;/code&gt;, &lt;code&gt;comm&lt;/code&gt;, &lt;code&gt;bucket&lt;/code&gt;, &lt;code&gt;pp&lt;/code&gt; and &lt;code&gt;engram&lt;/code&gt; — all &lt;code&gt;.hpp&lt;/code&gt;, 47 files, 6,335 lines in total.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The same batch of commits carries a breaking change.&lt;/strong&gt; FlashMLA removed Hopper support and V3, V3.2 and V4.0 in this release, changed the KV cache format, and requires switching back to the 15 September commit if you need older models or formats. The Ascend kernels here only serve V4.1. This version of the attention library moves with V4.1.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. What this post doesn't do
&lt;/h2&gt;

&lt;p&gt;I reproduced none of the performance numbers — I have no Ascend card. I did no independent verification of second-hand items like "160,000 chips" or the supernode timelines; each is marked with its source in the text. When the commercial HDK lands in mid-October, there will be a version a third party can actually check, and I'll come back and fill this in then.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek's announcement, "DeepSeek open-sources Ascend foundation components" (in Chinese): &lt;a href="https://mp.weixin.qq.com/s/X41mKH4Ds-VXUAnK6M8Eww" rel="noopener noreferrer"&gt;https://mp.weixin.qq.com/s/X41mKH4Ds-VXUAnK6M8Eww&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;FlashMLA Ascend technical report (Chinese): &lt;a href="https://github.com/deepseek-ai/FlashMLA/blob/20260930-ascend-open-source/docs/20260930-ascend-prefill-deep-dive-zh.md" rel="noopener noreferrer"&gt;https://github.com/deepseek-ai/FlashMLA/blob/20260930-ascend-open-source/docs/20260930-ascend-prefill-deep-dive-zh.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DeepGEMM-Ascend, DeepEP-Ascend, TileKernels, FlashMLA, DeepSelect: the same-named repositories under github.com/deepseek-ai&lt;/li&gt;
&lt;li&gt;TileLang main repo PR #3308: &lt;a href="https://github.com/tile-ai/tilelang/pull/3308" rel="noopener noreferrer"&gt;https://github.com/tile-ai/tilelang/pull/3308&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TileLang Ascend backend docs: &lt;a href="https://github.com/tile-ai/tilelang/blob/main/tilelang/ascend/README.md" rel="noopener noreferrer"&gt;https://github.com/tile-ai/tilelang/blob/main/tilelang/ascend/README.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Huawei, "Ascend 960 supernode launch" (2026-09-17): &lt;a href="https://www.huawei.com/cn/news/2026/9/hc-ascend960-supernode" rel="noopener noreferrer"&gt;https://www.huawei.com/cn/news/2026/9/hc-ascend960-supernode&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Huawei supernode interconnect keynote transcript (2025-09, Atlas 950 specs): &lt;a href="https://www.huawei.com/cn/news/2025/9/hc-xu-keynote-speech" rel="noopener noreferrer"&gt;https://www.huawei.com/cn/news/2025/9/hc-xu-keynote-speech&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ascend community, "TileLang AscendNPU IR from beginner to expert" (2026-05-08): &lt;a href="https://www.hiascend.com/developer/techArticles/20260506-1" rel="noopener noreferrer"&gt;https://www.hiascend.com/developer/techArticles/20260506-1&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Cailian Press, "Huawei's Atlas 950 hardware to appear at WAIC" (2026-07-07): &lt;a href="https://www.cls.cn/detail/2418935" rel="noopener noreferrer"&gt;https://www.cls.cn/detail/2418935&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Guancha, "DeepSeek reportedly to buy 160,000 Huawei Ascend 950DT chips" (2026-09-05): &lt;a href="https://www.guancha.cn/economy/2026_09_05_830131.shtml" rel="noopener noreferrer"&gt;https://www.guancha.cn/economy/2026_09_05_830131.shtml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Economic Observer, "Huawei computing power opens its chain" (2026-05-01): &lt;a href="http://www.eeo.com.cn/2026/0501/860610.shtml" rel="noopener noreferrer"&gt;http://www.eeo.com.cn/2026/0501/860610.shtml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;SemiAnalysis InferenceX, "DeepSeekV4 1.6T Day 0 to Day 43": &lt;a href="https://inferencex.semianalysis.com/blog/deepseekv4-16t-day-0-to-day-43-performance" rel="noopener noreferrer"&gt;https://inferencex.semianalysis.com/blog/deepseekv4-16t-day-0-to-day-43-performance&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number, error message and source in this post is in the list above; nothing here is repeated from somewhere else. I write one of these deep dives a week, published first in my own community in Chinese — this is the cleaned-up public version.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I also keep a small paid community where I write these engineering logs up in full, one deep dive a week. Its content is in Chinese — I mention it in case you read Chinese: &lt;a href="https://t.zsxq.com/fx2y5" rel="noopener noreferrer"&gt;AI落地实录&lt;/a&gt; (¥25/year with the current new-member coupon, ¥50 list price).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: this English version was translated and edited with an AI assistant from my own write-up. Every figure, error message and file count comes from my own runs or the linked primary sources — which is exactly why the parts I could not verify myself are marked as vendor claims.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally written in Chinese for my own notes; this English version is a translation.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>opensource</category>
      <category>hardware</category>
      <category>performance</category>
    </item>
    <item>
      <title>Collecting trending feeds from 20 platforms: only 8 had a usable API, and one silently failed for 12 days</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Mon, 28 Sep 2026 09:43:49 +0000</pubDate>
      <link>https://dev.to/hcxshh/collecting-trending-feeds-from-20-platforms-only-8-had-a-usable-api-and-one-silently-failed-for-1k3i</link>
      <guid>https://dev.to/hcxshh/collecting-trending-feeds-from-20-platforms-only-8-had-a-usable-api-and-one-silently-failed-for-1k3i</guid>
      <description>&lt;p&gt;I keep trending feeds for 20 platforms, fetched, translated, built and deployed automatically every night. This post is about the data sources themselves: how I sorted them, which paths look official but don't work, and the three traps I hit — including one that silently failed for 12 straight days.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Of 20 platforms, only 8 have an API I can call directly. The other 12 either need RSS or a workaround.&lt;/p&gt;

&lt;p&gt;The most deceptive status a collection script can report is "success". When a single platform breaks, the job still succeeds, and the page keeps rendering yesterday's numbers.&lt;/p&gt;

&lt;p&gt;Every path here was found by trial and error. This morning's run started at 04:30:18, all 20 platforms returned data, 480 items total, and fetch + translate + build + deploy finished in &lt;strong&gt;64 seconds&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a full run looks like
&lt;/h2&gt;

&lt;p&gt;This is today's run report, unedited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;== fetch     04:30:18 ==
OK  zhihu: 29 items
OK  bili: 30 items
OK  v2ex: 7 items
OK  hupu: 30 items
OK  maoyan: 30 items
OK  hn: 30 items
OK  lobsters: 25 items
OK  github: 30 items
OK  reddit: 25 items
OK  yt: 30 items
OK  kr36: 30 items
OK  tc: 20 items
OK  sspai: 10 items
OK  ifanr: 20 items
OK  ithome: 30 items
OK  solidot: 14 items
OK  verge: 10 items
OK  ars: 20 items
OK  cnbeta: 30 items
OK  arxiv: 30 items
TR  translating hn (30 titles)…
TR  translating github descriptions (28)…
SAVE zhihu.json: 29 items
done: 20/20 platforms collected (2026-09-28)
== summarize 04:31:15 ==
summarize: 19 platforms, 450 items total, 95 top entries fed to model
summarize OK: 10 focus items (3 merged across platforms) -&amp;gt; summary.json
== build     04:31:18 ==
== git       04:31:19 ==
DONE in 64s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In that one minute: 20 platforms returned 7 to 30 items each (480 total), 189 English headlines plus 28 English descriptions were translated, a model picked 10 front-page highlights from the top 5 of each platform (3 of today's were merges of the same story across sites), and the static site was built, committed, pushed and auto-deployed.&lt;/p&gt;

&lt;p&gt;Note the &lt;code&gt;done: 20/20&lt;/code&gt;. The script settles the books by &lt;strong&gt;number of successful platforms&lt;/strong&gt;. The job only counts as failed when the same platform dies several times in a row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three categories of data source
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Category 1: a real API (8 platforms)
&lt;/h3&gt;

&lt;p&gt;Zhihu, Bilibili, V2EX, Hupu, Maoyan, Hacker News, Lobsters, GitHub.&lt;/p&gt;

&lt;p&gt;They look like the easy ones. Every single one has a different temperament:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zhihu's official CLI gives you 2 trending-list calls per day.&lt;/strong&gt; Two runs and you're done. A third-party CLI that reuses cookies has no limit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bilibili, Hupu and Maoyan work from a Chinese IP directly&lt;/strong&gt; — but adding a proxy breaks them. All three fall into the "the more official the channel, the less usable it is" bucket; I ended up on mobile-app and dashboard endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hupu returns about ten items per call&lt;/strong&gt;, so it takes 5 pages to reach 30. It also reports a weighted heat score, not a pure numeric ranking, so you take it as given.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maoyan returns 403 without a desktop User-Agent and a Referer header.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V2EX has a fully public keyless endpoint that returns 7 to 10 items per call.&lt;/strong&gt; That's the platform's ceiling, not a bug. Today it gave me 7.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HN and Lobsters are the simplest&lt;/strong&gt;: one uses Algolia's search API, the other a plain hot-list JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub uses the Search API&lt;/strong&gt;, querying repositories created in the last 7 days, sorted by stars.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Category 2: RSS only (10 platforms)
&lt;/h3&gt;

&lt;p&gt;sspai, ifanr, ITHome, Solidot, cnBeta, The Verge, Ars Technica, TechCrunch, arXiv, Reddit.&lt;/p&gt;

&lt;p&gt;These are the easiest to maintain: no API key, uniform format, one regex parses them all. The cost is that you get exactly what they choose to publish. Solidot gives about a dozen items a day, sspai and The Verge give 10 each, and Reddit gives titles and links only — no scores, no comment counts (more on that below).&lt;/p&gt;

&lt;h3&gt;
  
  
  Category 3: needs a workaround (2 platforms)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;36Kr&lt;/strong&gt; and &lt;strong&gt;YouTube&lt;/strong&gt;. 36Kr's channel pages are pure client-side rendering, so a plain request returns an empty shell; you need a rendering service, and one of its tier parameters must be pushed to the maximum — at the default tier the response body is always empty. YouTube's official trending API returns an empty shell to datacenter IPs like mine, so I use a third-party aggregator page instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 1: twelve days of silent failure
&lt;/h2&gt;

&lt;p&gt;Reddit took the longest to fix, and cost me the most.&lt;/p&gt;

&lt;p&gt;The result first: &lt;strong&gt;from September 1 to September 12, twelve days in a row, the Reddit column showed August 31 data.&lt;/strong&gt; The page rendered fine. The job status was "success". I noticed nothing.&lt;/p&gt;

&lt;p&gt;On each of those twelve days, this line sat in the report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;KEEP reddit.json: fetch failed, keeping previous data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It &lt;em&gt;was&lt;/em&gt; in the report. The problem is it was buried among 20 lines of &lt;code&gt;OK&lt;/code&gt;, and the job status was still success. My daily routine was to scan for red text, and &lt;code&gt;KEEP&lt;/code&gt; read to me like "it kept the old data, that's stable."&lt;/p&gt;

&lt;p&gt;What actually exposed it was a completely unrelated incident. On September 12 a build log had wrong file permissions, the script exited non-zero, and for the first time error output made it into the report — where I saw the real message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERR reddit: reddit: no thing blocks found (proxy changed?)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It wasn't monitoring that caught it. It was a permission error that happened to occur nearby.&lt;/p&gt;

&lt;p&gt;The cause: Reddit's path had depended on a Google Translate proxy page, which switched to a 302 redirect in early September and died instantly. I moved to Reddit's official Atom RSS, and there are two things you must remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The URL must include &lt;code&gt;geo_filter=GLOBAL&lt;/code&gt;. Without it you get content localized to your exit IP's country. My proxy exits in Germany, so without that parameter the whole page came back in German.&lt;/li&gt;
&lt;li&gt;It must go through a proxy. Direct requests get nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing I gave up on: that RSS carries no scores or comment counts. I tried three ways to get them back. The keyless &lt;code&gt;.json&lt;/code&gt; endpoint returned 403 under all three approaches — direct, proxied, and with a Chrome TLS fingerprint spoof. A third-party archive returned 2015 posts; another moved to paid rate limiting. The only method that truly returned scores was reusing my own browser's login cookie, which returned 200 with real scores — but that's unapproved automated access, so I didn't use it.&lt;/p&gt;

&lt;p&gt;So that column is now permanently official RSS, no scores. I wrote it into the project doc marked "settled, stop tinkering."&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: looks fine, actually missing half
&lt;/h2&gt;

&lt;p&gt;arXiv fails in a very particular way.&lt;/p&gt;

&lt;p&gt;First the API broke: the official endpoint returned a 14-byte response body reading &lt;code&gt;Rate exceeded.&lt;/code&gt;, proxied or not. The official RSS fixed it — 200, no key needed.&lt;/p&gt;

&lt;p&gt;The real trouble came later, from a code audit. My RSS fetch pulled three categories at once (artificial intelligence, machine learning, natural language processing) in a loop — but there was an early &lt;code&gt;return&lt;/code&gt; inside it. As soon as the first category hit 30 items, the function returned. The other two categories were never requested, and machine learning and LLM papers silently vanished from the feed.&lt;/p&gt;

&lt;p&gt;What makes this hard to spot is that it does nothing to "normal operation": the job succeeds, the item count is a full 30, the page renders. You only find it by deliberately counting categories.&lt;/p&gt;

&lt;p&gt;Today's 30 items: 6 each from AI, ML and NLP, plus 12 from robotics, security and databases (the RSS carries cross-listed categories, so they leak in).&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 3: "zero items" and "fetch failed" are different bugs
&lt;/h2&gt;

&lt;p&gt;36Kr is the third category. It doesn't raise. It returns zero items.&lt;/p&gt;

&lt;p&gt;The cause is in the render service's response body. Below a certain tier, it doesn't raise an error — it returns an empty result list plus a failure reason. My code grabbed the first result, threw an array-index-out-of-range exception, and the outer handler swallowed it into the "keep previous data" branch.&lt;/p&gt;

&lt;p&gt;Same outcome as Reddit: the platform silently froze on yesterday. The only difference is the cause changed from a network problem to a parameter problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The day nothing updated
&lt;/h2&gt;

&lt;p&gt;On September 15 the whole pipeline froze.&lt;/p&gt;

&lt;p&gt;The entire report for that day was these few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Cron Job: daily-hub&lt;/span&gt;
&lt;span class="gs"&gt;**Run Time:**&lt;/span&gt; 2026-09-15 05:30:25
&lt;span class="gs"&gt;**Mode:**&lt;/span&gt; no_agent (script)
&lt;span class="gs"&gt;**Status:**&lt;/span&gt; script failed
Script timed out after 3600s: …/daily-hub-update.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;249 bytes. Nothing else: no error, no progress, not even a "starting fetch". Output went through a pipe with block buffering, so when the process was killed, everything in the buffer was lost.&lt;/p&gt;

&lt;p&gt;Nothing on the live site updated that day. I only found out the next morning, because the page still showed the previous day's date.&lt;/p&gt;

&lt;p&gt;The guardrails I added afterwards are four:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;exec &amp;lt;/dev/null&lt;/code&gt; — any child process reading stdin gets EOF immediately, so nothing hangs silently&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;timeout&lt;/code&gt; on every step: 20 minutes for fetch, 7 for summarize, 5 for build. If it's over, let it die rather than drag on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;python3 -u&lt;/code&gt; — buffering off, so when it gets killed you can at least see which line it reached&lt;/li&gt;
&lt;li&gt;Every step appends one timestamped line to a log file, so afterwards you can see exactly where it stuck&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Later audits found the same class of hazard twice more, so I added two budgets: 420 seconds total for the translation step, 300 seconds total for Reddit retries. The old backoff retry could sleep for ten minutes in the worst case — the same failure shape as September 15.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tradeoffs I made
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One platform failing doesn't block the other 19.&lt;/strong&gt; The cost is that failures go silent, so I watch "how many platforms returned data today" instead of the job's success status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When a fetch fails, keep the previous data&lt;/strong&gt; and tag it as stale; the front end marks it. I don't overwrite readable content with empty data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Translate 15 items per request.&lt;/strong&gt; Send 30 and the model's output gets truncated, JSON parsing fails, and the whole batch is wasted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For the front-page summary, the model may only return item indices.&lt;/strong&gt; Headlines and links are backfilled by the script from the raw data, so the model never gets a chance to invent a link that doesn't exist.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I don't walk unapproved paths to get Reddit scores&lt;/strong&gt;, and I don't queue up for developer-platform applications. The cost/benefit is bad.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If you want to copy this, three steps
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;First: put a &lt;code&gt;timeout&lt;/code&gt; on every step, and add &lt;code&gt;exec &amp;lt;/dev/null&lt;/code&gt; at the top of the script.&lt;/strong&gt; Five minutes of work that would have saved me a full day of lost data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: write one progress line per step.&lt;/strong&gt; Nothing fancy — a &lt;code&gt;mark&lt;/code&gt; function that appends a timestamp and step name to a file. When things break, that line is your fastest lead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third: decide what happens after a single platform fails.&lt;/strong&gt; Does the whole job fail, or do you keep old data and record how many platforms succeeded? I chose the latter, because one platform going down shouldn't waste the other 19.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls from my own notes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Chinese platforms: don't use a proxy. Overseas platforms: you must. Both requirements coexist in the same script — it's not one global switch.&lt;/li&gt;
&lt;li&gt;Different item counts per platform are normal. V2EX giving 7 and sspai giving 10 is the platform's ceiling, not a defect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Zero items returned" and "fetch failed" need separate handling.&lt;/strong&gt; The former raises no exception; it just makes you think everything is fine.&lt;/li&gt;
&lt;li&gt;Don't write build logs to the system temp directory. Once there was a stale file there owned by a different user; wrong permissions failed the whole job.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HOME&lt;/code&gt; inside a cron environment is not your home directory. Export it on the second line of the script, or every config and credential lookup fails.&lt;/li&gt;
&lt;li&gt;The timestamp log file accumulates thousands of lines a month. Remember to prune it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;40 lines. The comments mark which parts you need to change for your own environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# Daily update: fetch -&amp;gt; translate -&amp;gt; build -&amp;gt; commit &amp;amp; push&lt;/span&gt;
&lt;span class="c"&gt;# Every step has a timeout fallback + exec &amp;lt;/dev/null: a hung step must not take the job down.&lt;/span&gt;
&lt;span class="c"&gt;# Lesson: one silently hung step -&amp;gt; whole script hit the job limit and got killed, a full day of data lost&lt;/span&gt;
&lt;span class="c"&gt;# (output went through a pipe, line buffering swallowed by block buffering, not one word in the report).&lt;/span&gt;
&lt;span class="nb"&gt;exec&lt;/span&gt; &amp;lt;/dev/null                &lt;span class="c"&gt;# any child reading stdin gets EOF immediately, so nothing hangs silently&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/youruser      &lt;span class="c"&gt;# inside cron this is often /root; set it explicitly&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/repo &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"REPO NOT FOUND"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;STEPS_LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.logs/steps.log"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STEPS_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
mark&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"== &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt; =="&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%F %T'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$STEPS_LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;

mark &lt;span class="s2"&gt;"fetch &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; +%H:%M:%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;timeout&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 30 1200 python3 &lt;span class="nt"&gt;-u&lt;/span&gt; scripts/fetch.py &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"FETCH FAILED (rc=&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;; 124=step timeout)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

mark &lt;span class="s2"&gt;"summarize &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; +%H:%M:%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;timeout&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 30 420 python3 &lt;span class="nt"&gt;-u&lt;/span&gt; scripts/summarize.py &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"SUMMARIZE FAILED (non-fatal)"&lt;/span&gt;

mark &lt;span class="s2"&gt;"build &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; +%H:%M:%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;BUILDLOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.logs/build.log"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILDLOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;timeout&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 30 300 npm run build &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILDLOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"BUILD FAILED (rc=&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-5&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BUILDLOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

mark &lt;span class="s2"&gt;"git &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; +%H:%M:%S&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git add &lt;span class="nt"&gt;-A&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;git diff &lt;span class="nt"&gt;--cached&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"no changes to commit (data unchanged)"&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;timeout &lt;/span&gt;180 git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"daily update &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;TZ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Asia/Shanghai &lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m-%d&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"COMMIT FAILED"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;timeout &lt;/span&gt;180 git push origin main &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PUSH FAILED"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"DONE in &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; start &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only four things matter: the &lt;code&gt;exec &amp;lt;/dev/null&lt;/code&gt; on the first line, the numbers in each &lt;code&gt;timeout&lt;/code&gt;, the unbuffered &lt;code&gt;python3 -u&lt;/code&gt;, and that &lt;code&gt;mark&lt;/code&gt; function. The rest is paths to change for your own environment.&lt;/p&gt;







&lt;p&gt;&lt;em&gt;I also keep a small paid community where I write these engineering logs up in full, one deep dive a week. Its content is in Chinese — I mention it in case you read Chinese: &lt;a href="https://t.zsxq.com/fx2y5" rel="noopener noreferrer"&gt;AI落地实录&lt;/a&gt; (¥25/year with the current new-member coupon, ¥50 list price).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally written in Chinese for my own notes; this English version is a translation.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>python</category>
      <category>devops</category>
      <category>api</category>
    </item>
    <item>
      <title>I checked all 12 Reddit subreddits for freelance gigs. Only 3 are usable.</title>
      <dc:creator>hc_xshh</dc:creator>
      <pubDate>Sat, 26 Sep 2026 05:43:01 +0000</pubDate>
      <link>https://dev.to/hcxshh/i-checked-all-12-reddit-subreddits-for-freelance-gigs-only-3-are-usable-4p5d</link>
      <guid>https://dev.to/hcxshh/i-checked-all-12-reddit-subreddits-for-freelance-gigs-only-3-are-usable-4p5d</guid>
      <description>&lt;p&gt;I spent a few weeks going through every subreddit people mention when the topic of finding freelance work from overseas comes up. Twelve of them. I pulled subscriber counts and rules through Reddit's own API, and activity numbers from the official RSS feeds, so the numbers below are traceable rather than remembered.&lt;/p&gt;

&lt;p&gt;Here is what I found, and what I would tell anyone thinking about Reddit as a client source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three subreddits actually work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;r/forhire&lt;/strong&gt; — 684,000 subscribers, about 33 new posts a day. The hard rule: no gigs priced below &lt;strong&gt;$15/hour&lt;/strong&gt;. It protects you from a race to the bottom, and it also caps the low end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;r/DesignJobs&lt;/strong&gt; — 202,000 subscribers, design work. The threshold is written explicitly: the account must be &lt;strong&gt;older than 60 days and have more than 200 karma&lt;/strong&gt;. Floor price is $20/hour.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;r/gameDevClassifieds&lt;/strong&gt; — 158,000 subscribers, game outsourcing. Be careful: rev-share projects, free work, AI art and personal commissions are all banned. If you are a solo artist or writer, this one is mostly closed to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ones that are dead ends
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;r/freelance&lt;/strong&gt; bans hiring and self-promotion outright, and the penalty is permanent ban. &lt;strong&gt;r/WorkOnline&lt;/strong&gt; explicitly forbids "for hire" posts. &lt;strong&gt;r/beermoney&lt;/strong&gt; is a survey site directory, not a job board. &lt;strong&gt;r/jobbit&lt;/strong&gt; is effectively abandoned: the newest 100 posts run from November 2025 to 31 August 2026, and nothing after that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The karma wall nobody publishes
&lt;/h2&gt;

&lt;p&gt;Both r/forhire and r/slavelabour state in their rules that the karma threshold is not public and will not be answered. What I could measure elsewhere: r/hiring wants 25 comment karma, r/DoneDirtCheap wants 20, r/b2bforhire wants a two-month-old account, r/INAT wants a new account to be at least a week old.&lt;/p&gt;

&lt;p&gt;The practical consequence: a new account that posts gets silently removed. No error, no explanation. If you are starting from zero, the first job is not writing a portfolio, it is spending a couple of weeks commenting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Money never touches the platform
&lt;/h2&gt;

&lt;p&gt;No escrow, no guarantee, no dispute process. Payment happens off-platform: PayPal, gift cards, Bitcoin. The only line in the rules about it suggests you consider using an escrow service. If something goes wrong, the moderators are explicit that they will not get involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap worth knowing
&lt;/h2&gt;

&lt;p&gt;When your post is auto-removed, do not message the moderators to ask why or to request an exception. The r/forhire rules state that contacting the moderators for an exception results in a ban. Another large subreddit puts it as: no exceptions will be made for any reason, ever. The correct move is to fix the post and submit again.&lt;/p&gt;

&lt;h2&gt;
  
  
  So is it worth it
&lt;/h2&gt;

&lt;p&gt;For someone outside the US who wants paying clients without a platform taking a cut, Reddit is one of the few places where the rulebook is public and the demand is real. It is also gated by account history, moderated by automations that do not explain themselves, and it will not hold your money.&lt;/p&gt;

&lt;p&gt;Treat the first month as an investment in account reputation, not in finding work.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep the longer write-ups and the raw measurements at &lt;a href="https://notebookfield.com/" rel="noopener noreferrer"&gt;Field Notes&lt;/a&gt; — no ads, no tracking, one-click unsubscribe.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>freelance</category>
      <category>discuss</category>
    </item>
  </channel>
</rss>
