<?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: Maya Collins</title>
    <description>The latest articles on DEV Community by Maya Collins (@mayacollins1).</description>
    <link>https://dev.to/mayacollins1</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%2F4113622%2F74901a55-5a58-4032-975f-95482f0c2af7.png</url>
      <title>DEV Community: Maya Collins</title>
      <link>https://dev.to/mayacollins1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mayacollins1"/>
    <language>en</language>
    <item>
      <title>A Practical Python Pipeline for Batch Image Generation</title>
      <dc:creator>Maya Collins</dc:creator>
      <pubDate>Wed, 09 Sep 2026 02:22:27 +0000</pubDate>
      <link>https://dev.to/mayacollins1/a-practical-python-pipeline-for-batch-image-generation-28ch</link>
      <guid>https://dev.to/mayacollins1/a-practical-python-pipeline-for-batch-image-generation-28ch</guid>
      <description>&lt;p&gt;I prefer treating image generation as a job-processing problem rather than embedding a provider SDK throughout an application.&lt;/p&gt;

&lt;p&gt;Put each request into a queue, select a model at routing time, limit concurrency, retry only temporary failures, and save the resulting asset with a manifest record. With a unified API, compatible models can share the same authentication and request path; changing models then becomes a routing decision instead of a new integration.&lt;/p&gt;

&lt;p&gt;This walkthrough builds that workflow in Python. It accepts JSON Lines jobs, routes them by type, handles URL and base64 responses, records estimated usage, and writes successful and failed jobs to a manifest.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pipeline
&lt;/h2&gt;

&lt;p&gt;The basic flow is:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;jobs.jsonl → bounded worker pool → /v1/images/generations → object storage → manifest.jsonl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The queue and storage remain under application control. In this example, the API base URL is &lt;code&gt;https://api.cometapi.com/v1&lt;/code&gt;, using one server-side CometAPI key.&lt;/p&gt;

&lt;p&gt;The implementation covers the production concerns I usually want in a first version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Durable job IDs&lt;/li&gt;
&lt;li&gt;Model selection and catalog validation&lt;/li&gt;
&lt;li&gt;Bounded concurrency&lt;/li&gt;
&lt;li&gt;Exponential backoff with jitter&lt;/li&gt;
&lt;li&gt;URL and base64 image responses&lt;/li&gt;
&lt;li&gt;Estimated per-job cost&lt;/li&gt;
&lt;li&gt;A manifest containing success and failure records&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Requirements and Model Selection
&lt;/h2&gt;

&lt;p&gt;You need Python 3.10 or later, the &lt;code&gt;requests&lt;/code&gt; package, a key, and a writable output directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the key on the server rather than putting it in browser code or a repository:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;COMETAPI_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generation endpoint is &lt;code&gt;POST /images/generations&lt;/code&gt; under the base URL &lt;code&gt;https://api.cometapi.com/v1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before deploying, check the &lt;a href="https://api.cometapi.com/api/models" rel="noopener noreferrer"&gt;live model catalog&lt;/a&gt;. It returns the current model ID, supported endpoint, features, and pricing metadata without requiring an authorization header.&lt;/p&gt;

&lt;p&gt;As of August 20, 2026, two useful routes in that catalog were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Product images with controlled settings&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gpt-image-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns usage data and base64 image content on the documented OpenAI-compatible route&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-volume advertising and content concepts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;doubao-seedream-4-5-251128&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses the same generation route and is listed with per-request pricing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These models should not be assumed to support identical options. Size, quality, format, reference-image handling, and response behavior can differ. I would check the individual model record before sending optional parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design the Input Around Stable IDs
&lt;/h2&gt;

&lt;p&gt;JSON Lines keeps the input simple. A queue consumer, database export, or spreadsheet conversion can produce the same format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"sku-1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Studio product photo of a ceramic coffee dripper on a warm neutral background"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"campaign-204"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"ad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Editorial summer travel image, vivid natural light, wide composition, no text"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"blog-088"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Minimal illustration of a developer automating a creative workflow, no text"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; is used for the output filename and manifest key. In a real queue, I would also use it as the idempotency key and skip IDs already marked successful.&lt;/p&gt;

&lt;p&gt;The sample routing is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;product&lt;/code&gt; → &lt;code&gt;gpt-image-2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ad&lt;/code&gt; → &lt;code&gt;doubao-seedream-4-5-251128&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;content&lt;/code&gt; → &lt;code&gt;doubao-seedream-4-5-251128&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A job can override the default with its own &lt;code&gt;model&lt;/code&gt; field. The script checks the live catalog at startup so an obsolete model ID fails early instead of producing a series of invalid requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concurrency and Retry Behavior
&lt;/h2&gt;

&lt;p&gt;The default worker count is four. That is an application-level starting point, not a universal account limit. I would monitor latency and &lt;code&gt;429&lt;/code&gt; responses before increasing &lt;code&gt;MAX_WORKERS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Only these responses are retried:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;408&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;429&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;5xx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The retry loop uses exponential backoff and jitter. Authentication failures, invalid models, and unsupported parameters are not retried because the request itself must be fixed first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save the Actual Image, Not Just a Provider URL
&lt;/h2&gt;

&lt;p&gt;The documented GPT Image response includes &lt;code&gt;data[0].b64_json&lt;/code&gt;. Other compatible models may return &lt;code&gt;data[0].url&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;The worker supports both forms. It writes the result to a temporary path conceptually by completing the download or decode first, then stores the final file. For a production deployment, I would replace the local &lt;code&gt;output/&lt;/code&gt; directory with S3, R2, GCS, or another object store.&lt;/p&gt;

&lt;p&gt;A provider-hosted URL should not be treated as permanent storage unless its retention policy explicitly guarantees that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete Python Example
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;batch_image_pipeline.py&lt;/code&gt;. Put &lt;code&gt;jobs.jsonl&lt;/code&gt; beside it, then run &lt;code&gt;python3 batch_image_pipeline.py&lt;/code&gt;.&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;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;

&lt;span class="n"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.cometapi.com/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;KEY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;COMETAPI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;WORKERS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MAX_WORKERS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ROUTES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;product&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-image-2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ad&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubao-seedream-4-5-251128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;doubao-seedream-4-5-251128&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;catalog&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://api.cometapi.com/api/models&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;CATALOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;catalog&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ROUTES&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kind&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CATALOG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;ValueError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unknown model: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-image-2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quality&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;low&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1024x1024&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jpeg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;BASE_URL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/images/generations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;KEY&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;180&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;408&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;lt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;
        &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b64_json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;b64_json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_format&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;
        &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image/webp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;webp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content-type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jpg&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;extension&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CATALOG&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pricing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;usage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;per_request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&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="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
                &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&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="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;estimated_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ratio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;safe_generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;job&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;

&lt;span class="n"&gt;OUT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exist_ok&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jobs.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;splitlines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;WORKERS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;safe_generate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;with &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;OUT&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;manifest.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writelines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The catalog is fetched at runtime, but the fallback routes in &lt;code&gt;ROUTES&lt;/code&gt; were verified on August 20, 2026. They should be checked again before deploying on another date.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run a Small Smoke Test First
&lt;/h2&gt;

&lt;p&gt;Start with one worker and one job:&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="nv"&gt;MAX_WORKERS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 python3 batch_image_pipeline.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful GPT Image response has this shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1776841943&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output_format"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jpeg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"quality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1024x1024"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"usage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;208&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;224&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"b64_json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script decodes this response and writes &lt;code&gt;output/.jpeg&lt;/code&gt;. It then adds a success row to &lt;code&gt;output/manifest.jsonl&lt;/code&gt;. URL-based responses are downloaded and represented in the same manifest format.&lt;/p&gt;

&lt;p&gt;The code was syntax-checked locally, but a live generation request still requires a valid key. I would run this one-job test before increasing concurrency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estimating Cost
&lt;/h2&gt;

&lt;p&gt;Pricing is time-sensitive. On August 20, 2026, the live catalog returned these base values and a &lt;code&gt;0.8&lt;/code&gt; billing ratio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;gpt-image-2&lt;/code&gt;: &lt;code&gt;$5&lt;/code&gt; per 1M input tokens and &lt;code&gt;$30&lt;/code&gt; per 1M output tokens&lt;/li&gt;
&lt;li&gt;Effective rates after the listed ratio: &lt;code&gt;$4&lt;/code&gt; per 1M input tokens and &lt;code&gt;$24&lt;/code&gt; per 1M output tokens&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doubao-seedream-4-5-251128&lt;/code&gt;: &lt;code&gt;$0.04&lt;/code&gt; per request&lt;/li&gt;
&lt;li&gt;Effective request price after the listed ratio: &lt;code&gt;$0.032&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://apidoc.cometapi.com/pricing/about-pricing" rel="noopener noreferrer"&gt;pricing guide&lt;/a&gt; describes token-based billing for models with official token pricing and request-based billing for models priced per call.&lt;/p&gt;

&lt;p&gt;The script applies these calculations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;token cost = ratio × (input tokens × input rate + output tokens × output rate) / 1,000,000
request cost = ratio × per-request price
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the documented GPT Image response with 16 input tokens and 208 output tokens, the August 20 catalog values produce an illustrative estimate of about &lt;code&gt;$0.005056&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The actual bill depends on the model, prompt, quality, size, response usage, and retries. Account usage and API responses should be treated as the billing record rather than a fixed per-image estimate.&lt;/p&gt;

&lt;p&gt;I also budget for failed or rejected work. A retry after an uncertain timeout may create a second billable result. A technically successful image can still fail review. A useful operational metric is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;effective cost per accepted image = total batch spend / approved images
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Troubleshooting Guide
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;401&lt;/td&gt;
&lt;td&gt;Missing or invalid key&lt;/td&gt;
&lt;td&gt;Check the server-side &lt;code&gt;COMETAPI_KEY&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400&lt;/td&gt;
&lt;td&gt;Invalid model or unsupported option&lt;/td&gt;
&lt;td&gt;Recheck the live catalog and remove model-specific fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;429&lt;/td&gt;
&lt;td&gt;Excessive concurrency&lt;/td&gt;
&lt;td&gt;Lower &lt;code&gt;MAX_WORKERS&lt;/code&gt; and retain exponential backoff&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Repeated 5xx&lt;/td&gt;
&lt;td&gt;Temporary upstream failure&lt;/td&gt;
&lt;td&gt;Retry with a cap, then use a dead-letter queue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No saved image&lt;/td&gt;
&lt;td&gt;Different response container&lt;/td&gt;
&lt;td&gt;Inspect &lt;code&gt;data[0]&lt;/code&gt; and support &lt;code&gt;b64_json&lt;/code&gt; or &lt;code&gt;url&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate spend&lt;/td&gt;
&lt;td&gt;Replay after partial failure&lt;/td&gt;
&lt;td&gt;Use durable IDs and acknowledge only after storage succeeds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A permanent &lt;code&gt;400&lt;/code&gt; will not become valid through retries. Likewise, an unlimited &lt;code&gt;429&lt;/code&gt; loop can turn a traffic spike into a growing backlog.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Change for Production
&lt;/h2&gt;

&lt;p&gt;For multiple workers, I would replace JSON Lines with a durable queue. Set the visibility timeout longer than the maximum generation time, acknowledge only after both the image and manifest are stored, and send exhausted jobs to a dead-letter queue.&lt;/p&gt;

&lt;p&gt;Keep optional parameters model-specific. The shared payload should contain only fields such as &lt;code&gt;model&lt;/code&gt;, &lt;code&gt;prompt&lt;/code&gt;, and &lt;code&gt;n: 1&lt;/code&gt;. Add &lt;code&gt;quality&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, or &lt;code&gt;output_format&lt;/code&gt; only when the selected model documentation supports them. If fallback routing is added, rebuild the payload for the fallback model rather than blindly reusing provider-specific options.&lt;/p&gt;

&lt;p&gt;Other practical safeguards include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store keys in a secret manager.&lt;/li&gt;
&lt;li&gt;Restrict and validate prompt input.&lt;/li&gt;
&lt;li&gt;Scan generated assets according to your policy.&lt;/li&gt;
&lt;li&gt;Keep provider URLs out of long-term product records.&lt;/li&gt;
&lt;li&gt;Log job ID, model ID, latency, attempts, usage, storage path, review result, and catalog snapshot date.&lt;/li&gt;
&lt;li&gt;Set a maximum batch size and per-job retry limit.&lt;/li&gt;
&lt;li&gt;Add daily spend alerts and approval-rate stop conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key metric is not headline price. It is accepted-image cost after retries, failures, post-processing, and review.&lt;/p&gt;

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

&lt;p&gt;For endpoint and response details, use the &lt;a href="https://apidoc.cometapi.com/overview/quick-start" rel="noopener noreferrer"&gt;Quick Start&lt;/a&gt;, &lt;a href="https://apidoc.cometapi.com/overview/models" rel="noopener noreferrer"&gt;model catalog documentation&lt;/a&gt;, &lt;a href="https://apidoc.cometapi.com/api/image/openai/images" rel="noopener noreferrer"&gt;image generation reference&lt;/a&gt;, and &lt;a href="https://apidoc.cometapi.com/pricing/about-pricing" rel="noopener noreferrer"&gt;pricing guide&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
    </item>
    <item>
      <title>GPT-6 Astra Is Expensive per Token. I’m Not Sure That’s the Right Metric</title>
      <dc:creator>Maya Collins</dc:creator>
      <pubDate>Tue, 08 Sep 2026 03:10:12 +0000</pubDate>
      <link>https://dev.to/mayacollins1/gpt-6-astra-is-expensive-per-token-im-not-sure-thats-the-right-metric-1621</link>
      <guid>https://dev.to/mayacollins1/gpt-6-astra-is-expensive-per-token-im-not-sure-thats-the-right-metric-1621</guid>
      <description>&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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5z87kjv613h0v5fpki8w.png" 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%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5z87kjv613h0v5fpki8w.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing that jumped out at me when GPT-6 Astra launched was the price.&lt;/p&gt;

&lt;p&gt;Standard API pricing starts at &lt;strong&gt;$10 per million input tokens and $50 per million output tokens&lt;/strong&gt;. That’s 2.5× the short-context token price of GPT-5.6 Sol. Once a request goes beyond 272K input tokens, Astra moves onto an even more expensive long-context schedule. &lt;/p&gt;

&lt;p&gt;On paper, that makes the decision look pretty straightforward.&lt;/p&gt;

&lt;p&gt;If two models can do roughly the same work and one costs 2.5× more per token, use the cheaper one.&lt;/p&gt;

&lt;p&gt;The problem is that Astra doesn’t seem to be optimized around doing the same work slightly better.&lt;/p&gt;

&lt;p&gt;Most of the interesting gains show up when the model has to keep acting until something is actually finished.&lt;/p&gt;

&lt;p&gt;That changes the economics quite a bit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark pattern is stranger than “Astra is smarter”
&lt;/h2&gt;

&lt;p&gt;If you only look at broad intelligence scores, Astra doesn’t look like a huge generational jump.&lt;/p&gt;

&lt;p&gt;On the Artificial Analysis Intelligence Index reported in OpenAI’s comparison, GPT-6 Astra scores &lt;strong&gt;61.2&lt;/strong&gt;, while GPT-5.6 Sol scores &lt;strong&gt;60.9&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That’s basically flat.&lt;/p&gt;

&lt;p&gt;Now look at some of the execution-heavy benchmarks.&lt;/p&gt;

&lt;p&gt;On Terminal-Bench 4.0:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT-6 Astra: &lt;strong&gt;57.9%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;GPT-5.6 Sol: &lt;strong&gt;37.3%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On AutomationBench:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT-6 Astra: &lt;strong&gt;41.4%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;GPT-5.6 Sol: &lt;strong&gt;18.1%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And on very-long-context MRCR in the 512K–1M range:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GPT-6 Astra: &lt;strong&gt;96.3%&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;GPT-5.6 Sol: &lt;strong&gt;73.8%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s a very different story. &lt;/p&gt;

&lt;p&gt;The improvement is concentrated around tasks where the model has to interact with an environment, use tools, keep track of state, recover from mistakes, and carry something through to completion.&lt;/p&gt;

&lt;p&gt;That’s also much closer to the kind of work I’d consider paying premium-model prices for.&lt;/p&gt;

&lt;h2&gt;
  
  
  A failed cheap run is still expensive
&lt;/h2&gt;

&lt;p&gt;This is where token pricing starts becoming a bad shortcut.&lt;/p&gt;

&lt;p&gt;Imagine I give a coding agent a repository migration.&lt;/p&gt;

&lt;p&gt;The cheaper model costs $2 for a run.&lt;/p&gt;

&lt;p&gt;The expensive model costs $5.&lt;/p&gt;

&lt;p&gt;If the $2 model fails twice before producing something I can accept, while the $5 model gets there on the first attempt, the supposedly cheaper model is no longer obviously cheaper.&lt;/p&gt;

&lt;p&gt;And API spend is only part of it.&lt;/p&gt;

&lt;p&gt;Retries can also mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;another terminal session&lt;/li&gt;
&lt;li&gt;another browser run&lt;/li&gt;
&lt;li&gt;more tool calls&lt;/li&gt;
&lt;li&gt;duplicated infrastructure work&lt;/li&gt;
&lt;li&gt;more time waiting&lt;/li&gt;
&lt;li&gt;another human review pass&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once agents start doing real work, I care much more about what the entire task costs than what one token costs.&lt;/p&gt;

&lt;p&gt;That’s why I’d rather track &lt;strong&gt;cost per accepted task&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s a boring metric, but it forces everything into the same number: token usage, retries, failed attempts, and whether the output was actually useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Astra’s token efficiency makes this less obvious than I expected
&lt;/h2&gt;

&lt;p&gt;There’s another wrinkle here.&lt;/p&gt;

&lt;p&gt;Independent testing from Artificial Analysis found that Astra used roughly &lt;strong&gt;three times fewer tokens than GPT-5.6 Sol at max effort in its coding-agent setup&lt;/strong&gt;, while still scoring higher on its Coding Agent Index.&lt;/p&gt;

&lt;p&gt;That meant Astra could end up around the same cost per coding-agent task despite having much more expensive individual tokens. &lt;/p&gt;

&lt;p&gt;That does not mean Astra is suddenly cheap.&lt;/p&gt;

&lt;p&gt;The same testing found a very different result on general-intelligence workloads. There, the higher per-token price outweighed the token savings, leaving Astra roughly &lt;strong&gt;75% more expensive per task&lt;/strong&gt; at max effort. &lt;/p&gt;

&lt;p&gt;I actually like that result because it makes model selection less simplistic.&lt;/p&gt;

&lt;p&gt;For coding agents, the premium may be recoverable.&lt;/p&gt;

&lt;p&gt;For ordinary reasoning, maybe not.&lt;/p&gt;

&lt;p&gt;Those are exactly the distinctions that disappear when everything gets reduced to "$10 input / $50 output."&lt;/p&gt;

&lt;h2&gt;
  
  
  I definitely wouldn’t use Astra everywhere
&lt;/h2&gt;

&lt;p&gt;There are plenty of requests where paying for Astra makes little sense to me.&lt;/p&gt;

&lt;p&gt;I wouldn’t reach for it first for basic classification, short rewriting, simple extraction, routine summaries, or other high-volume work that a cheaper model already handles reliably.&lt;/p&gt;

&lt;p&gt;Even within coding, not every issue needs the strongest agent you can buy.&lt;/p&gt;

&lt;p&gt;A one-file config change is very different from a migration that touches 30 files, runs tests, changes infrastructure, and needs to recover from failures along the way.&lt;/p&gt;

&lt;p&gt;The harder it is to recover from a bad result, the easier Astra’s premium becomes to justify.&lt;/p&gt;

&lt;p&gt;That’s the line I’d use.&lt;/p&gt;

&lt;p&gt;Not “hard prompt versus easy prompt.”&lt;/p&gt;

&lt;p&gt;More like &lt;strong&gt;cheap failure versus expensive failure&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 1M context window has a price attached to it
&lt;/h2&gt;

&lt;p&gt;Astra also comes with a roughly &lt;strong&gt;1.05M-token context window and 128K maximum output&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;That sounds great until it encourages people to stop managing context.&lt;/p&gt;

&lt;p&gt;I wouldn’t.&lt;/p&gt;

&lt;p&gt;Requests above 272K input tokens enter Astra’s long-context pricing tier, where Standard pricing rises to &lt;strong&gt;$20 per million input tokens and $75 per million output tokens&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;So the million-token window is useful headroom, but it’s not free storage.&lt;/p&gt;

&lt;p&gt;I’d still use retrieval.&lt;/p&gt;

&lt;p&gt;I’d still trim irrelevant tool history.&lt;/p&gt;

&lt;p&gt;I’d still summarize old state.&lt;/p&gt;

&lt;p&gt;I’d still avoid dumping an entire repository into the prompt just because the API accepts it.&lt;/p&gt;

&lt;p&gt;In fact, the bigger the available context becomes, the more important it is to know which parts are actually helping.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I’d pay the premium
&lt;/h2&gt;

&lt;p&gt;The workloads that make Astra interesting to me are the ones where execution reliability matters more than raw generation price.&lt;/p&gt;

&lt;p&gt;Things like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;repository-wide coding changes&lt;/li&gt;
&lt;li&gt;terminal-heavy agents&lt;/li&gt;
&lt;li&gt;browser and computer-use automation&lt;/li&gt;
&lt;li&gt;SRE and debugging workflows&lt;/li&gt;
&lt;li&gt;long technical investigations&lt;/li&gt;
&lt;li&gt;workflows involving several tools&lt;/li&gt;
&lt;li&gt;professional tasks where the final artifact has to be usable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;OpenAI’s published results line up pretty closely with that. Astra’s biggest gains over Sol show up in computer use, terminal work, automation, science, SRE, and long-context retrieval rather than a giant jump in general intelligence. &lt;/p&gt;

&lt;p&gt;That’s why I think treating Astra as “the new default GPT” misses the point.&lt;/p&gt;

&lt;p&gt;It makes more sense to me as an escalation model.&lt;/p&gt;

&lt;p&gt;Use something cheaper for the normal path.&lt;/p&gt;

&lt;p&gt;Send Astra the jobs where retries, mistakes, or human intervention are actually expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  I’d route before I’d standardize
&lt;/h2&gt;

&lt;p&gt;This is also why I’m increasingly skeptical of picking one model for an entire product.&lt;/p&gt;

&lt;p&gt;Different requests have completely different economics.&lt;/p&gt;

&lt;p&gt;A fast model might be perfect for 70% of the traffic.&lt;/p&gt;

&lt;p&gt;A stronger mid-tier model can take another 20%.&lt;/p&gt;

&lt;p&gt;Maybe Astra only sees the final 10% of tasks that are genuinely difficult.&lt;/p&gt;

&lt;p&gt;If that 10% contains the work responsible for most of your failures, that can still be a very good trade.&lt;/p&gt;

&lt;p&gt;I’ve been using CometAPI for this kind of comparison because I can keep the surrounding API layer mostly unchanged while swapping models and rerunning the same workload.&lt;/p&gt;

&lt;p&gt;That’s the useful part for me.&lt;/p&gt;

&lt;p&gt;I don’t want to compare a GPT-6 Astra demo against a completely different Gemini or Claude demo.&lt;/p&gt;

&lt;p&gt;I want the same task, same tools, same success criteria, and different models.&lt;/p&gt;

&lt;p&gt;Then I can compare completion rate, token use, latency, retries, and actual cost.&lt;/p&gt;

&lt;p&gt;GPT-6 Astra is undeniably expensive per token.&lt;/p&gt;

&lt;p&gt;For a lot of workloads, it will also simply be expensive per task.&lt;/p&gt;

&lt;p&gt;But for the jobs where failed attempts are the expensive part, I’m not convinced token price is the number that matters most.&lt;/p&gt;

&lt;p&gt;That’s what I’d benchmark before deciding whether the premium is worth paying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; This post is adapted from research originally published by the CometAPI team.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
