<?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: CloudGPU</title>
    <description>The latest articles on DEV Community by CloudGPU (@cloudgpu_app).</description>
    <link>https://dev.to/cloudgpu_app</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%2F4202377%2F0d175f44-703f-4bbe-9f65-e81aa5b0ae28.png</url>
      <title>DEV Community: CloudGPU</title>
      <link>https://dev.to/cloudgpu_app</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cloudgpu_app"/>
    <language>en</language>
    <item>
      <title>A model as the judge: build preference pairs with an API, then train with DPO on a rented GPU</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Sun, 01 Nov 2026 03:10:36 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/a-model-as-the-judge-build-preference-pairs-with-an-api-then-train-with-dpo-on-a-rented-gpu-14ij</link>
      <guid>https://dev.to/cloudgpu_app/a-model-as-the-judge-build-preference-pairs-with-an-api-then-train-with-dpo-on-a-rented-gpu-14ij</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; write a short list of principles and a set of questions, have an API model write two answers to each question, have it pick the answer that follows your principles better (twice, once in each order), keep only the pairs where both picks agree, and train a LoRA adapter on those &lt;code&gt;chosen&lt;/code&gt; and &lt;code&gt;rejected&lt;/code&gt; pairs with DPO in LLaMA-Factory on a rented GPU. Generate the pairs on your own machine; rent the GPU only for training; copy the adapter off, then destroy the instance.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU. The endpoint (&lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;) and the GPU rentals below are ours. Each step ends with a "What to check" line, because details (versions, template names, model access) change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the idea comes from, and what this guide does not claim
&lt;/h2&gt;

&lt;p&gt;Using a model's judgments as the training signal is usually called RL from AI Feedback (RLAIF). One public paper on it, "RLAIF vs. RLHF: Scaling Reinforcement Learning from Human Feedback with AI Feedback" (Lee et al., arXiv, submitted 2023-09-01: &lt;a href="https://arxiv.org/abs/2309.00267" rel="noopener noreferrer"&gt;paper&lt;/a&gt;), describes training a reward model on preferences generated by an off-the-shelf LLM and then using reinforcement learning. This guide keeps only the "a model states a preference between two answers" idea and skips the rest: it trains directly on the pairs with Direct Preference Optimization (&lt;a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer"&gt;paper&lt;/a&gt;), using LLaMA-Factory.&lt;/p&gt;

&lt;p&gt;That makes this a small, single-developer adaptation. It is our own construction, not a result from those sources. A model's pick can be wrong, and we make no claim that the adapter you train will be better than the base model. That is why the last step is a comparison you read yourself, with a rule for when to throw the round away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read these things first
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek's terms.&lt;/strong&gt; The answers and the picks in this guide come from DeepSeek, and this guide covers DeepSeek only. DeepSeek's &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026) say in section 4.2(3) that you may apply the Outputs of the Services to "a wide range of use cases, including personal use, academic research, derivative product development, training other models (such as model distillation), etc., as long as such usage is legal and adhere to these Terms." Read the terms yourself. This guide does not cover any other provider, and it does not tell you what any other provider's terms allow; if you want to use another model's outputs for training, read that provider's own terms first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labelling.&lt;/strong&gt; Section 3.1 of the same terms says that if you publish or disseminate outputs, you must clearly indicate that the output content is generated by artificial intelligence. The &lt;code&gt;chosen&lt;/code&gt; and &lt;code&gt;rejected&lt;/code&gt; answers are outputs. If you publish the pairs, a model card or examples from them, label them as AI-generated; the script below writes a &lt;code&gt;DATASET_NOTE.md&lt;/code&gt; you can keep next to the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your data.&lt;/strong&gt; The questions you write must be ones you have the right to use. CloudGPU's &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt; (section 1.8) prohibits training on datasets you do not have rights to use. Also read the &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;terms&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The base model's license.&lt;/strong&gt; Each base model below has its own license. Open the link, read it, and confirm for yourself that your use is allowed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3 and the &lt;code&gt;openai&lt;/code&gt; package on your own machine: &lt;code&gt;pip install openai&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A CloudGPU account with balance, and an API key (the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;API page&lt;/a&gt; has a "Get your API key" link). Keep it in an environment variable, never in code:
&lt;/li&gt;
&lt;/ul&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;CLOUDGPU_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"paste-your-key-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GPU instances are billed per minute, rounded up, with a 1-minute minimum, and billing stops when you destroy the instance (&lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;terms&lt;/a&gt;). Rates are on the &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. Payment is by card or USDT (&lt;a href="https://cloudgpu.app/payments?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;payments&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A Hugging Face account and a read token if you choose Llama or Gemma (both are gated, see the table below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;python3 -c "import os; print(bool(os.environ.get('CLOUDGPU_API_KEY')))"&lt;/code&gt; prints &lt;code&gt;True&lt;/code&gt; in the shell where you will run the scripts, and &lt;code&gt;python3 -c "import openai"&lt;/code&gt; raises no error.&lt;/p&gt;

&lt;p&gt;API calls are billed per token from the same balance as GPU rentals; the current rate for every model is in the table on the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;API page&lt;/a&gt;. This post does not repeat rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: write the principles and the questions (on your own machine)
&lt;/h2&gt;

&lt;p&gt;Create three plain-text files in one folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;principles.txt&lt;/code&gt; is a short list, in your own words, of what a better answer looks like for your use case. An example to replace with yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Answer only what the question asks.
2. If the answer depends on information you were not given, say what is missing instead of guessing.
3. Do not invent names, numbers, links or quotes.
4. Keep the answer under 150 words unless the question asks for more.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;prompts.txt&lt;/code&gt; is the training questions, one per line. Start with ten lines to test the script, then add more; the more varied the questions are, the more the judge has to compare. &lt;code&gt;heldout.txt&lt;/code&gt; is 20 or so questions in the same style that you will &lt;strong&gt;not&lt;/strong&gt; use for training. Write them separately, and do not copy lines between the files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;wc -l prompts.txt heldout.txt&lt;/code&gt; shows the line counts you expect, and no line of &lt;code&gt;heldout.txt&lt;/code&gt; appears in &lt;code&gt;prompts.txt&lt;/code&gt; (&lt;code&gt;grep -x -F -f heldout.txt prompts.txt&lt;/code&gt; prints nothing).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: write two answers, let the model pick, keep consistent picks
&lt;/h2&gt;

&lt;p&gt;For each question the script asks &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt; for two answers at a high temperature, then asks it twice which answer follows your principles better: once with the answers in one order and once in the other. A pair is kept only if both picks choose the same answer. Judges can lean towards whichever answer comes first, or towards the longer one; the two orders and the length count the script prints are our own suggestions for spotting that, not claims from a source. The model ID comes from the live model table on the API page (checked 2026-10-27); the same table lists &lt;code&gt;deepseek-v4-flash&lt;/code&gt; and &lt;code&gt;deepseek-v4-pro&lt;/code&gt;. The OpenAI SDK only needs &lt;code&gt;base_url&lt;/code&gt; and the key changed.&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="c1"&gt;# make_pairs.py
&lt;/span&gt;&lt;span class="kn"&gt;import&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;sys&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;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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://cloudgpu.app/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_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;CLOUDGPU_API_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;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;deepseek-v4.1-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PRINCIPLES&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;principles.txt&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="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;JUDGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You compare two answers to the same question.
Judge them ONLY against these principles:
{principles}

Question:
{question}

Answer A:
{a}

Answer B:
{b}

Reply with exactly one letter: A if Answer A follows the principles better,
B if Answer B follows them better.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;temperature&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;3&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="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;messages&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;role&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;user&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="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# network error, rate limit
&lt;/span&gt;            &lt;span class="nf"&gt;print&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;attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err&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="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUDGE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;principles&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PRINCIPLES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;second&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;letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&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;B&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="bp"&gt;None&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# `one` is shown first
&lt;/span&gt;    &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# `two` is shown first
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v1&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;or&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="ow"&gt;is&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;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;winner1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;two&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;winner2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;two&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;winner1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;winner2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# the judge changed its mind when the order changed
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rejected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;one&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;two&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;winner1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="nf"&gt;else &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;two&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;one&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;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;question&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="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;chosen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;chosen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;rejected&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;questions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;q&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;prompts.txt&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="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&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;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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="mi"&gt;4&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;make_pair&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;pairs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chosen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&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;dpo_pairs.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&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;pairs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;DATASET_NOTE.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AI-generated data: the answers in dpo_pairs.json were generated, and the preferences between &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;them chosen, with deepseek-v4.1-flash from questions written by the dataset author. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Review before use.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;longer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chosen&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rejected&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;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;questions:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;questions&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pairs kept:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pairs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;chosen is the longer answer:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;longer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it first on the ten-line &lt;code&gt;prompts.txt&lt;/code&gt;: &lt;code&gt;python3 make_pairs.py&lt;/code&gt;. Each question takes four API calls (two answers, two picks), so a long &lt;code&gt;prompts.txt&lt;/code&gt; makes many calls; lower &lt;code&gt;max_workers&lt;/code&gt; if you see rate-limit errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;dpo_pairs.json&lt;/code&gt; and &lt;code&gt;DATASET_NOTE.md&lt;/code&gt; exist, and the script prints how many pairs it kept and how many times the chosen answer was the longer one. If the chosen answer is the longer one in nearly every pair, the judge may be choosing length, not your principles: tighten the principles, or drop pairs where the lengths differ a lot. If it keeps very few pairs, the two orders disagree often; read why in a few cases before you change anything. A &lt;code&gt;401&lt;/code&gt; means the key variable is not set in this shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: read the pairs yourself
&lt;/h2&gt;

&lt;p&gt;The format is LLaMA-Factory's preference format: per its &lt;a href="https://llamafactory.readthedocs.io/en/latest/getting_started/data_preparation.html" rel="noopener noreferrer"&gt;data preparation docs&lt;/a&gt;, a preference dataset needs a better response in the &lt;code&gt;chosen&lt;/code&gt; column and a worse one in &lt;code&gt;rejected&lt;/code&gt;. A row looks like this (illustrative, written by hand, not model output):&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;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Does the config file need a restart to take effect?"&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"&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="nl"&gt;"chosen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"The documentation you gave me does not say. Check the section on reloading before relying on it."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rejected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yes, always restart the service after editing the config file."&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;Read a sample:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,random; [print(r['instruction'], '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;CHOSEN:', r['chosen'], '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;REJECTED:', r['rejected'], '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;') for r in random.sample(json.load(open('dpo_pairs.json')), 5)]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; for each sample, ask whether you agree that &lt;code&gt;chosen&lt;/code&gt; follows your principles better than &lt;code&gt;rejected&lt;/code&gt;. If you disagree with several, the judge prompt or the principles are the problem, not the training: fix them and run Step 2 again. Once you are happy with the ten-line run, put all your questions in &lt;code&gt;prompts.txt&lt;/code&gt; and run it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: rent a GPU
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://cloudgpu.app/deploy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;, choose a card. The &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt; say two things that matter here: every template is an image with root SSH, and for anything a template does not cover you can rent a raw GPU (Deploy â†’ Raw GPU) and install what you need over SSH. For this guide, pick a template that includes PyTorch, or choose Raw GPU and install it yourself. Pick a card with enough memory for your base model: the LLaMA-Factory README has a rough memory table (marked as estimates), and &lt;code&gt;nvidia-smi&lt;/code&gt; shows real use during the first training steps.&lt;/p&gt;

&lt;p&gt;Connect with the SSH details of your running instance. Then upload the pairs from your own machine (&lt;code&gt;scp&lt;/code&gt; takes the port with a capital &lt;code&gt;-P&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; dpo_pairs.json &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; connect with &lt;code&gt;ssh -p &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;&lt;/code&gt;, then run &lt;code&gt;nvidia-smi&lt;/code&gt; (a GPU is listed) and &lt;code&gt;python3 -c "import torch; print(torch.cuda.is_available())"&lt;/code&gt; (prints &lt;code&gt;True&lt;/code&gt;). If torch is missing, install PyTorch with CUDA from the &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch install page&lt;/a&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: install LLaMA-Factory and register the preference dataset
&lt;/h2&gt;

&lt;p&gt;These are the install commands from the &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;LLaMA-Factory README&lt;/a&gt; (read 2026-10-27; the repository name is spelled &lt;code&gt;LlamaFactory&lt;/code&gt; there). Don't count on a pre-installed copy being current: check, and install if needed.&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;cd&lt;/span&gt; /workspace
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/hiyouga/LlamaFactory.git
&lt;span class="nb"&gt;cd &lt;/span&gt;LlamaFactory
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements/metrics.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the pairs into &lt;code&gt;data/&lt;/code&gt; and add an entry to &lt;code&gt;data/dataset_info.json&lt;/code&gt;. The &lt;code&gt;"ranking": true&lt;/code&gt; line is what tells LLaMA-Factory this is a preference dataset:&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;cp&lt;/span&gt; /workspace/dpo_pairs.json data/
python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import json
p = "data/dataset_info.json"
info = json.load(open(p, encoding="utf-8"))
info["judge_pairs"] = {
    "file_name": "dpo_pairs.json",
    "ranking": True,
    "columns": {"prompt": "instruction", "query": "input", "chosen": "chosen", "rejected": "rejected"},
}
json.dump(info, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;llamafactory-cli help&lt;/code&gt; prints the command list, and &lt;code&gt;grep -A 3 judge_pairs data/dataset_info.json&lt;/code&gt; shows your entry with &lt;code&gt;"ranking": true&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: choose a base model and train with DPO
&lt;/h2&gt;

&lt;p&gt;Small models are enough to see the pipeline work. Pick one row; the template name must match the model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base model&lt;/th&gt;
&lt;th&gt;LLaMA-Factory &lt;code&gt;template&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;License (read it yourself)&lt;/th&gt;
&lt;th&gt;Access&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Llama 3.2 3B Instruct (&lt;code&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;llama3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Gated on Hugging Face: accept the license, use a token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B IT (&lt;code&gt;google/gemma-3-4b-it&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemma3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Gated on Hugging Face: accept the terms, use a token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mistral 7B Instruct v0.3 (&lt;code&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mistral&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;License shown on the &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;model card&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The model card listed no gate when we looked (2026-10-27)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Template names are from the LLaMA-Factory README's model table; license and access information was read on 2026-10-27 and can change. The Llama and Gemma licenses have conditions that can apply if you distribute or make available a model built on them. Confirm the license for your use yourself before you train or share anything.&lt;/p&gt;

&lt;p&gt;For a gated model, request access on its Hugging Face page, then give the instance a fine-grained &lt;strong&gt;read&lt;/strong&gt; token for just this run (you will revoke it at the end):&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;HF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"paste-a-read-token-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put this in a file named &lt;code&gt;judge_dpo.yaml&lt;/code&gt; in the &lt;code&gt;LlamaFactory&lt;/code&gt; folder. The keys follow the official DPO LoRA example in the repo's &lt;code&gt;examples/train_lora/&lt;/code&gt; folder; the model, template and dataset lines are the parts you change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;### model&lt;/span&gt;
&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;

&lt;span class="c1"&gt;### method&lt;/span&gt;
&lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dpo&lt;/span&gt;
&lt;span class="na"&gt;do_train&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;finetuning_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lora&lt;/span&gt;
&lt;span class="na"&gt;lora_rank&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;lora_target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;
&lt;span class="na"&gt;pref_beta&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;pref_loss&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sigmoid&lt;/span&gt;

&lt;span class="c1"&gt;### dataset&lt;/span&gt;
&lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;judge_pairs&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistral&lt;/span&gt;
&lt;span class="na"&gt;cutoff_len&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;
&lt;span class="na"&gt;preprocessing_num_workers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="c1"&gt;### output&lt;/span&gt;
&lt;span class="na"&gt;output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saves/mistral-7b/lora/judge-dpo&lt;/span&gt;
&lt;span class="na"&gt;logging_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;save_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="na"&gt;plot_loss&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;overwrite_output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;### train&lt;/span&gt;
&lt;span class="na"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;learning_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;5.0e-6&lt;/span&gt;
&lt;span class="na"&gt;num_train_epochs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;
&lt;span class="na"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cosine&lt;/span&gt;
&lt;span class="na"&gt;warmup_ratio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;bf16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Train:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llamafactory-cli train judge_dpo.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;a href="https://github.com/hiyouga/LlamaFactory/tree/main/examples" rel="noopener noreferrer"&gt;LLaMA-Factory examples folder&lt;/a&gt; and compare your YAML with the current DPO example: key names and good values change between releases. &lt;code&gt;pref_loss: sigmoid&lt;/code&gt; is the plain DPO loss in that example.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the log prints a training loss every 10 steps; &lt;code&gt;saves/mistral-7b/lora/judge-dpo&lt;/code&gt; contains &lt;code&gt;adapter_model.safetensors&lt;/code&gt; and &lt;code&gt;adapter_config.json&lt;/code&gt;. If the base model download fails with a 401 or 403, the Hugging Face access request or token is the problem. An out-of-memory error means a smaller &lt;code&gt;cutoff_len&lt;/code&gt;, a smaller model or a card with more memory; &lt;code&gt;nvidia-smi&lt;/code&gt; shows the real use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: compare the base model and the adapter on questions it never saw
&lt;/h2&gt;

&lt;p&gt;A quick test in the terminal (no server is started). Create two small files in the &lt;code&gt;LlamaFactory&lt;/code&gt; folder. &lt;code&gt;infer_base.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistral&lt;/span&gt;
&lt;span class="na"&gt;infer_backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;huggingface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and &lt;code&gt;infer_dpo.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;
&lt;span class="na"&gt;adapter_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saves/mistral-7b/lora/judge-dpo&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistral&lt;/span&gt;
&lt;span class="na"&gt;infer_backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;huggingface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask each one the questions in &lt;code&gt;heldout.txt&lt;/code&gt; (copy that file to the instance first, or paste the questions):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llamafactory-cli chat infer_base.yaml
llamafactory-cli chat infer_dpo.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read both sets of answers against your own &lt;code&gt;principles.txt&lt;/code&gt;. A lower training loss is not the test; your reading of questions the adapter never trained on is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; for each held-out question, write down which answer follows your principles better, or "can't tell". Stop rule: if you cannot say that the adapter's answers follow your principles better more often than the base model's, throw this round away. Change the principles, the questions or the judge prompt and start again from Step 2; do not publish the adapter or write that it improved anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: copy the adapter off, then destroy the instance
&lt;/h2&gt;

&lt;p&gt;Before you destroy anything, copy out what you want to keep: destroying the instance permanently deletes its local storage. The docs have a section called "What to save before you destroy an instance"; read it in the &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt;. From your own machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &amp;lt;ssh-port&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/LlamaFactory/saves/mistral-7b/lora/judge-dpo/ ./adapter-judge-dpo/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that &lt;code&gt;adapter_model.safetensors&lt;/code&gt; is in &lt;code&gt;./adapter-judge-dpo/&lt;/code&gt;, revoke the Hugging Face token you pasted on the instance (if you used one), then destroy the instance from your Dashboard (the docs: "You can destroy an instance at any time"). The minutes you spent copying are billed like any other minute, and billing stops once the instance is destroyed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the instance no longer appears as running in your Dashboard, and &lt;code&gt;./adapter-judge-dpo/&lt;/code&gt; still opens on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and access, in plain terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Step 2 sends your questions, the principles, and the candidate answers to a model API. Don't send text you would not want processed by a model service, and read the &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt; for what the service handles.&lt;/li&gt;
&lt;li&gt;You keep ownership of what you put on the GPU instance. CloudGPU's &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;terms&lt;/a&gt; (section 5) say it does not access the contents of your instance except where needed to investigate a credible report of a policy violation, where compelled by valid legal process, or to protect the safety of users or third parties. Local instance storage is deleted when you destroy the instance.&lt;/li&gt;
&lt;li&gt;This guide does not start a server. If you later serve your model from an instance, use your own endpoint with your own API key: the terms (section 6) say instance service URLs are not authenticated by default, so add authentication yourself before you share the URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All read on 2026-10-27 (UTC+8), except the RLAIF paper, read on 2026-11-01 (UTC+8).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lee et al., "RLAIF vs. RLHF: Scaling Reinforcement Learning from Human Feedback with AI Feedback" (arXiv 2309.00267, submitted 2023-09-01, read 2026-11-01 UTC+8): &lt;a href="https://arxiv.org/abs/2309.00267" rel="noopener noreferrer"&gt;paper&lt;/a&gt;. Direct Preference Optimization: &lt;a href="https://arxiv.org/abs/2305.18290" rel="noopener noreferrer"&gt;paper&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;DeepSeek, Terms of Use, Last Update March 27, 2026: &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;cdn.deepseek.com&lt;/a&gt;, sections 3.1 and 4.2.&lt;/li&gt;
&lt;li&gt;LLaMA-Factory: &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;README&lt;/a&gt; and &lt;a href="https://llamafactory.readthedocs.io/en/latest/getting_started/data_preparation.html" rel="noopener noreferrer"&gt;data preparation docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;CloudGPU: &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;API page&lt;/a&gt;, &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;terms&lt;/a&gt; (sections 4 to 6), &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt; (section 1.8, last updated April 22, 2026), &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt;, &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t7_model_as_judge_dpo" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Licenses: &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt;, &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;Mistral 7B Instruct v0.3 model card&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Run your own model endpoint on a rented GPU with vLLM: your own API key, an SSH tunnel, and an optional LoRA adapter</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Sat, 31 Oct 2026 05:31:59 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/run-your-own-model-endpoint-on-a-rented-gpu-with-vllm-your-own-api-key-an-ssh-tunnel-and-an-3h9p</link>
      <guid>https://dev.to/cloudgpu_app/run-your-own-model-endpoint-on-a-rented-gpu-with-vllm-your-own-api-key-an-ssh-tunnel-and-an-3h9p</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; rent a GPU with SSH access, install vLLM in a fresh Python environment, start it so it listens only on the instance's own localhost, require an API key of your own, and reach it from your machine through an SSH tunnel. If you have a LoRA adapter for the same base model, vLLM can serve it next to the base model under its own name. When you are done, copy out what you need and destroy the instance.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU, and the GPU rental in this guide is ours. Each step ends with a "What to check" line, because versions, flags and defaults change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which kind of endpoint is this?
&lt;/h2&gt;

&lt;p&gt;CloudGPU's &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;terms&lt;/a&gt; (section 6) say that each running instance with a service URL gets a public HTTPS address; these URLs are "hard to guess but they are not authenticated by default", and if you need authentication you configure it inside your instance, for example an API key on your Ollama deployment. The &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;docs&lt;/a&gt; say that templates that serve an API, such as Ollama and ComfyUI, get such a URL.&lt;/p&gt;

&lt;p&gt;This guide takes a different route on purpose. You rent a raw GPU, run vLLM on it, and do not publish any URL: the server listens only on the instance's own localhost, requires your own API key, and you reach it through an SSH tunnel from your machine. That gives you an endpoint only you can use, as long as you keep the SSH access and the key to yourself.&lt;/p&gt;

&lt;p&gt;vLLM's own &lt;a href="https://docs.vllm.ai/en/latest/usage/security/" rel="noopener noreferrer"&gt;security guide&lt;/a&gt; is clear about the limits of the key: &lt;code&gt;--api-key&lt;/code&gt; protects only the endpoints under the &lt;code&gt;/v1&lt;/code&gt;, &lt;code&gt;/v2&lt;/code&gt;, &lt;code&gt;/inference&lt;/code&gt; and &lt;code&gt;/cohere&lt;/code&gt; prefixes, other endpoints on the same server are not covered, and it says not to rely on &lt;code&gt;--api-key&lt;/code&gt; alone. It also notes that some underlying components may listen on all network interfaces. So treat the key as one layer, keep the tunnel as the way in, and do not send anything to the instance that you would not put on a rented machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read these things first
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The license of your base model.&lt;/strong&gt; An adapter sits on top of a base model, so the base model's license applies to what you serve. The walkthrough uses Mistral 7B Instruct v0.3, whose &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;model card&lt;/a&gt; lists the Apache-2.0 license (read 2026-10-27). If you use a small Llama or Gemma model instead, read the &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt; or the &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt; first, including what they say about derivative models. This guide gives no legal conclusions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What your adapter was trained on.&lt;/strong&gt; Use only data you have the right to use; CloudGPU's &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt; (section 1.8) prohibits training on datasets you do not have rights to use. If your adapter was trained on text generated through DeepSeek's API and you publish its outputs, DeepSeek's &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026) require you to label published outputs as AI-generated (section 3.1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudGPU's terms.&lt;/strong&gt; Read the &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;terms&lt;/a&gt; before you rent. Section 1 says compute comes from third-party data centres and supplier networks, which can change, so this guide does not claim your data stays on any particular machine.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A CloudGPU account with balance. GPU instances are billed per minute, rounded up, with a 1-minute minimum, and billing stops when you destroy the instance (&lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;terms&lt;/a&gt;). Rates are on the &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. Payment is by card or USDT (&lt;a href="https://cloudgpu.app/payments?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;payments&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A terminal with &lt;code&gt;ssh&lt;/code&gt;, &lt;code&gt;scp&lt;/code&gt; and Python 3 on your own machine.&lt;/li&gt;
&lt;li&gt;Optional: a LoRA adapter trained on Mistral 7B Instruct v0.3, stored in the usual Hugging Face PEFT layout (a folder with &lt;code&gt;adapter_config.json&lt;/code&gt; and &lt;code&gt;adapter_model.safetensors&lt;/code&gt;), for example the one from the self-critique guide in this series.  Without an adapter, do everything below and skip the adapter parts; you will serve the base model only.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;ssh -V&lt;/code&gt; prints a version, and &lt;code&gt;python3 --version&lt;/code&gt; prints a version. If you have an adapter, its folder contains the two files above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: make your endpoint key and look at your adapter (on your own machine)
&lt;/h2&gt;

&lt;p&gt;The key is a long random string that only you and the server know. Generate one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import secrets; print(secrets.token_urlsafe(32))"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep it in a password manager. Make a new one for every instance; never reuse a key from another service.&lt;/p&gt;

&lt;p&gt;If you have an adapter, open &lt;code&gt;adapter_config.json&lt;/code&gt; in its folder and note two fields: &lt;code&gt;base_model_name_or_path&lt;/code&gt; and &lt;code&gt;r&lt;/code&gt; (the LoRA rank).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the command printed a long random string that you have stored somewhere other than a shell history you share. For an adapter, &lt;code&gt;base_model_name_or_path&lt;/code&gt; is &lt;code&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/code&gt; (if not, the adapter belongs to another base model and this guide's command will not match it), and &lt;code&gt;r&lt;/code&gt; is a number you can compare with the rank limit in Step 4.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: rent a raw GPU
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://cloudgpu.app/deploy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;, choose Raw GPU and a card. The &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt; say that every template is a ready-made image with root SSH and that for anything a template does not cover you rent a raw GPU (Deploy â†’ Raw GPU) and install what you need over SSH. The docs' GPU table lists 16 GB as the minimum VRAM for models from 0.5B to 8B parameters; vLLM also needs Linux and an NVIDIA GPU with compute capability 7.5 or higher, according to its &lt;a href="https://docs.vllm.ai/en/latest/getting_started/installation/gpu/" rel="noopener noreferrer"&gt;installation page&lt;/a&gt;. A 7B model in 16-bit needs memory for the weights plus a cache for requests, so a card with more than the minimum is the easy fix if the server reports it cannot fit.&lt;/p&gt;

&lt;p&gt;Connect with the SSH details of your running instance. If you have an adapter, upload its folder from your own machine (&lt;code&gt;scp&lt;/code&gt; takes the port with a capital &lt;code&gt;-P&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; ./adapter-selfloop &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;ssh -p &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;&lt;/code&gt; connects, &lt;code&gt;nvidia-smi&lt;/code&gt; lists a GPU, and (with an adapter) &lt;code&gt;ls /workspace/adapter-selfloop&lt;/code&gt; shows &lt;code&gt;adapter_config.json&lt;/code&gt; and &lt;code&gt;adapter_model.safetensors&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: install vLLM in a fresh environment
&lt;/h2&gt;

&lt;p&gt;The vLLM &lt;a href="https://docs.vllm.ai/en/latest/getting_started/installation/gpu/" rel="noopener noreferrer"&gt;installation page&lt;/a&gt; recommends &lt;code&gt;uv&lt;/code&gt; for the Python environment. &lt;code&gt;uv&lt;/code&gt; can be installed from PyPI (see the &lt;a href="https://docs.astral.sh/uv/getting-started/installation/" rel="noopener noreferrer"&gt;uv installation page&lt;/a&gt;). On the instance:&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;uv
&lt;span class="nb"&gt;cd&lt;/span&gt; /workspace
uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12 &lt;span class="nt"&gt;--seed&lt;/span&gt; &lt;span class="nt"&gt;--managed-python&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
uv pip &lt;span class="nb"&gt;install &lt;/span&gt;vllm &lt;span class="nt"&gt;--torch-backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;auto
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These lines follow the vLLM page as read on 2026-10-27; if the page now shows different commands, use its commands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; with the environment active, &lt;code&gt;vllm --version&lt;/code&gt; prints a version, and &lt;code&gt;python3 -c "import torch; print(torch.cuda.is_available())"&lt;/code&gt; prints &lt;code&gt;True&lt;/code&gt;. If the install stops with an error, read the error text and the troubleshooting parts of the vLLM page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: start the server on localhost, with your key
&lt;/h2&gt;

&lt;p&gt;Use your own key in place of &lt;code&gt;&amp;lt;your-endpoint-key&amp;gt;&lt;/code&gt;. With an adapter:&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;cd&lt;/span&gt; /workspace &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
&lt;span class="nb"&gt;nohup &lt;/span&gt;vllm serve mistralai/Mistral-7B-Instruct-v0.3 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--host&lt;/span&gt; 127.0.0.1 &lt;span class="nt"&gt;--port&lt;/span&gt; 8000 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--api-key&lt;/span&gt; &amp;lt;your-endpoint-key&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tokenizer-mode&lt;/span&gt; hf &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-lora&lt;/span&gt; &lt;span class="nt"&gt;--max-lora-rank&lt;/span&gt; 16 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lora-modules&lt;/span&gt; &lt;span class="nv"&gt;selfloop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/workspace/adapter-selfloop &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /workspace/vllm.log 2&amp;gt;&amp;amp;1 &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without an adapter, remove the &lt;code&gt;--enable-lora&lt;/code&gt;, &lt;code&gt;--max-lora-rank&lt;/code&gt; and &lt;code&gt;--lora-modules&lt;/code&gt; lines.&lt;/p&gt;

&lt;p&gt;What the flags do, from the vLLM &lt;a href="https://docs.vllm.ai/en/latest/cli/serve/" rel="noopener noreferrer"&gt;serve reference&lt;/a&gt; and &lt;a href="https://docs.vllm.ai/en/latest/features/lora/" rel="noopener noreferrer"&gt;LoRA page&lt;/a&gt;: &lt;code&gt;--host 127.0.0.1&lt;/code&gt; keeps the HTTP server on the instance's localhost; &lt;code&gt;--api-key&lt;/code&gt; makes requests under &lt;code&gt;/v1&lt;/code&gt; carry that key; &lt;code&gt;--tokenizer-mode hf&lt;/code&gt; uses the Hugging Face tokenizer instead of letting &lt;code&gt;auto&lt;/code&gt; pick the &lt;code&gt;mistral_common&lt;/code&gt; tokenizer for Mistral models; pinning it is meant to match how an adapter was trained with the Hugging Face tokenizer, so drop the flag if your adapter was trained another way; &lt;code&gt;--lora-modules name=path&lt;/code&gt; registers the adapter under the name &lt;code&gt;selfloop&lt;/code&gt;; &lt;code&gt;--max-lora-rank&lt;/code&gt; must be at least the adapter's &lt;code&gt;r&lt;/code&gt; and the docs list the allowed values (the default is 16), so if your &lt;code&gt;r&lt;/code&gt; from Step 1 is larger than 16, use the next allowed value. The key is visible in the instance's process list while the server runs, which is one more reason to use a fresh key per instance.&lt;/p&gt;

&lt;p&gt;Wait until the log says the server is ready before you send a request. If the process exits, read the end of the log with &lt;code&gt;tail -n 50 /workspace/vllm.log&lt;/code&gt;. A message about memory for the cache is fixed by adding &lt;code&gt;--max-model-len 8192&lt;/code&gt; or by choosing a card with more memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; on the instance, &lt;code&gt;curl -s -i http://127.0.0.1:8000/v1/models | head -n 1&lt;/code&gt; prints an HTTP status line that is not a success (an authentication error is expected, because you sent no key), and &lt;code&gt;curl -s -H "Authorization: Bearer &amp;lt;your-endpoint-key&amp;gt;" http://127.0.0.1:8000/v1/models&lt;/code&gt; prints JSON that lists the base model and, with an adapter, &lt;code&gt;selfloop&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: open the tunnel and call your endpoint (on your own machine)
&lt;/h2&gt;

&lt;p&gt;In one terminal, open the tunnel. Nothing is printed while it runs; leave it open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="nt"&gt;-L&lt;/span&gt; 8000:127.0.0.1:8000 &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If local port 8000 is already used, pick another local port (for example &lt;code&gt;-L 8001:127.0.0.1:8000&lt;/code&gt;) and use it in the URLs below. In a second terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://127.0.0.1:8000/v1/models &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &amp;lt;your-endpoint-key&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the same request through the OpenAI SDK (&lt;code&gt;pip install openai&lt;/code&gt; on your own machine). Put this in a file named &lt;code&gt;ask.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;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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;http://127.0.0.1:8000/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_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;ENDPOINT_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;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;ADAPTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;selfloop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# delete this name from the loop below if you started without an adapter
&lt;/span&gt;
&lt;span class="n"&gt;question&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Explain what a LoRA adapter is in two sentences.&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;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ADAPTER&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;reply&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&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;role&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;user&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="n"&gt;question&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
        &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&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;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&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;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it with the key in an environment variable, not in the file:&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;ENDPOINT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your-endpoint-key&amp;gt;"&lt;/span&gt;
python3 ask.py &lt;span class="s2"&gt;"Explain what a LoRA adapter is in two sentences."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any tool that lets you set an OpenAI-compatible base URL and a key can use the same two values (&lt;code&gt;http://127.0.0.1:8000/v1&lt;/code&gt; and your key) while the tunnel is open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the &lt;code&gt;curl&lt;/code&gt; call lists the model names you expect; &lt;code&gt;ask.py&lt;/code&gt; prints one answer per model name; and a call with a wrong or missing key is refused rather than answered. If the tunnel command prints "address already in use", use another local port. Reading two answers is not a test of your adapter; for that, use questions the adapter never saw and compare them yourself, as in the self-critique guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: stop, copy out what you need, destroy
&lt;/h2&gt;

&lt;p&gt;Close the tunnel with &lt;code&gt;Ctrl+C&lt;/code&gt; in its terminal. Before you destroy anything, copy what you want to keep. The docs have a section called "What to save before you destroy an instance"; read it in the &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt;. For example, copy the server log from your own machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/vllm.log ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then destroy the instance from your Dashboard (the docs: "You can destroy an instance at any time"). Minutes spent copying are billed like any other minute, and billing stops once the instance is destroyed. The endpoint key was meant for this instance only; do not reuse it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the instance no longer appears as running in your Dashboard, and the &lt;code&gt;curl&lt;/code&gt; call from Step 5 now fails to connect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and access, in plain terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This is your own endpoint, with your own API key, reached through your own SSH tunnel. It is not a public URL.&lt;/li&gt;
&lt;li&gt;Per CloudGPU's &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;terms&lt;/a&gt; (section 5), you keep ownership of any data, models, code or output you create on or upload to an instance, and CloudGPU says it does not access, inspect, copy or back up the contents of your instance except in three listed cases: a credible report of a violation of section 3, valid legal process, or to protect the safety of users or third parties.&lt;/li&gt;
&lt;li&gt;Per the same terms, destroying an instance permanently deletes its local storage, and you are responsible for backing up what you want to keep.&lt;/li&gt;
&lt;li&gt;Whatever you send to the endpoint is processed on that rented instance. Do not send secrets or personal data you would not put on a rented machine.&lt;/li&gt;
&lt;li&gt;If you call a model through CloudGPU's AI API instead of your own instance, the request is forwarded to the model provider and its terms apply; see the &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All read on 2026-10-27 (UTC+8).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vLLM: &lt;a href="https://docs.vllm.ai/en/latest/getting_started/installation/gpu/" rel="noopener noreferrer"&gt;installation (NVIDIA CUDA)&lt;/a&gt;, &lt;a href="https://docs.vllm.ai/en/latest/features/lora/" rel="noopener noreferrer"&gt;serving LoRA adapters&lt;/a&gt;, &lt;a href="https://docs.vllm.ai/en/latest/cli/serve/" rel="noopener noreferrer"&gt;serve command reference&lt;/a&gt;, &lt;a href="https://docs.vllm.ai/en/latest/usage/security/" rel="noopener noreferrer"&gt;security guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;uv: &lt;a href="https://docs.astral.sh/uv/getting-started/installation/" rel="noopener noreferrer"&gt;installation&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Licenses: &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;Mistral 7B Instruct v0.3 model card&lt;/a&gt;, &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;DeepSeek: &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026).&lt;/li&gt;
&lt;li&gt;CloudGPU: &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;terms&lt;/a&gt;, &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt;, &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt;, &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t4_own_endpoint_vllm" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
    <item>
      <title>Let a small model critique and revise its own answers, then fine-tune on the revisions (on a rented GPU)</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Fri, 30 Oct 2026 03:10:23 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/let-a-small-model-critique-and-revise-its-own-answers-then-fine-tune-on-the-revisions-on-a-rented-1p11</link>
      <guid>https://dev.to/cloudgpu_app/let-a-small-model-critique-and-revise-its-own-answers-then-fine-tune-on-the-revisions-on-a-rented-1p11</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; write a short list of principles and a set of questions, have a small model answer them, critique its own answers against your principles and revise them, filter the revisions with a script and read a sample yourself, train a LoRA adapter on the result with LLaMA-Factory, then compare the base model and the adapter on questions you held back. Everything runs on one rented GPU; nothing is sent to a third-party API, and the guide starts no server.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU, and the GPU rental in this guide is ours. Each step links the documentation it follows and ends with a "What to check" line, because versions, template names and model access change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the idea comes from, and what this guide does not claim
&lt;/h2&gt;

&lt;p&gt;Anthropic's Constitutional AI work (2022-12-15) describes a supervised phase in which the model samples answers, critiques them against a list of principles, revises them, and is then fine-tuned on the revisions: &lt;a href="https://www.anthropic.com/research/constitutional-ai-harmlessness-from-ai-feedback" rel="noopener noreferrer"&gt;research page&lt;/a&gt;, &lt;a href="https://arxiv.org/abs/2212.08073" rel="noopener noreferrer"&gt;paper&lt;/a&gt;. OpenAI's deliberative alignment post (2024-12-20) says its training data is generated automatically from written specifications, without human-labeled completions: &lt;a href="https://openai.com/index/deliberative-alignment/" rel="noopener noreferrer"&gt;post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This guide is a small, single-developer version of that pattern. It is our own adaptation, not a result from those sources, and we make no claim that the adapter you train will be better than the base model. A model's critique of its own answer can be wrong, and a revision can be worse than the original. That is why the last step is a comparison you read yourself, with a rule for when to throw the round away.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read these things first
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Your base model's license.&lt;/strong&gt; You will train on text a base model wrote, so the license of that model matters. The walkthrough uses Mistral 7B Instruct v0.3, whose &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;model card&lt;/a&gt; lists the Apache-2.0 license (read 2026-10-27). Two other small models have conditions that touch this exact workflow. The &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt; says that if you use the Llama Materials "or any outputs or results of the Llama Materials to create, train, fine tune, or otherwise improve an AI model, which is distributed or made available", the model name must begin with "Llama". The &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt; define Model Derivatives to include models created by "methods based on the generation of synthetic data Outputs by Gemma for training that model". Read the current text of the license of whichever model you use; this is an excerpt, not legal advice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your data.&lt;/strong&gt; The questions you write must be ones you have the right to use. CloudGPU's &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt; (section 1.8) prohibits training on datasets you do not have rights to use. Also read the &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;terms&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Labelling.&lt;/strong&gt; This guide generates data with your own model, not with an API. If you would rather use an API as the generator, the guide on generating a training set with DeepSeek, from this same series, uses DeepSeek only. DeepSeek's &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026) say in section 3.1 that if you publish or disseminate outputs, you must clearly indicate that the content is generated by artificial intelligence. If you publish any dataset or example from this guide, label it AI-generated as well.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A CloudGPU account with balance. GPU instances are billed per minute, rounded up, with a 1-minute minimum, and billing stops when you destroy the instance (&lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;terms&lt;/a&gt;). Rates are on the &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;. Payment is by card or USDT (&lt;a href="https://cloudgpu.app/payments?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;payments&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;A text editor and a terminal on your own machine. You write three small files there before you rent anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; you can open a terminal and run &lt;code&gt;ssh -V&lt;/code&gt; and &lt;code&gt;scp&lt;/code&gt; (both print a usage line or version).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: write the principles and the questions (on your own machine)
&lt;/h2&gt;

&lt;p&gt;Create three plain-text files in one folder.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;principles.txt&lt;/code&gt; is a short list, in your own words, of what a good answer looks like for your use case. An example to replace with yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Answer only what the question asks.
2. If the answer depends on information you were not given, say what is missing instead of guessing.
3. Do not invent names, numbers, links or quotes.
4. Keep the answer under 150 words unless the question asks for more.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;prompts.txt&lt;/code&gt; is the training questions, one per line, as many as you can write (a few hundred is a reasonable start; the more varied they are, the more the loop has to work with). &lt;code&gt;heldout.txt&lt;/code&gt; is 20 or so questions in the same style that you will &lt;strong&gt;not&lt;/strong&gt; use for training. Write them separately, and do not copy lines between the files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;wc -l prompts.txt heldout.txt&lt;/code&gt; shows both files non-empty, and &lt;code&gt;comm -12 &amp;lt;(sort prompts.txt) &amp;lt;(sort heldout.txt)&lt;/code&gt; prints nothing (no question appears in both).&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: rent a GPU
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://cloudgpu.app/deploy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;, choose a card. The &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt; say two things that matter here: every template is an image with root SSH, and for anything a template does not cover you can rent a raw GPU (Deploy → Raw GPU) and install what you need over SSH. For this guide, pick a template that includes PyTorch, or choose Raw GPU and install it yourself. A 7B model in 16-bit needs a card with enough memory for the weights plus the generation cache; &lt;code&gt;nvidia-smi&lt;/code&gt; shows real use, and a smaller model or a card with more memory is the fix for an out-of-memory error.&lt;/p&gt;

&lt;p&gt;Connect with the SSH details of your running instance. Then upload your three files (&lt;code&gt;scp&lt;/code&gt; takes the port with a capital &lt;code&gt;-P&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; principles.txt prompts.txt heldout.txt &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; connect with &lt;code&gt;ssh -p &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;&lt;/code&gt;, then &lt;code&gt;ls /workspace&lt;/code&gt; lists your three files, &lt;code&gt;nvidia-smi&lt;/code&gt; lists a GPU, and &lt;code&gt;python3 -c "import torch; print(torch.cuda.is_available())"&lt;/code&gt; prints &lt;code&gt;True&lt;/code&gt;. If torch is missing, install PyTorch with CUDA from the &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch install page&lt;/a&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: install LLaMA-Factory
&lt;/h2&gt;

&lt;p&gt;These are the install commands from the &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;LLaMA-Factory README&lt;/a&gt; (read 2026-10-27; the repository name is spelled &lt;code&gt;LlamaFactory&lt;/code&gt; there). The repository's &lt;code&gt;pyproject.toml&lt;/code&gt; lists &lt;code&gt;torch&lt;/code&gt;, &lt;code&gt;transformers&lt;/code&gt;, &lt;code&gt;accelerate&lt;/code&gt; and &lt;code&gt;peft&lt;/code&gt; as dependencies, which is all the scripts below use. The docs FAQ lists a LLaMA-Factory template, but don't count on it being installed or current: check, and install if needed.&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;cd&lt;/span&gt; /workspace
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/hiyouga/LlamaFactory.git
&lt;span class="nb"&gt;cd &lt;/span&gt;LlamaFactory
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements/metrics.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;llamafactory-cli help&lt;/code&gt; prints the command list, and &lt;code&gt;python3 -c "import transformers, peft; print(transformers.__version__, peft.__version__)"&lt;/code&gt; prints two versions without an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: generate, critique, revise
&lt;/h2&gt;

&lt;p&gt;Two files. &lt;code&gt;common.py&lt;/code&gt; loads the base model and answers a list of prompts in batches (the &lt;a href="https://huggingface.co/docs/transformers/en/llm_tutorial" rel="noopener noreferrer"&gt;transformers docs&lt;/a&gt; say to pad on the left for batched generation, and the &lt;a href="https://huggingface.co/docs/transformers/en/chat_templating" rel="noopener noreferrer"&gt;chat templating docs&lt;/a&gt; describe &lt;code&gt;apply_chat_template&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="c1"&gt;# /workspace/common.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&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;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;tok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&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="n"&gt;padding_side&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;left&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;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token&lt;/span&gt; &lt;span class="ow"&gt;is&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;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;eos_token&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;AutoModelForCausalLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&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="n"&gt;torch_dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bfloat16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;device_map&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&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="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;tok&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;def&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&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="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample&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;batch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;outs&lt;/span&gt; &lt;span class="o"&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;i&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;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;texts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&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;role&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;user&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="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
                &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;add_generation_prompt&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="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;batch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;texts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;padding&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;add_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&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="n"&gt;device&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;kw&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;do_sample&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;top_p&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.9&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;sample&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;do_sample&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;gen&lt;/span&gt; &lt;span class="o"&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;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pad_token_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pad_token_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&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;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;gen&lt;/span&gt;&lt;span class="p"&gt;[:,&lt;/span&gt; &lt;span class="n"&gt;enc&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_ids&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;shape&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="n"&gt;outs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skip_special_tokens&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="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;outs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;loop.py&lt;/code&gt; runs the three passes: answer, critique against your principles, revise. Principles go inside the user message so the same scripts work with chat templates that have no system role.&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="c1"&gt;# /workspace/loop.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;

&lt;span class="n"&gt;NONE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NONE&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;principles&lt;/span&gt; &lt;span class="o"&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;/workspace/principles.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;prompts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;l&lt;/span&gt; &lt;span class="ow"&gt;in&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;/workspace/prompts.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

&lt;span class="n"&gt;tok&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="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&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="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;critique_in&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;Principles:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;principles&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\n&lt;/span&gt;&lt;span class="s"&gt;Question:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;p&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\n&lt;/span&gt;&lt;span class="s"&gt;Answer:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;a&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\n&lt;/span&gt;&lt;span class="s"&gt;In at most three short bullet points, say where the answer breaks a principle. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If it breaks none, reply with exactly &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;NONE&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&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;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;critiques&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&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="n"&gt;critique_in&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;todo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;enumerate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;critiques&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;NONE&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;revise_in&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;Principles:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;principles&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\n&lt;/span&gt;&lt;span class="s"&gt;Question:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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\n&lt;/span&gt;&lt;span class="s"&gt;Answer:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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\n&lt;/span&gt;&lt;span class="s"&gt;Problems found:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;critiques&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&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\n&lt;/span&gt;&lt;span class="s"&gt;Rewrite the answer so that it follows the principles and fixes these problems. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Reply with the rewritten answer only.&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;revised&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&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="n"&gt;revise_in&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;revised_by_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;revised&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="k"&gt;with&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;/workspace/loop_raw.jsonl&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;f&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;i&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="n"&gt;rec&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;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;prompts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critique&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;critiques&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revised&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;revised_by_index&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;i&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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;rec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;with&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;/workspace/loop_revised.jsonl&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;f&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;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;rec&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;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;prompts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;answers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;critique&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;critiques&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
               &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revised&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;revised_by_index&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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;rec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompts:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revised rows:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;todo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run it from &lt;code&gt;/workspace&lt;/code&gt;:&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;cd&lt;/span&gt; /workspace
python3 loop.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your transformers version prints a warning that &lt;code&gt;torch_dtype&lt;/code&gt; is deprecated, replace it with &lt;code&gt;dtype&lt;/code&gt; in &lt;code&gt;common.py&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the script ends by printing the number of prompts and revised rows. Open &lt;code&gt;loop_revised.jsonl&lt;/code&gt; and read five rows yourself: does the critique point at a real problem, and is the revised answer actually better than the first one? If critiques are vague or revisions are not better, stop here and change your principles or your prompts before you go on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: filter and read a sample
&lt;/h2&gt;

&lt;p&gt;The loop writes everything the model said, including its mistakes. Filter before you train. This script drops rows where the revision is empty, identical to the first answer, very long, repeated, or contains words that show the model talked about the task instead of answering:&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="c1"&gt;# /workspace/filter.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;LEAK&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;principles&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;critique&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;rewritten&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;revised answer&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;problems found&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nf"&gt;set&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="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;/workspace/loop_revised.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&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="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revised&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="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2500&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rev&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;low&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;LEAK&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;low&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&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;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;r&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;input&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="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;rev&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;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&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;/workspace/selfloop_sft.json&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&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="nf"&gt;seed&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;with&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;/workspace/sample_to_read.md&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;f&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;r&lt;/span&gt; &lt;span class="ow"&gt;in&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;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;))):&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;r&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="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kept:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 filter.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then read &lt;code&gt;sample_to_read.md&lt;/code&gt; yourself. Edit &lt;code&gt;selfloop_sft.json&lt;/code&gt; by hand and remove any row you would not want the model to learn from, or tighten the filter and run it again. If you publish the filtered file or examples from it, label them as AI-generated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the script prints how many rows it kept. If that number is very small, the loop is not producing usable revisions for your prompts; go back to Step 1. If you read the sample and many rows are wrong or off-topic, do not train on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: register the data and train a LoRA adapter
&lt;/h2&gt;

&lt;p&gt;Copy the dataset into LLaMA-Factory's &lt;code&gt;data/&lt;/code&gt; folder and register it in &lt;code&gt;data/dataset_info.json&lt;/code&gt;, which is how LLaMA-Factory finds custom datasets:&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;cd&lt;/span&gt; /workspace/LlamaFactory
&lt;span class="nb"&gt;cp&lt;/span&gt; /workspace/selfloop_sft.json data/
python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import json
p = "data/dataset_info.json"
info = json.load(open(p, encoding="utf-8"))
info["selfloop_sft"] = {
    "file_name": "selfloop_sft.json",
    "columns": {"prompt": "instruction", "query": "input", "response": "output"},
}
json.dump(info, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this as &lt;code&gt;selfloop_lora.yaml&lt;/code&gt; in the &lt;code&gt;LlamaFactory&lt;/code&gt; folder. The keys follow the official LoRA example in the repo's &lt;code&gt;examples/train_lora/&lt;/code&gt; folder; the model, template and dataset lines are the parts you change (the &lt;code&gt;mistral&lt;/code&gt; template name is from the README's model table):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;### model&lt;/span&gt;
&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/span&gt;

&lt;span class="c1"&gt;### method&lt;/span&gt;
&lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sft&lt;/span&gt;
&lt;span class="na"&gt;do_train&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;finetuning_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lora&lt;/span&gt;
&lt;span class="na"&gt;lora_rank&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;lora_target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;

&lt;span class="c1"&gt;### dataset&lt;/span&gt;
&lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selfloop_sft&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mistral&lt;/span&gt;
&lt;span class="na"&gt;cutoff_len&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;
&lt;span class="na"&gt;preprocessing_num_workers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="c1"&gt;### output&lt;/span&gt;
&lt;span class="na"&gt;output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saves/mistral7b/lora/selfloop&lt;/span&gt;
&lt;span class="na"&gt;logging_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;save_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="na"&gt;plot_loss&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;overwrite_output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;### train&lt;/span&gt;
&lt;span class="na"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;learning_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0e-4&lt;/span&gt;
&lt;span class="na"&gt;num_train_epochs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;
&lt;span class="na"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cosine&lt;/span&gt;
&lt;span class="na"&gt;warmup_ratio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;bf16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;### eval&lt;/span&gt;
&lt;span class="na"&gt;val_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;per_device_eval_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;eval_strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps&lt;/span&gt;
&lt;span class="na"&gt;eval_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llamafactory-cli train selfloop_lora.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compare your YAML with the current LoRA example in the &lt;a href="https://github.com/hiyouga/LlamaFactory/tree/main/examples" rel="noopener noreferrer"&gt;LLaMA-Factory examples folder&lt;/a&gt;: key names change between releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the log prints a training loss every 10 steps and an evaluation loss every 50; &lt;code&gt;saves/mistral7b/lora/selfloop&lt;/code&gt; contains &lt;code&gt;adapter_model.safetensors&lt;/code&gt; and &lt;code&gt;adapter_config.json&lt;/code&gt;. Make sure no earlier script is still holding the GPU (&lt;code&gt;nvidia-smi&lt;/code&gt; shows what holds memory). If you choose another base model, change &lt;code&gt;MODEL&lt;/code&gt; in &lt;code&gt;common.py&lt;/code&gt;, &lt;code&gt;model_name_or_path&lt;/code&gt;, and &lt;code&gt;template&lt;/code&gt; together, and set a Hugging Face token for a gated model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 7: compare on questions the model never saw
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;compare.py&lt;/code&gt; answers your held-out questions with the base model and with the adapter, greedily (no sampling, so the two columns are comparable), and writes both next to each other:&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="c1"&gt;# /workspace/compare.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;peft&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;common&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chat&lt;/span&gt;

&lt;span class="n"&gt;ADAPTER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/workspace/LlamaFactory/saves/mistral7b/lora/selfloop&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;qs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;l&lt;/span&gt; &lt;span class="ow"&gt;in&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;/workspace/heldout.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;

&lt;span class="n"&gt;tok&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="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&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="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tuned_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PeftModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&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="n"&gt;ADAPTER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;tuned_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;tuned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tok&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tuned_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&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;/workspace/compare.md&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;f&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;q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tuned&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;## &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;q&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\n&lt;/span&gt;&lt;span class="s"&gt;**Base:**&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&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\n&lt;/span&gt;&lt;span class="s"&gt;**Adapter:**&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;t&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\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;questions:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qs&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /workspace
python3 compare.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read &lt;code&gt;compare.md&lt;/code&gt; against your principles, question by question. Decide for yourself what "better" means, and write it down before you read. &lt;strong&gt;Stop rule:&lt;/strong&gt; if the adapter is not clearly better on your principles, do not keep it. Change the principles, the prompts or the filter and run the loop again; each round uses more GPU minutes, so decide in advance when to stop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the script prints the number of questions and &lt;code&gt;compare.md&lt;/code&gt; exists with two answers per question. Remember that 20 questions is a small test, and that you wrote the principles that decide the outcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 8: copy the adapter off and destroy the instance
&lt;/h2&gt;

&lt;p&gt;Before you destroy anything, copy what you want to keep. The docs have a section called "What to save before you destroy an instance"; read it in the &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt;. From your own machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &amp;lt;ssh-port&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/LlamaFactory/saves/mistral7b/lora/selfloop/ ./adapter-selfloop/
scp &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/compare.md ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that &lt;code&gt;adapter_model.safetensors&lt;/code&gt; is in &lt;code&gt;./adapter-selfloop/&lt;/code&gt;, then destroy the instance from your Dashboard (the docs: "You can destroy an instance at any time"). The minutes spent copying are billed like any other minute, and billing stops once the instance is destroyed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the instance no longer appears as running in your Dashboard, and &lt;code&gt;./adapter-selfloop/&lt;/code&gt; opens on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and access, in plain terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The guide generates and trains on the instance itself and does not call an API, so your questions and the model's answers stay on that instance and on your machine. No server is started, so nothing listens for outside requests. If you later serve the model, put your own API key in front of it before you expose it.&lt;/li&gt;
&lt;li&gt;Per CloudGPU's &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;terms&lt;/a&gt;, destroying an instance deletes its local storage; copy what you need first.&lt;/li&gt;
&lt;li&gt;If you used a Hugging Face token for a gated base model, revoke it when you are done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All read on 2026-10-27 (UTC+8).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anthropic, 2022-12-15: &lt;a href="https://www.anthropic.com/research/constitutional-ai-harmlessness-from-ai-feedback" rel="noopener noreferrer"&gt;Constitutional AI: Harmlessness from AI Feedback&lt;/a&gt; and the &lt;a href="https://arxiv.org/abs/2212.08073" rel="noopener noreferrer"&gt;paper&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;OpenAI, 2024-12-20: &lt;a href="https://openai.com/index/deliberative-alignment/" rel="noopener noreferrer"&gt;Deliberative alignment&lt;/a&gt; and the &lt;a href="https://arxiv.org/abs/2412.16339" rel="noopener noreferrer"&gt;paper&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Licenses: &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;Mistral 7B Instruct v0.3 model card&lt;/a&gt;, &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;DeepSeek: &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026).&lt;/li&gt;
&lt;li&gt;Tools: &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;LLaMA-Factory&lt;/a&gt;, &lt;a href="https://huggingface.co/docs/transformers/en/llm_tutorial" rel="noopener noreferrer"&gt;transformers text generation tutorial&lt;/a&gt;, &lt;a href="https://huggingface.co/docs/transformers/en/chat_templating" rel="noopener noreferrer"&gt;chat templating&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;CloudGPU: &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;terms&lt;/a&gt;, &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt;, &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;docs&lt;/a&gt;, &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t8_self_critique_loop" rel="noopener noreferrer"&gt;pricing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Run Ollama on a per-minute GPU, then point your app at an OpenAI-compatible API</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Thu, 29 Oct 2026 09:13:30 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/run-ollama-on-a-per-minute-gpu-then-point-your-app-at-an-openai-compatible-api-497l</link>
      <guid>https://dev.to/cloudgpu_app/run-ollama-on-a-per-minute-gpu-then-point-your-app-at-an-openai-compatible-api-497l</guid>
      <description>&lt;p&gt;Your laptop can't hold the model you want, and you don't want to spend an afternoon on CUDA and port forwarding. This walkthrough rents a GPU billed per minute, runs Ollama on it with a public HTTPS endpoint, and calls that endpoint with curl and the OpenAI Python client.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://cloudgpu.app/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;CloudGPU account&lt;/a&gt; (email or Google sign-up) with a prepaid balance you can top up by card or USDT. Rates are listed on the &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Python with &lt;code&gt;pip install openai&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Deploy the Ollama template
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://cloudgpu.app/deploy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;, choose the Ollama template, select a GPU card with enough VRAM for the model you plan to run, and click Deploy. The Deploy page shows the cards you can pick. To size a model, see our &lt;a href="https://cloudgpu.app/guides/how-to-choose-a-rental-gpu-for-ollama?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;Ollama GPU guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Find the API endpoint
&lt;/h2&gt;

&lt;p&gt;The docs say: "Every running Ollama instance gives you an OpenAI-compatible endpoint. Open the instance in your Dashboard and click the API button." They also say "Replace  with the id shown on your instance row." The endpoint looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://&amp;lt;random-id&amp;gt;-i.cloudgpu.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for the current wording. The URL works only while the instance runs. The terms say service URLs are "not authenticated by default" and that you "should treat them as a credential and not share them publicly", so keep it private. If you need authentication, configure it inside your instance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Pull a model
&lt;/h2&gt;

&lt;p&gt;The docs describe a Pick Model button for pulling any model from the Ollama library after deploy. As an example, this guide uses &lt;code&gt;llama3.1:8b&lt;/code&gt; (&lt;a href="https://ollama.com/library/llama3.1:8b" rel="noopener noreferrer"&gt;Llama 3.1 8B on ollama.com&lt;/a&gt;). Check it fits the VRAM of the card you picked. You can also run &lt;code&gt;ollama pull llama3.1:8b&lt;/code&gt; yourself if you connect over SSH.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Smoke-test it with curl
&lt;/h2&gt;

&lt;p&gt;List the models on the instance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://&amp;lt;random-id&amp;gt;-i.cloudgpu.app/api/tags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Send one chat turn (non-streaming). &lt;code&gt;keep_alive&lt;/code&gt; is the Ollama setting that controls how long a model stays loaded after the last call; the CloudGPU docs example uses &lt;code&gt;"30m"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://&amp;lt;random-id&amp;gt;-i.cloudgpu.app/api/chat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "llama3.1:8b",
    "stream": false,
    "keep_alive": "30m",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a model has been unloaded, Ollama has to load it again before it can answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Call it with the OpenAI Python client
&lt;/h2&gt;

&lt;p&gt;Ollama serves an OpenAI-compatible API under &lt;code&gt;/v1&lt;/code&gt; (see &lt;a href="https://docs.ollama.com/api/openai-compatibility" rel="noopener noreferrer"&gt;Ollama's OpenAI compatibility docs&lt;/a&gt;), so the &lt;code&gt;openai&lt;/code&gt; package works with a different &lt;code&gt;base_url&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;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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://&amp;lt;random-id&amp;gt;-i.cloudgpu.app/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ollama&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# any non-empty string, per the CloudGPU docs
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llama3.1:8b&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# a name from /api/tags
&lt;/span&gt;    &lt;span class="n"&gt;messages&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;role&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;user&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;Explain LoRA in one sentence.&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anything else that accepts an OpenAI-compatible base URL can use the same endpoint and model name.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want ComfyUI instead?
&lt;/h2&gt;

&lt;p&gt;The Deploy page also lists a ComfyUI template. The docs say templates that serve an API (Ollama and ComfyUI) get a public HTTPS URL, and that the ComfyUI template installs ComfyUI only: it does not include FLUX or other model weights, so you download the weights you need yourself once the instance is running. The same advice about keeping the URL private applies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Destroy the instance
&lt;/h2&gt;

&lt;p&gt;When you're done, destroy the instance. GPU time is billed per minute, rounded up, with a 1-minute minimum, and billing stops when you destroy the instance. The rules are in the &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;terms&lt;/a&gt;. Destroying an instance deletes everything stored on it, so copy out anything you need first. The terms also say an instance is destroyed automatically if your balance reaches zero.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model not found or wrong name.&lt;/strong&gt; Use a name exactly as &lt;code&gt;/api/tags&lt;/code&gt; returns it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The endpoint does not answer.&lt;/strong&gt; Check in your Dashboard that the instance is still running and that you copied the id from the instance row. A destroyed instance no longer serves its URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The first request after a model was unloaded fails or times out.&lt;/strong&gt; Ollama may still be loading it into VRAM; retry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No API button or endpoint on the instance.&lt;/strong&gt; Send the instance ID through the &lt;a href="https://cloudgpu.app/contact?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;contact page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CloudGPU is in public beta with no SLA.&lt;/p&gt;

&lt;p&gt;Prefer a hosted model to your own GPU? The OpenAI-compatible gateway is billed per token from the same prepaid balance and is documented in the &lt;a href="https://cloudgpu.app/api/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg05_localllm&amp;amp;utm_content=devto_ollama_gpu" rel="noopener noreferrer"&gt;API docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ollama</category>
      <category>llm</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Generate a training set with DeepSeek, then fine-tune a small model on a rented GPU</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Thu, 29 Oct 2026 05:43:23 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/generate-a-training-set-with-deepseek-then-fine-tune-a-small-model-on-a-rented-gpu-4gil</link>
      <guid>https://dev.to/cloudgpu_app/generate-a-training-set-with-deepseek-then-fine-tune-a-small-model-on-a-rented-gpu-4gil</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; write a few documentation pages yourself, have DeepSeek turn them into question-and-answer pairs through an OpenAI-compatible endpoint, filter the pairs with a short script, then train a LoRA adapter on a small base model with LLaMA-Factory on a rented GPU. Generate and filter on your own machine; rent the GPU only for training; download the adapter, then destroy the instance.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU. The endpoint (&lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;) and the GPU rentals below are ours. Each step links the official documentation it follows and ends with a "What to check" line, because the details (versions, template names, model access) change.&lt;/p&gt;

&lt;p&gt;Why this pattern? AI labs describe using models to write or judge training data from written rules. This is the small, single-developer version of that idea. It is our own analogy, not a claim from those papers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read these three things first
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek's terms.&lt;/strong&gt; This tutorial generates data with DeepSeek only. DeepSeek's &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;Terms of Use&lt;/a&gt; (Last Update: March 27, 2026) say in section 4.2(3) that you may apply the Outputs of the Services to "a wide range of use cases, including personal use, academic research, derivative product development, training other models (such as model distillation), etc., as long as such usage is legal and adhere to these Terms." Section 3.1 adds that if you publish or disseminate outputs, you must "clearly indicate that the output content is generated by artificial intelligence". So if you publish the dataset, a model card or examples from it, label them as AI-generated. Read the terms yourself, and read CloudGPU's &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;terms&lt;/a&gt; and &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt;, which apply to the gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rights to your source text.&lt;/strong&gt; The acceptable use policy lists "Training models on datasets you do not have rights to use" under large-scale copyright infringement (section 1.8). In this tutorial the generator only sees documentation you wrote yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The base model's license.&lt;/strong&gt; Each base model below has its own license. Open the link, read it, and confirm for yourself that your use is allowed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Python 3 and the &lt;code&gt;openai&lt;/code&gt; package on your own machine: &lt;code&gt;pip install openai&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A CloudGPU account with balance, and an API key (the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;API page&lt;/a&gt; has a "Get your API key" link). Keep it in an environment variable, never in code:
&lt;/li&gt;
&lt;/ul&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;CLOUDGPU_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"paste-your-key-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Three to ten Markdown files of your own documentation, with &lt;code&gt;##&lt;/code&gt; section headings, in a &lt;code&gt;docs/&lt;/code&gt; folder. A README of your own project works.&lt;/li&gt;
&lt;li&gt;A Hugging Face account and a read token if you choose Llama or Gemma (both are gated, see the table below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;python3 -c "import os; print(bool(os.environ.get('CLOUDGPU_API_KEY')))"&lt;/code&gt; prints &lt;code&gt;True&lt;/code&gt; in the shell where you will run the scripts, and &lt;code&gt;python3 -c "import openai"&lt;/code&gt; raises no error.&lt;/p&gt;

&lt;p&gt;API calls are billed per token from the same balance as GPU rentals; the current rate for every model is in the table on the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;API page&lt;/a&gt;. This post does not repeat rates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: generate question-and-answer pairs with DeepSeek
&lt;/h2&gt;

&lt;p&gt;The script splits each Markdown file at &lt;code&gt;##&lt;/code&gt; headings, asks &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt; for a handful of pairs per section, and writes one JSON object per line. The model ID comes from the live model table on the API page (checked 2026-10-27); the same table lists &lt;code&gt;deepseek-v4-flash&lt;/code&gt; and &lt;code&gt;deepseek-v4-pro&lt;/code&gt;. The OpenAI SDK only needs &lt;code&gt;base_url&lt;/code&gt; and the key changed.&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="c1"&gt;# gen_data.py
&lt;/span&gt;&lt;span class="kn"&gt;import&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;re&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&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;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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://cloudgpu.app/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_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;CLOUDGPU_API_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;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;deepseek-v4.1-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;PAIRS_PER_SECTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;

&lt;span class="n"&gt;PROMPT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You write training examples for a support assistant.
Use ONLY the documentation below. Do not invent features or settings.
Write {n} varied question-and-answer pairs that a user might ask about it.
Return ONLY a JSON array. Each item has the keys &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; (the question),
&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="s"&gt; (extra context, or an empty string) and &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="s"&gt; (the answer, 1-4 sentences).

Documentation:
{chunk}
&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder&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;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*.md&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;part&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&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="n"&gt;path&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="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;fence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;`&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="c1"&gt;# the model may wrap its JSON in a code fence
&lt;/span&gt;    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fence&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;removeprefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fence&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;removesuffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fence&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&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;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;keys&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;instruction&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;input&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;output&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;r&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;k&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;str&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;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;keys&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;ask&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="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;item&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;3&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="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;messages&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;role&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;user&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="n"&gt;PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;PAIRS_PER_SECTION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;)}],&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&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;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# network error, rate limit, bad JSON
&lt;/span&gt;            &lt;span class="nf"&gt;print&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;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: attempt &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;err&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="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;)&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="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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&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="mi"&gt;4&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;ask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sections&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="k"&gt;with&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;raw.jsonl&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;w&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&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;f&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;rows&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;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;write&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;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&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="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sections:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pairs:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&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;r&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;Run &lt;code&gt;python3 gen_data.py&lt;/code&gt;. The four-worker pool and the retry loop keep you inside the default rate limits listed on the API page; check those limits there and lower &lt;code&gt;max_workers&lt;/code&gt; if you see rate-limit errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;raw.jsonl&lt;/code&gt; exists and has lines; the script prints how many sections and pairs it got. If a section failed three times it is skipped and the error is on stderr. A &lt;code&gt;401&lt;/code&gt; means the key variable is not set in this shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: filter and de-duplicate
&lt;/h2&gt;

&lt;p&gt;Model-written data needs a filter before training. This script drops empty rows, answers that are very short or very long, boilerplate such as "as an AI", and near-duplicate questions (token overlap of 0.8 or more). Tune the numbers for your data.&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="c1"&gt;# filter_data.py
&lt;/span&gt;&lt;span class="kn"&gt;import&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;re&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;split&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;jaccard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;union&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;union&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;union&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;

&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&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;dropped&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;empty&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;length&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boilerplate&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duplicate&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;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;raw.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="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;row&lt;/span&gt; &lt;span class="o"&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="n"&gt;ins&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inp&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="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;strip&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;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&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;input&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;output&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="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ins&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;empty&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&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;len&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="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;length&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&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;\bas an ai\b|\blanguage model\b&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="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;I&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;boilerplate&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&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;jaccard&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins&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="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.8&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;duplicate&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&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;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ins&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;instruction&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ins&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="n"&gt;inp&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;out&lt;/span&gt;&lt;span class="p"&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;synth_docs_qa.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&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;kept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ensure_ascii&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;DATASET_NOTE.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;write_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;AI-generated data: the examples in synth_docs_qa.json were generated with deepseek-v4.1-flash &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;from documentation written by the dataset author. Review before use.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kept&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kept&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dropped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dropped&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is in LLaMA-Factory's Alpaca format: &lt;code&gt;instruction&lt;/code&gt;, optional &lt;code&gt;input&lt;/code&gt;, and &lt;code&gt;output&lt;/code&gt;, per the &lt;a href="https://llamafactory.readthedocs.io/en/latest/getting_started/data_preparation.html" rel="noopener noreferrer"&gt;data preparation docs&lt;/a&gt;. A row looks like this (illustrative, written by hand, not model output):&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;"instruction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"How do I change the output folder?"&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"&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="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pass --out followed by a path, for example --out ./build."&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;Then read a sample yourself. Filters catch format problems, not wrong answers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import json,random; [print(r['instruction'],'-&amp;gt;',r['output'],'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;') for r in random.sample(json.load(open('synth_docs_qa.json')), 10)]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;DATASET_NOTE.md&lt;/code&gt; is the label DeepSeek's terms ask for if you publish the data: keep it next to the file. Compare ten sampled answers with your documentation; delete rows that are wrong, or tighten the prompt and regenerate. If the filter drops most rows, the model is probably not returning clean JSON: print one raw response and adjust &lt;code&gt;parse&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your data is multi-turn, LLaMA-Factory also reads a conversation format; the same docs page shows the &lt;code&gt;dataset_info.json&lt;/code&gt; entry for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: rent a GPU
&lt;/h2&gt;

&lt;p&gt;On &lt;a href="https://cloudgpu.app/deploy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;Deploy&lt;/a&gt;, choose a card. The &lt;a href="https://cloudgpu.app/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;CloudGPU docs&lt;/a&gt; say two things that matter here: every template is an image with root SSH, and for anything a template does not cover you can rent a raw GPU (Deploy â†’ Raw GPU) and install what you need over SSH. For this tutorial, pick a template that includes PyTorch, or choose Raw GPU and install it yourself. Pick a card with enough memory for your base model: the LLaMA-Factory README has a rough memory table (marked as estimates), and &lt;code&gt;nvidia-smi&lt;/code&gt; shows real use during the first training steps.&lt;/p&gt;

&lt;p&gt;GPU instances are billed per minute, rounded up, with a 1-minute minimum, and billing stops when you destroy the instance (&lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;terms&lt;/a&gt;). Rates are on the &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;pricing page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Connect with the SSH details of your running instance. Then upload the dataset from your own machine (&lt;code&gt;scp&lt;/code&gt; takes the port with a capital &lt;code&gt;-P&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-P&lt;/span&gt; &amp;lt;ssh-port&amp;gt; synth_docs_qa.json &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; connect with &lt;code&gt;ssh -p &amp;lt;ssh-port&amp;gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;&lt;/code&gt;, then run &lt;code&gt;nvidia-smi&lt;/code&gt; (a GPU is listed) and &lt;code&gt;python3 -c "import torch; print(torch.cuda.is_available())"&lt;/code&gt; (prints &lt;code&gt;True&lt;/code&gt;). If torch is missing, install PyTorch with CUDA from the &lt;a href="https://pytorch.org/get-started/locally/" rel="noopener noreferrer"&gt;PyTorch install page&lt;/a&gt; first.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: install LLaMA-Factory and register the dataset
&lt;/h2&gt;

&lt;p&gt;These are the install commands from the &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;LLaMA-Factory README&lt;/a&gt; (read 2026-10-27; the repository name is spelled &lt;code&gt;LlamaFactory&lt;/code&gt; there). The docs FAQ lists a LLaMA-Factory template, but don't count on it being installed or current: check, and install if needed.&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;cd&lt;/span&gt; /workspace
git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 https://github.com/hiyouga/LlamaFactory.git
&lt;span class="nb"&gt;cd &lt;/span&gt;LlamaFactory
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements/metrics.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the dataset into &lt;code&gt;data/&lt;/code&gt; and add an entry to &lt;code&gt;data/dataset_info.json&lt;/code&gt;, which is how LLaMA-Factory finds custom datasets:&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;cp&lt;/span&gt; /workspace/synth_docs_qa.json data/
python3 - &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
import json
p = "data/dataset_info.json"
info = json.load(open(p, encoding="utf-8"))
info["synth_docs_qa"] = {
    "file_name": "synth_docs_qa.json",
    "columns": {"prompt": "instruction", "query": "input", "response": "output"},
}
json.dump(info, open(p, "w", encoding="utf-8"), ensure_ascii=False, indent=2)
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; &lt;code&gt;llamafactory-cli help&lt;/code&gt; prints the command list, and &lt;code&gt;grep synth_docs_qa data/dataset_info.json&lt;/code&gt; finds your entry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: choose a base model and train a LoRA adapter
&lt;/h2&gt;

&lt;p&gt;Small models are enough to see the pipeline work. Pick one row; the template name must match the model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base model&lt;/th&gt;
&lt;th&gt;LLaMA-Factory &lt;code&gt;template&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;License (read it yourself)&lt;/th&gt;
&lt;th&gt;Access&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Llama 3.2 3B Instruct (&lt;code&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;llama3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2 Community License&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Gated on Hugging Face: accept the license, use a token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gemma 3 4B IT (&lt;code&gt;google/gemma-3-4b-it&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;gemma3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma Terms of Use&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Gated on Hugging Face: accept the terms, use a token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mistral 7B Instruct v0.3 (&lt;code&gt;mistralai/Mistral-7B-Instruct-v0.3&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mistral&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;License shown on the &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;model card&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;The model card listed no gate when we looked (2026-10-27)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Template names are from the LLaMA-Factory README's model table; license and access information was read on 2026-10-27 and can change. The Llama license has conditions that apply if you distribute or make available a model built on it. Confirm the license for your use yourself before you train or share anything.&lt;/p&gt;

&lt;p&gt;For a gated model, request access on its Hugging Face page, then give the instance a fine-grained &lt;strong&gt;read&lt;/strong&gt; token for just this run (you will revoke it at the end):&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;HF_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"paste-a-read-token-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save this as &lt;code&gt;synth_lora.yaml&lt;/code&gt; in the &lt;code&gt;LlamaFactory&lt;/code&gt; folder. The keys follow the official LoRA example in the repo's &lt;code&gt;examples/train_lora/&lt;/code&gt; folder; the model, template and dataset lines are the parts you change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;### model&lt;/span&gt;
&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;

&lt;span class="c1"&gt;### method&lt;/span&gt;
&lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sft&lt;/span&gt;
&lt;span class="na"&gt;do_train&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;finetuning_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lora&lt;/span&gt;
&lt;span class="na"&gt;lora_rank&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;lora_target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;all&lt;/span&gt;

&lt;span class="c1"&gt;### dataset&lt;/span&gt;
&lt;span class="na"&gt;dataset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;synth_docs_qa&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;llama3&lt;/span&gt;
&lt;span class="na"&gt;cutoff_len&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2048&lt;/span&gt;
&lt;span class="na"&gt;preprocessing_num_workers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="c1"&gt;### output&lt;/span&gt;
&lt;span class="na"&gt;output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saves/llama32-3b/lora/synth&lt;/span&gt;
&lt;span class="na"&gt;logging_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;span class="na"&gt;save_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
&lt;span class="na"&gt;plot_loss&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;overwrite_output_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;### train&lt;/span&gt;
&lt;span class="na"&gt;per_device_train_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;gradient_accumulation_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8&lt;/span&gt;
&lt;span class="na"&gt;learning_rate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0e-4&lt;/span&gt;
&lt;span class="na"&gt;num_train_epochs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.0&lt;/span&gt;
&lt;span class="na"&gt;lr_scheduler_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cosine&lt;/span&gt;
&lt;span class="na"&gt;warmup_ratio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;bf16&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="c1"&gt;### eval&lt;/span&gt;
&lt;span class="na"&gt;val_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.1&lt;/span&gt;
&lt;span class="na"&gt;per_device_eval_batch_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;eval_strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;steps&lt;/span&gt;
&lt;span class="na"&gt;eval_steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Train:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llamafactory-cli train synth_lora.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the &lt;a href="https://github.com/hiyouga/LlamaFactory/tree/main/examples" rel="noopener noreferrer"&gt;LLaMA-Factory examples folder&lt;/a&gt; and compare your YAML with the current LoRA example: key names change between releases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the log prints a training loss every 10 steps and, with &lt;code&gt;val_size&lt;/code&gt; set, an evaluation loss every 50; &lt;code&gt;saves/llama32-3b/lora/synth&lt;/code&gt; contains &lt;code&gt;adapter_model.safetensors&lt;/code&gt; and &lt;code&gt;adapter_config.json&lt;/code&gt;. If the base model download fails with a 401 or 403, the Hugging Face access request or token is the problem. An out-of-memory error means a smaller &lt;code&gt;cutoff_len&lt;/code&gt;, a smaller model or a card with more memory; &lt;code&gt;nvidia-smi&lt;/code&gt; shows the real use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Unsloth (optional).&lt;/strong&gt; The LLaMA-Factory README lists Unsloth among its supported add-ons and describes a &lt;code&gt;use_unsloth: true&lt;/code&gt; argument for LoRA. Unsloth has its own &lt;a href="https://docs.unsloth.ai/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; and install steps, and supports a specific list of models, so check that your base model is on its list before you add it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 6: try the adapter, copy it off, destroy the instance
&lt;/h2&gt;

&lt;p&gt;A quick test in the terminal (no server is started). Save as &lt;code&gt;infer.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;model_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;meta-llama/Llama-3.2-3B-Instruct&lt;/span&gt;
&lt;span class="na"&gt;adapter_name_or_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;saves/llama32-3b/lora/synth&lt;/span&gt;
&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;llama3&lt;/span&gt;
&lt;span class="na"&gt;infer_backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;huggingface&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;llamafactory-cli chat infer.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask ten questions that are &lt;strong&gt;not&lt;/strong&gt; in your training data and that you wrote yourself, and compare the answers with your documentation. That is your real test; the evaluation loss is not.&lt;/p&gt;

&lt;p&gt;Now copy the adapter to your own machine before you destroy anything: destroying the instance permanently deletes its local storage. From your own machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &amp;lt;ssh-port&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/LlamaFactory/saves/llama32-3b/lora/synth/ ./adapter-synth/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check that &lt;code&gt;adapter_model.safetensors&lt;/code&gt; is in &lt;code&gt;./adapter-synth/&lt;/code&gt;, revoke the Hugging Face token you pasted on the instance, then destroy the instance from your Dashboard (the docs: "You can destroy an instance at any time"). The minutes you spent copying are billed like any other minute, and billing stops once the instance is destroyed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check:&lt;/strong&gt; the instance no longer appears as running in your dashboard, and &lt;code&gt;./adapter-synth/&lt;/code&gt; still opens on your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy and access, in plain terms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Generation (step 1) sends the documentation sections you give it to a model API. Don't send text you would not want processed by a model service, and read the &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt; for what the service handles.&lt;/li&gt;
&lt;li&gt;You keep ownership of what you put on the GPU instance. CloudGPU's terms (section 5) say it does not access the contents of your instance except where needed to investigate a credible report of a policy violation, where compelled by valid legal process, or to protect the safety of users or third parties. Local instance storage is deleted when you destroy the instance.&lt;/li&gt;
&lt;li&gt;This tutorial does not start a server. If you later serve your model from an instance, use your own endpoint with your own API key: the terms (section 6) say instance service URLs are not authenticated by default, so add authentication yourself before you share the URL.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;All read on 2026-10-27 (UTC+8).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DeepSeek, Terms of Use, Last Update March 27, 2026: &lt;a href="https://cdn.deepseek.com/policies/en-US/deepseek-terms-of-use.html" rel="noopener noreferrer"&gt;cdn.deepseek.com&lt;/a&gt;, sections 3.1 and 4.2.&lt;/li&gt;
&lt;li&gt;LLaMA-Factory: &lt;a href="https://github.com/hiyouga/LlamaFactory" rel="noopener noreferrer"&gt;README&lt;/a&gt; and &lt;a href="https://llamafactory.readthedocs.io/en/latest/getting_started/data_preparation.html" rel="noopener noreferrer"&gt;data preparation docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;CloudGPU: &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;API page&lt;/a&gt;, &lt;a href="https://cloudgpu.app/terms?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;terms&lt;/a&gt; (sections 4 to 6), &lt;a href="https://cloudgpu.app/aup?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;acceptable use policy&lt;/a&gt; (section 1.8, last updated April 22, 2026), &lt;a href="https://cloudgpu.app/privacy?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_aitrainsai&amp;amp;utm_content=t2_synthetic_data_finetune" rel="noopener noreferrer"&gt;privacy policy&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Licenses: &lt;a href="https://www.llama.com/llama3_2/license/" rel="noopener noreferrer"&gt;Llama 3.2&lt;/a&gt;, &lt;a href="https://ai.google.dev/gemma/terms" rel="noopener noreferrer"&gt;Gemma&lt;/a&gt;, &lt;a href="https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.3" rel="noopener noreferrer"&gt;Mistral 7B Instruct v0.3 model card&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>machinelearning</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Wan 2.2 A14B at 720p on a 24 GB GPU: why it runs out of memory and what to change</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:37:10 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/wan-22-a14b-at-720p-on-a-24-gb-gpu-why-it-runs-out-of-memory-and-what-to-change-8nc</link>
      <guid>https://dev.to/cloudgpu_app/wan-22-a14b-at-720p-on-a-24-gb-gpu-why-it-runs-out-of-memory-and-what-to-change-8nc</guid>
      <description>&lt;p&gt;If you try Wan 2.2 T2V A14B at 1280×720 on a 24 GB card and get &lt;code&gt;torch.OutOfMemoryError&lt;/code&gt;, you are not alone. A public bug report, reported in InvokeAI issue #9831 (2026-10-12) on an RTX 4090 24GB, lays out exactly when it fails, why, and which setting got it running. This post walks through that report and the official docs, step by step.&lt;/p&gt;

&lt;p&gt;Everything below is quoted from the issue or from official documentation. We have not reproduced the runs ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The symptom and the conditions it was reported under
&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://github.com/invoke-ai/InvokeAI/issues/9831" rel="noopener noreferrer"&gt;InvokeAI issue #9831&lt;/a&gt; (opened 2026-10-12, open, labelled bug, no comments when read on 2026-10-28):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Machine:&lt;/strong&gt; Windows 10, RTX 4090 with 24 GB VRAM, manual development checkout of InvokeAI &lt;code&gt;main&lt;/code&gt; at 8111b5067e (7.0.0-alpha.1), Python 3.12, torch 2.13.0+cu130.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; Wan 2.2 T2V A14B, GGUF Q4_K_M, both experts (High Noise and Low Noise), plus the &lt;code&gt;Wan2.2-T2V-A14B-Diffusers&lt;/code&gt; components and the Lightning 4-step LoRAs (V1.1, high and low).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job:&lt;/strong&gt; 1280×720, 81 frames, 4 steps, guidance 1.0, default &lt;code&gt;invokeai.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error:&lt;/strong&gt; &lt;code&gt;torch.OutOfMemoryError&lt;/code&gt; in &lt;code&gt;diffusers/models/transformers/transformer_wan.py:502&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The issue's results table (settings and outcome as reported; we list the memory outcome only):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;enable_partial_loading&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;device_working_mem_gb&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Attention&lt;/th&gt;
&lt;th&gt;Memory outcome reported in #9831&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;true (default)&lt;/td&gt;
&lt;td&gt;3 (default)&lt;/td&gt;
&lt;td&gt;PyTorch SDPA&lt;/td&gt;
&lt;td&gt;OOM in the first transformer block, also at 832×480&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;PyTorch SDPA&lt;/td&gt;
&lt;td&gt;Completed, but VRAM full; overflowed into shared system memory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;PyTorch SDPA&lt;/td&gt;
&lt;td&gt;Completed (the issue's workaround for 24 GB)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;true&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;SageAttention&lt;/td&gt;
&lt;td&gt;Completed, but overflowed (about 1.2 GB more held than SDPA)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;SageAttention&lt;/td&gt;
&lt;td&gt;Completed, but overflowed into shared system memory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two details matter when you compare this with your own machine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The run with partial loading off did not really "fit". The issue says the card still overflowed and Windows moved memory to shared system memory instead of raising an OOM.&lt;/li&gt;
&lt;li&gt;This is a Windows machine on a 7.0.0 alpha development build. Linux, or a release build, may behave differently; the issue does not test them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your error matches: &lt;code&gt;torch.OutOfMemoryError&lt;/code&gt; in the Wan transformer, at the first denoise block.&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;invokeai.yaml&lt;/code&gt; still has the defaults (&lt;code&gt;enable_partial_loading: true&lt;/code&gt;, &lt;code&gt;device_working_mem_gb: 3&lt;/code&gt;, or neither line set).&lt;/li&gt;
&lt;li&gt;On Windows, watch "Shared GPU memory" in Task Manager. If it climbs during generation, you are in the spill case, not the OOM case.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Why 24 GB is not enough here
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Two experts, one at a time.&lt;/strong&gt; The official &lt;a href="https://github.com/Wan-Video/Wan2.2" rel="noopener noreferrer"&gt;Wan2.2 README&lt;/a&gt; (read 2026-10-28) describes the A14B models as a two-expert design: "a high-noise expert for the early stages, focusing on overall layout; and a low-noise expert for the later stages, refining video details. Each expert model has about 14B parameters, resulting in a total of 27B parameters but only 14B active parameters per step". So you download and load two diffusion models, but each denoise step uses one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The official script's own guidance.&lt;/strong&gt; For its single-GPU 1280*720 A14B command (with &lt;code&gt;--offload_model True --convert_model_dtype&lt;/code&gt;), the README says: "This command can run on a GPU with at least 80GB VRAM." That statement is about the Wan repo's &lt;code&gt;generate.py&lt;/code&gt;, not about InvokeAI or GGUF weights, but it tells you 720p A14B is not a small job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What the issue found inside InvokeAI.&lt;/strong&gt; According to #9831:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wan denoise has no working-memory estimate. &lt;code&gt;_get_wan_transformer_working_mem_bytes&lt;/code&gt; returns &lt;code&gt;None&lt;/code&gt; unless &lt;code&gt;wan_memory_optimization&lt;/code&gt; is on, so the expert is loaded with the floor, &lt;code&gt;device_working_mem_gb&lt;/code&gt; (3 GB), as its working memory.&lt;/li&gt;
&lt;li&gt;At 720p with 81 frames the transformer sees 75,600 tokens, "and the activations need far more than 3 GB."&lt;/li&gt;
&lt;li&gt;With partial loading on, the cache frees only 2,003 MB of the 10.8 GB UMT5 text encoder before loading the 9.2 GB expert, so the first block runs out of memory with 21.3 GiB allocated.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short: model weights plus text encoder plus the attention activations for 75,600 tokens do not fit in 24 GB, and InvokeAI reserves only 3 GB for the activations by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The InvokeAI log around model loading: is the text encoder still partly in VRAM when the expert loads?&lt;/li&gt;
&lt;li&gt;Your resolution and frame count. The 75,600-token figure is for 1280×720 with 81 frames only.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Local settings to change
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3a. Raise the working memory
&lt;/h3&gt;

&lt;p&gt;InvokeAI's &lt;a href="https://invoke.ai/configuration/low-vram-mode/" rel="noopener noreferrer"&gt;Low-VRAM mode docs&lt;/a&gt; (read 2026-10-28): "Invoke reserves 3GB VRAM as working memory by default", and you can raise it in &lt;code&gt;invokeai.yaml&lt;/code&gt; to prevent OOMs (the docs' example sets 4).&lt;/p&gt;

&lt;p&gt;The issue's workaround for this exact case on 24 GB is 10:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# invokeai.yaml — workaround reported in InvokeAI issue #9831 for 24 GB&lt;/span&gt;
&lt;span class="na"&gt;device_working_mem_gb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, partial loading on and SDPA, the issue reports the 720p, 81-frame job completing. Invoke needs a restart after you edit the file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The OOM is gone and Task Manager's shared GPU memory stays flat during denoise.&lt;/li&gt;
&lt;li&gt;If you also set &lt;code&gt;max_cache_vram_gb&lt;/code&gt;, the docs say to keep it at or below your VRAM minus &lt;code&gt;device_working_mem_gb&lt;/code&gt; minus &lt;code&gt;reserve_vram_gb&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3b. Keep partial loading on, and turn off the Windows sysmem fallback
&lt;/h3&gt;

&lt;p&gt;The docs say Low-VRAM mode is enabled by default via &lt;code&gt;enable_partial_loading: true&lt;/code&gt;. In the issue, turning it off did not prevent the overflow: the encoder was unloaded, but the run spilled into shared memory.&lt;/p&gt;

&lt;p&gt;The same docs ask Windows users to disable the Nvidia sysmem fallback: NVIDIA Control Panel → 3D Settings → Manage 3D Settings → "CUDA - Sysmem Fallback Policy" → "Prefer No Sysmem Fallback". With it disabled, an overflow shows up as an OOM you can act on instead of silently spilling into shared system memory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enable_partial_loading&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; (or absent) in &lt;code&gt;invokeai.yaml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On Windows, the NVIDIA Control Panel setting reads "Prefer No Sysmem Fallback".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3c. Try &lt;code&gt;wan_memory_optimization&lt;/code&gt; (opt-in)
&lt;/h3&gt;

&lt;p&gt;From the same docs page: setting &lt;code&gt;wan_memory_optimization: true&lt;/code&gt; makes partial-load Wan transformer weights "target about 2 GiB resident and stream remaining layers from RAM". It "reduces peak VRAM during both denoise and decode, but generation can be substantially slower and requires enough system RAM for offloaded weights." The docs also note same-seed output may differ slightly from the baseline path.&lt;/p&gt;

&lt;p&gt;The issue explains that this flag is also what turns on Wan's working-memory estimate. It does not report a run with the flag on, so we have no figures for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;wan_memory_optimization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free system RAM during generation; the docs say offloaded weights need it.&lt;/li&gt;
&lt;li&gt;Compare a short test clip against your baseline before committing to long runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3d. Lower the resolution, and treat frame count with care
&lt;/h3&gt;

&lt;p&gt;The issue's only other resolution is 832×480. With default settings it also ran out of memory. With partial loading on and &lt;code&gt;device_working_mem_gb: 10&lt;/code&gt;, it fit with both attention backends.&lt;/p&gt;

&lt;p&gt;The issue tests 81 frames only. Fewer frames means a shorter clip, but the issue gives no figures for it, so try it on your own setup rather than relying on a number from us.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At 832×480, the default settings still fail in the issue. Raise &lt;code&gt;device_working_mem_gb&lt;/code&gt; as well; do not rely on resolution alone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3e. Quantization, and the official script's flags
&lt;/h3&gt;

&lt;p&gt;The issue already used GGUF Q4_K_M weights for both experts and still ran out of memory at default settings. Lower-bit weights alone did not fix it there; working memory did.&lt;/p&gt;

&lt;p&gt;If you use the Wan repo's own &lt;code&gt;generate.py&lt;/code&gt; instead of InvokeAI, the README says: "If you encounter OOM (Out-of-Memory) issues, you can use the &lt;code&gt;--offload_model True&lt;/code&gt;, &lt;code&gt;--convert_model_dtype&lt;/code&gt; and &lt;code&gt;--t5_cpu&lt;/code&gt; options to reduce GPU memory usage."&lt;/p&gt;

&lt;p&gt;On attention, these are figures reported in InvokeAI issue #9831 (one RTX 4090 24GB on Windows), not measured by us and not a claim about CloudGPU cards: the issue reports SageAttention held about 1.2 GB more than SDPA at 75,600 tokens, which pushed that card over the edge at 720p. At 832×480, the issue reports both backends fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you switched to SageAttention and shared GPU memory now climbs at 720p, switch back to SDPA and compare.&lt;/li&gt;
&lt;li&gt;The Wan2.2 model weights are licensed Apache-2.0 (per the README and the &lt;a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B" rel="noopener noreferrer"&gt;model card&lt;/a&gt;); check the license of any community GGUF build you download separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. If local settings are still not enough: run the same workflow on a card with more VRAM
&lt;/h2&gt;

&lt;p&gt;If the slower settings are not an option for you, the other route is more VRAM. On CloudGPU you can rent a card with more VRAM, billed per minute, and set up the same InvokeAI workflow on it. Cards listed on &lt;a href="https://cloudgpu.app/pricing?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg01_comfyui&amp;amp;utm_content=wan22_oom" rel="noopener noreferrer"&gt;/pricing&lt;/a&gt; when we read it on 2026-10-28 include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Card on /pricing&lt;/th&gt;
&lt;th&gt;VRAM (as shown on /pricing)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;RTX 5090 32G&lt;/td&gt;
&lt;td&gt;32 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RTX 4090 48G&lt;/td&gt;
&lt;td&gt;48 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A100 80G&lt;/td&gt;
&lt;td&gt;80 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To be clear about what this does and does not mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have not tested Wan 2.2 A14B, InvokeAI or this workflow on any of these cards. The only difference we can state is more VRAM than a 24 GB card.&lt;/li&gt;
&lt;li&gt;VRAM need depends on model, precision and workflow. See &lt;a href="https://cloudgpu.app/guides/how-much-vram-do-i-need-to-run-an-llm" rel="noopener noreferrer"&gt;How much VRAM do I need to run an LLM?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Billing is per minute, rounded up, with a 1-minute minimum, and stops when you destroy the instance (&lt;a href="https://cloudgpu.app/terms" rel="noopener noreferrer"&gt;terms&lt;/a&gt;). Rates per card are on /pricing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to check&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Before renting, compare the VRAM column above with what your settings needed locally (the InvokeAI log and Task Manager tell you where it ran out).&lt;/li&gt;
&lt;li&gt;Destroy the instance when the job is done, so billing stops.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Why does Wan 2.2 A14B run out of memory at 720p on a 24 GB GPU in InvokeAI?&lt;/strong&gt;&lt;br&gt;
According to InvokeAI issue #9831, Wan denoise has no working-memory estimate unless &lt;code&gt;wan_memory_optimization&lt;/code&gt; is on, so InvokeAI reserves only the default 3 GB for activations. At 1280×720 with 81 frames the transformer sees 75,600 tokens, and the activations need far more than that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What setting fixed it in the issue?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;device_working_mem_gb: 10&lt;/code&gt; in &lt;code&gt;invokeai.yaml&lt;/code&gt;, with partial loading left on. The issue reports the 720p, 81-frame job then completed on an RTX 4090 24GB (Windows 10, InvokeAI 7.0.0-alpha.1).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does turning off partial loading help?&lt;/strong&gt;&lt;br&gt;
Not in the issue. With &lt;code&gt;enable_partial_loading: false&lt;/code&gt; the card still overflowed, and Windows moved memory to shared system memory instead of raising an OOM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does lowering the resolution to 832×480 avoid the OOM?&lt;/strong&gt;&lt;br&gt;
Not on its own. The issue reports an OOM at 832×480 with default settings too; with &lt;code&gt;device_working_mem_gb: 10&lt;/code&gt; it fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What does the official Wan2.2 README say about VRAM for A14B at 720p?&lt;/strong&gt;&lt;br&gt;
For its own single-GPU &lt;code&gt;generate.py&lt;/code&gt; command at 1280*720, it says the command "can run on a GPU with at least 80GB VRAM", and suggests &lt;code&gt;--offload_model True&lt;/code&gt;, &lt;code&gt;--convert_model_dtype&lt;/code&gt; and &lt;code&gt;--t5_cpu&lt;/code&gt; for OOM issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;InvokeAI issue #9831, &lt;a href="https://github.com/invoke-ai/InvokeAI/issues/9831" rel="noopener noreferrer"&gt;https://github.com/invoke-ai/InvokeAI/issues/9831&lt;/a&gt; (opened 2026-10-12; read 2026-10-28)&lt;/li&gt;
&lt;li&gt;Wan2.2 README (GitHub), &lt;a href="https://github.com/Wan-Video/Wan2.2" rel="noopener noreferrer"&gt;https://github.com/Wan-Video/Wan2.2&lt;/a&gt; (read 2026-10-28)&lt;/li&gt;
&lt;li&gt;Wan2.2-T2V-A14B model card, &lt;a href="https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B" rel="noopener noreferrer"&gt;https://huggingface.co/Wan-AI/Wan2.2-T2V-A14B&lt;/a&gt; (read 2026-10-28)&lt;/li&gt;
&lt;li&gt;InvokeAI docs, Low-VRAM mode, &lt;a href="https://invoke.ai/configuration/low-vram-mode/" rel="noopener noreferrer"&gt;https://invoke.ai/configuration/low-vram-mode/&lt;/a&gt; (read 2026-10-28)&lt;/li&gt;
&lt;li&gt;CloudGPU /pricing and /terms (read 2026-10-28)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>video</category>
      <category>machinelearning</category>
      <category>gpu</category>
    </item>
    <item>
      <title>Use DeepSeek V4.1 Flash, Kimi and GLM in RAGFlow with CloudGPU</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Tue, 27 Oct 2026 09:43:22 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-kimi-and-glm-in-ragflow-with-cloudgpu-4jpk</link>
      <guid>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-kimi-and-glm-in-ragflow-with-cloudgpu-4jpk</guid>
      <description>&lt;p&gt;RAGFlow's pull request &lt;a href="https://github.com/infiniflow/ragflow/pull/21543" rel="noopener noreferrer"&gt;#21543&lt;/a&gt;, "feat: add CloudGPU as a model provider", was merged into RAGFlow's &lt;code&gt;main&lt;/code&gt; branch on 2026-10-27. It adds CloudGPU as a model provider for &lt;strong&gt;chat&lt;/strong&gt; models and &lt;strong&gt;image input&lt;/strong&gt;. This post shows how to use DeepSeek, Kimi, GLM and MiniMax chat models from it.&lt;/p&gt;

&lt;p&gt;As of 2026-10-27 the CloudGPU provider is merged into RAGFlow's main branch but is not in any tagged release yet. Until then, use RAGFlow's &lt;strong&gt;OpenAI-API-Compatible&lt;/strong&gt; provider with the base URL &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your RAGFlow build includes the merge, pick &lt;strong&gt;CloudGPU&lt;/strong&gt; under &lt;strong&gt;User settings &amp;gt; Model providers&lt;/strong&gt; and paste an API key from the CloudGPU console.&lt;/li&gt;
&lt;li&gt;If you run a tagged release (the CloudGPU provider is not in one yet), use the &lt;strong&gt;OpenAI-API-Compatible&lt;/strong&gt; provider with the base URL &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;CloudGPU has &lt;strong&gt;no embedding models&lt;/strong&gt;. Embeddings for your knowledge bases must come from somewhere else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Disclosure: I work on CloudGPU. We have not run RAGFlow end to end against this API ourselves. The steps below follow the PR and RAGFlow's own docs, and anything we could not check is marked as a check for you to run.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A RAGFlow install (self-hosted).&lt;/li&gt;
&lt;li&gt;A CloudGPU API key. It starts with &lt;code&gt;cgw-sk-&lt;/code&gt; and is created in the &lt;a href="https://cloudgpu.app/api/console?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg06_indiedev&amp;amp;utm_content=devto_ragflow_cloudgpu" rel="noopener noreferrer"&gt;API Console&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An embedding model from another source (see the embeddings section).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the shell checks, put the key in an environment variable so it stays out of your history and config files:&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;CGW_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgw-sk-your-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create an API key
&lt;/h2&gt;

&lt;p&gt;Following the &lt;a href="https://cloudgpu.app/api/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg06_indiedev&amp;amp;utm_content=devto_ragflow_cloudgpu" rel="noopener noreferrer"&gt;API docs&lt;/a&gt; quick start:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://cloudgpu.app/api/console?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg06_indiedev&amp;amp;utm_content=devto_ragflow_cloudgpu" rel="noopener noreferrer"&gt;API Console&lt;/a&gt; and create a key.&lt;/li&gt;
&lt;li&gt;Copy the &lt;code&gt;cgw-sk-...&lt;/code&gt; value. The docs say it is shown once, so copy it right away and store it somewhere safe.&lt;/li&gt;
&lt;li&gt;Use it as &lt;code&gt;Authorization: Bearer cgw-sk-&amp;lt;your_key&amp;gt;&lt;/code&gt;, or paste it into RAGFlow's &lt;strong&gt;API Key&lt;/strong&gt; field as shown below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The docs also say keys are scoped to your CloudGPU account and can be revoked at any time from the console.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which models
&lt;/h2&gt;

&lt;p&gt;These IDs are on the CloudGPU &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg06_indiedev&amp;amp;utm_content=devto_ragflow_cloudgpu" rel="noopener noreferrer"&gt;API page&lt;/a&gt; (checked 2026-10-27) and in the PR's &lt;code&gt;conf/models/cloudgpu.json&lt;/code&gt;. The IDs are plain (no organization prefix) and case-sensitive.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Image input (marked in the provider config)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1 Flash&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Pro&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-k2.7-code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi K2.7 Code&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-k2.6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi K2.6&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;glm-5.3-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GLM-5.3 Flash&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;glm-5.2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GLM-5.2&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimax-m3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MiniMax M3&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These 5 models (&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;, &lt;code&gt;kimi-k2.7-code&lt;/code&gt;, &lt;code&gt;kimi-k2.6&lt;/code&gt;, &lt;code&gt;glm-5.3-flash&lt;/code&gt;, &lt;code&gt;minimax-m3&lt;/code&gt;) are marked as supporting image input in the CloudGPU provider config. Send one test image yourself before relying on it.&lt;/p&gt;

&lt;p&gt;The PR's config also marks all of these models as tool-capable. Treat that as a hint and test it with your own agent or chat setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option A: the CloudGPU provider (builds that include the merge)
&lt;/h2&gt;

&lt;p&gt;The PR adds a provider named exactly &lt;code&gt;CloudGPU&lt;/code&gt; (&lt;code&gt;CloudGPU = 'CloudGPU'&lt;/code&gt; in &lt;code&gt;web/src/constants/llm.ts&lt;/code&gt;, and &lt;code&gt;"name": "CloudGPU"&lt;/code&gt; in &lt;code&gt;conf/llm_factories.json&lt;/code&gt;). Its default URL is &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, and the API key link in the UI points to the CloudGPU API console. The provider entry is tagged &lt;code&gt;LLM,IMAGE2TEXT&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Following RAGFlow's &lt;a href="https://ragflow.io/docs/llm_api_key_setup" rel="noopener noreferrer"&gt;model provider docs&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;User settings &amp;gt; Model providers&lt;/strong&gt;. Under &lt;strong&gt;Available models&lt;/strong&gt;, look for &lt;strong&gt;CloudGPU&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Select it and create an instance (the docs ask for an instance name).&lt;/li&gt;
&lt;li&gt;Enter your &lt;strong&gt;API Key&lt;/strong&gt; (the &lt;code&gt;cgw-sk-...&lt;/code&gt; key). Keep the default &lt;strong&gt;Base URL&lt;/strong&gt;, &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, if the form shows one.&lt;/li&gt;
&lt;li&gt;Verify the connection, add models from the list or as custom models, then store the instance.&lt;/li&gt;
&lt;li&gt;Under default models, set a default LLM from the IDs above.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reader check: if &lt;strong&gt;CloudGPU&lt;/strong&gt; is not in the list, your build does not include the merge yet. Use Option B. Exact field names can differ between RAGFlow versions; follow the docs for the version you run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option B: OpenAI-API-Compatible provider (use this until a tagged release includes CloudGPU)
&lt;/h2&gt;

&lt;p&gt;RAGFlow's docs say that for models not listed, you can click &lt;strong&gt;OpenAI-API-Compatible&lt;/strong&gt; on the &lt;strong&gt;Model providers&lt;/strong&gt; page. This provider is already in RAGFlow's provider list.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;strong&gt;User settings &amp;gt; Model providers&lt;/strong&gt; and select &lt;strong&gt;OpenAI-API-Compatible&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create an instance and enter a name.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Key&lt;/strong&gt;: your &lt;code&gt;cgw-sk-...&lt;/code&gt; key. &lt;strong&gt;Base URL&lt;/strong&gt;: &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;. RAGFlow's docs warn that an incorrect Base URL makes verification or model calls fail, and to confirm whether the path must include &lt;code&gt;/v1&lt;/code&gt;. Here it does.&lt;/li&gt;
&lt;li&gt;Verify the connection.&lt;/li&gt;
&lt;li&gt;Add a model with &lt;strong&gt;Add custom model&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Model name: one ID from the table, exactly as written, for example &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;. The docs say the name must match the ID the provider exposes.&lt;/li&gt;
&lt;li&gt;Model type: the chat/LLM type.&lt;/li&gt;
&lt;li&gt;Max tokens: fill in a value that fits your use. CloudGPU does not publish per-model output caps on its API page, so pick your own.&lt;/li&gt;
&lt;li&gt;Tool call: enable it only if you tested tool calling for that model (the docs say not to enable it for models that lack it). Newer docs call this field &lt;strong&gt;Tool calling&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Confirm&lt;/strong&gt;, then check that the model works with a real call.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Repeat for each ID you want. Then set your default LLM.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For image input in Option B, add the same ID once more and choose the vision-language model type (the docs call it VLM). Only use IDs marked "yes" in the table above. Reader check: the exact type name in the dropdown depends on your version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the key and model ID outside RAGFlow first
&lt;/h2&gt;

&lt;p&gt;Two curl calls take a minute and separate "my key or model ID is wrong" from "my RAGFlow settings are wrong".&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;# lists the IDs your key can use (without a key this returns HTTP 401 missing_authorization)&lt;/span&gt;
curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://cloudgpu.app/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# one plain chat request&lt;/span&gt;
curl https://cloudgpu.app/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"Say hi in one sentence."}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a JSON chat completion back. Run it yourself to confirm; we are not quoting an output here.&lt;/p&gt;

&lt;p&gt;The same call with the OpenAI Python SDK, since the endpoint is OpenAI-compatible:&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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://cloudgpu.app/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_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;CGW_API_KEY&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-v4.1-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&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;role&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;user&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;Say hi in one sentence.&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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Embeddings: configure them separately
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CloudGPU does not offer embedding models.&lt;/strong&gt; The PR states this as well ("CloudGPU serves no embedding models, so this adds chat and image input only"), and the API docs say "Embeddings are not available yet."&lt;/p&gt;

&lt;p&gt;A RAGFlow knowledge base needs an embedding model to index and retrieve your documents. RAGFlow's docs say that, at minimum, you should set a default LLM and a default embedding model. So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the embedding option your RAGFlow install already has, or a local embedding model (RAGFlow's docs describe connecting locally deployed models with Ollama or vLLM), or an embedding model from another provider.&lt;/li&gt;
&lt;li&gt;Set that as your default embedding model, and use a CloudGPU model only as the chat model for chats, search and agents.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reader check: after you set both, create a small knowledge base, parse one document and ask a question in a chat that uses a CloudGPU model. If parsing fails, look at the embedding settings first, not the CloudGPU ones.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 401 &lt;code&gt;invalid_api_key&lt;/code&gt;&lt;/strong&gt;: the key is unknown or revoked. Create a new one in the console.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 401 &lt;code&gt;missing_authorization&lt;/code&gt;&lt;/strong&gt;: no &lt;code&gt;Authorization: Bearer&lt;/code&gt; header was sent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 400 &lt;code&gt;model_not_found&lt;/code&gt;&lt;/strong&gt;: the model ID is not in the catalog. Compare it with &lt;code&gt;GET /v1/models&lt;/code&gt;; IDs are case-sensitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 402 &lt;code&gt;insufficient_balance&lt;/code&gt;&lt;/strong&gt;: the account balance is too low for the request.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 429 &lt;code&gt;rate_limit_exceeded&lt;/code&gt;&lt;/strong&gt;: the API docs list 100 requests per minute per key and 10,000 requests per day per account for the public beta. Bulk jobs such as parsing many files at once could reach that, so check whether your RAGFlow task makes many calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification fails in RAGFlow but curl works&lt;/strong&gt;: re-check the Base URL (it must end in &lt;code&gt;/v1&lt;/code&gt;, no trailing path), and that no extra spaces were pasted into the API Key field.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CloudGPU is missing from Available models&lt;/strong&gt;: use Option B.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is CloudGPU in a RAGFlow release?&lt;/strong&gt;&lt;br&gt;
Not yet. As of 2026-10-27 the provider is merged into RAGFlow's main branch but is not in any tagged release. Until then, use the OpenAI-API-Compatible provider with the base URL &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, and check the &lt;a href="https://github.com/infiniflow/ragflow/releases" rel="noopener noreferrer"&gt;releases page&lt;/a&gt; for later versions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can CloudGPU models make my embeddings?&lt;/strong&gt;&lt;br&gt;
No. Configure embeddings separately, as described above.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do I need the PR for the base URL route?&lt;/strong&gt;&lt;br&gt;
No. The OpenAI-API-Compatible provider only needs a base URL, a key and a model ID.&lt;/p&gt;

&lt;p&gt;Keys, the model table and the quick start are on the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg06_indiedev&amp;amp;utm_content=devto_ragflow_cloudgpu" rel="noopener noreferrer"&gt;CloudGPU API page&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>llm</category>
    </item>
    <item>
      <title>Use DeepSeek V4.1 Flash and Kimi K2.7 Code in goose with a custom provider file</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Tue, 27 Oct 2026 06:35:25 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-and-kimi-k27-code-in-goose-with-a-custom-provider-file-4bl3</link>
      <guid>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-and-kimi-k27-code-in-goose-with-a-custom-provider-file-4bl3</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; copy the JSON below into &lt;code&gt;~/.config/goose/custom_providers/cloudgpu.json&lt;/code&gt;, run &lt;code&gt;export CGW_API_KEY="cgw-sk-your-key"&lt;/code&gt;, then start goose with &lt;code&gt;--provider cloudgpu --model deepseek-v4.1-flash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU, and the endpoint in this post is ours. We haven't tested goose against this endpoint ourselves; the steps follow goose's docs (&lt;a href="https://github.com/aaif-goose/goose/blob/main/documentation/docs/getting-started/providers.md#configure-custom-provider" rel="noopener noreferrer"&gt;Configure Custom Provider&lt;/a&gt;). Treat the checks below as things you run, not results we measured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Tool calling through this endpoint isn't tested by us. goose leans on tool calls for real work, so run the self-checks in Step 5 before you hand it a task that matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a file and not a built-in provider
&lt;/h2&gt;

&lt;p&gt;goose's CONTRIBUTING.md asks services with an OpenAI-compatible API to publish a goose custom-provider JSON file with their own documentation, so users can drop it into their local &lt;code&gt;custom_providers&lt;/code&gt; directory without waiting for a goose release. This post is that documentation for CloudGPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;goose (CLI or Desktop), installed from its own docs.&lt;/li&gt;
&lt;li&gt;A CloudGPU API key. Go to API Console and create a key. Copy the &lt;code&gt;cgw-sk-...&lt;/code&gt; value — it's shown once. The quick start is on the &lt;a href="https://cloudgpu.app/api/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg04_codingagent&amp;amp;utm_content=devto_goose_provider" rel="noopener noreferrer"&gt;API docs page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Get the JSON
&lt;/h2&gt;

&lt;p&gt;Copy this into a file named &lt;code&gt;cloudgpu.json&lt;/code&gt;:&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cloudgpu"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"engine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openai"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"display_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CloudGPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DeepSeek, Kimi, GLM and MiniMax models through CloudGPU's OpenAI-compatible API."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"api_key_env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CGW_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"base_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cloudgpu.app/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dynamic_models"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"models"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deepseek-v4.1-flash"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"deepseek-v4-pro"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kimi-k2.7-code"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"glm-5.3-flash"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"minimax-m3"&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="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"supports_streaming"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requires_auth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model_doc_link"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cloudgpu.app/api/docs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"setup_steps"&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="s2"&gt;"Sign in at https://cloudgpu.app, then go to API Console: https://cloudgpu.app/api/console"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Create a key. Copy the cgw-sk-... value — it's shown once."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Top up your balance via Billing. Model IDs are listed on https://cloudgpu.app/api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Set the CGW_API_KEY environment variable to your key before starting goose"&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="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;What the fields do, in goose's terms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; is the provider ID you pass to &lt;code&gt;--provider&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;engine: "openai"&lt;/code&gt; tells goose to speak the OpenAI-compatible chat format.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;base_url&lt;/code&gt; is the CloudGPU endpoint. goose adds &lt;code&gt;chat/completions&lt;/code&gt; after the &lt;code&gt;/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api_key_env&lt;/code&gt; is the name of the environment variable goose reads the key from. There is no key in the file, and there never should be.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;models&lt;/code&gt; is the list goose shows. With &lt;code&gt;dynamic_models: false&lt;/code&gt;, goose uses only these five IDs and doesn't fetch a model list from the endpoint.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;context_limit&lt;/code&gt; is set. It's optional in goose's format, and we don't publish context sizes for these models, so goose uses its own defaults.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 2: Put it where goose looks
&lt;/h2&gt;

&lt;p&gt;goose's docs give one folder per OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macOS/Linux: &lt;code&gt;~/.config/goose/custom_providers/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\Block\goose\config\custom_providers\&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On macOS or Linux:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/.config/goose/custom_providers
&lt;span class="nb"&gt;cp &lt;/span&gt;cloudgpu.json ~/.config/goose/custom_providers/cloudgpu.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Self-check: &lt;code&gt;goose info&lt;/code&gt; prints the configuration file location. &lt;code&gt;custom_providers&lt;/code&gt; sits in that same config folder. If CloudGPU doesn't show up in the provider list, restart goose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Set the key
&lt;/h2&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;CGW_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgw-sk-your-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use your own key in place of &lt;code&gt;cgw-sk-your-key&lt;/code&gt;, and run goose from that same shell. Two things from goose's config docs worth knowing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;goose does not read provider API keys from &lt;code&gt;config.yaml&lt;/code&gt;. A key there is ignored.&lt;/li&gt;
&lt;li&gt;If you'd rather keep the key in goose's keychain, open the provider in goose Desktop and enter the key there.&lt;/li&gt;
&lt;li&gt;If goose Desktop changes the variable name, use the one shown in the provider's settings (&lt;code&gt;Settings&lt;/code&gt;, then the &lt;code&gt;Models&lt;/code&gt; tab, then &lt;code&gt;Configure providers&lt;/code&gt;, then click CloudGPU in the list).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Self-check, outside goose: this is the request from our API docs. If it fails, fix it before going further.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"Say hi in one sentence."}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Pick a model
&lt;/h2&gt;

&lt;p&gt;These are the model IDs in the file. Each one is in the model table on our API page:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Pro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-k2.7-code&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi K2.7 Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;glm-5.3-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GLM 5.3 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimax-m3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;MiniMax M3&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;IDs are case-sensitive and the dots matter. A wrong ID comes back as &lt;code&gt;400 model_not_found&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;goose CLI:&lt;/strong&gt; pass the provider and model per run, as in goose's CLI docs: &lt;code&gt;goose run --provider cloudgpu --model kimi-k2.7-code -t "..."&lt;/code&gt;. goose also reads &lt;code&gt;GOOSE_PROVIDER&lt;/code&gt; and &lt;code&gt;GOOSE_MODEL&lt;/code&gt; from the environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;goose Desktop:&lt;/strong&gt; goose's docs say custom providers appear in the provider list like any other provider. Open &lt;code&gt;Settings&lt;/code&gt;, then the &lt;code&gt;Models&lt;/code&gt; tab, then &lt;code&gt;Switch models&lt;/code&gt;, choose CloudGPU, and pick a model (or &lt;code&gt;Use custom model&lt;/code&gt; to type an ID). Confirm with &lt;code&gt;Select model&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Quick test
&lt;/h2&gt;

&lt;p&gt;Start with a plain prompt and no session file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;goose run &lt;span class="nt"&gt;--no-session&lt;/span&gt; &lt;span class="nt"&gt;--provider&lt;/span&gt; cloudgpu &lt;span class="nt"&gt;--model&lt;/span&gt; deepseek-v4.1-flash &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"Reply with one short sentence that names the model you are."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Self-checks (what to look for, not what we saw):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You get a reply from the model, not an error. If you see &lt;code&gt;401 missing_authorization&lt;/code&gt; or &lt;code&gt;401 invalid_api_key&lt;/code&gt;, the key isn't reaching goose. Check that &lt;code&gt;CGW_API_KEY&lt;/code&gt; is set in the shell that runs goose.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400 model_not_found&lt;/code&gt;: compare the &lt;code&gt;--model&lt;/code&gt; value with the table above, character for character.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cloudgpu&lt;/code&gt; isn't recognised as a provider: check the file is at the path from Step 2 and that it parses (&lt;code&gt;python3 -m json.tool ~/.config/goose/custom_providers/cloudgpu.json&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then try a first coding task in a scratch folder, with goose's &lt;code&gt;developer&lt;/code&gt; extension turned on:&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;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/goose-cloudgpu-test &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; ~/goose-cloudgpu-test
goose run &lt;span class="nt"&gt;--no-session&lt;/span&gt; &lt;span class="nt"&gt;--provider&lt;/span&gt; cloudgpu &lt;span class="nt"&gt;--model&lt;/span&gt; kimi-k2.7-code &lt;span class="nt"&gt;--with-builtin&lt;/span&gt; developer &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"Create hello.py that prints the numbers 1 to 5, then show me the file."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one needs tool calls, which we haven't tested through our endpoint. Self-check: &lt;code&gt;hello.py&lt;/code&gt; exists in the folder and contains what you asked for. If goose answers in text but no file appears, the model's tool calls didn't make it through. Try another model ID from the table and tell us which one failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;goose's own reference for this file format: &lt;a href="https://github.com/aaif-goose/goose/blob/main/documentation/docs/getting-started/providers.md#configure-custom-provider" rel="noopener noreferrer"&gt;Configure Custom Provider&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;goose CLI flags (&lt;code&gt;run&lt;/code&gt;, &lt;code&gt;--provider&lt;/code&gt;, &lt;code&gt;--model&lt;/code&gt;): &lt;a href="https://github.com/aaif-goose/goose/blob/main/documentation/docs/guides/goose-cli-commands.md" rel="noopener noreferrer"&gt;goose CLI commands&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something in these steps doesn't match what your goose shows, leave a comment with your goose version and the error text (never the key), and we'll update the post.&lt;/p&gt;

</description>
      <category>goose</category>
      <category>ai</category>
      <category>deepseek</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Roo Code with DeepSeek V4 Flash over an OpenAI-compatible endpoint (plus a tool-call check first)</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Tue, 27 Oct 2026 00:22:35 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/roo-code-with-deepseek-v4-flash-over-an-openai-compatible-endpoint-plus-a-tool-call-check-first-4fl6</link>
      <guid>https://dev.to/cloudgpu_app/roo-code-with-deepseek-v4-flash-over-an-openai-compatible-endpoint-plus-a-tool-call-check-first-4fl6</guid>
      <description>&lt;p&gt;Adapted from our CloudGPU guide on using DeepSeek V4 Flash in Roo Code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; in Roo Code, pick the &lt;strong&gt;OpenAI Compatible&lt;/strong&gt; provider, set the Base URL to &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, paste your API key and type the model ID &lt;code&gt;deepseek-v4-flash&lt;/code&gt;. Before you start a real task, send one request with a &lt;code&gt;tools&lt;/code&gt; array. Roo Code only works with models that return native tool calls, and that one request tells you whether yours does.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU, and the endpoint below is ours. The Roo Code steps follow the official Roo Code docs (&lt;a href="https://docs.roocode.com/providers/openai-compatible" rel="noopener noreferrer"&gt;Using OpenAI Compatible Providers&lt;/a&gt;). We haven't run Roo Code end to end against this endpoint ourselves, so treat the checks below as something you run, not results we measured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Tool calling through this endpoint isn't tested by us; run the self-check in Step 3 before relying on Roo Code's tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;An API key (&lt;code&gt;cgw-sk-...&lt;/code&gt;) from the API Console. It is shown once, so copy it right away. The quick start is on the &lt;a href="https://cloudgpu.app/api/docs?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg04_codingagent&amp;amp;utm_content=devto_roo_deepseek" rel="noopener noreferrer"&gt;API docs page&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Roo Code installed in VS Code (follow its install docs).&lt;/li&gt;
&lt;li&gt;The key in an environment variable, so it stays out of your shell history and scripts:
&lt;/li&gt;
&lt;/ul&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;CGW_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgw-sk-your-key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1: Configure the provider in Roo Code
&lt;/h2&gt;

&lt;p&gt;From the Roo Code docs, the settings live in the Roo Code settings panel:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Roo Code field&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;API Provider&lt;/td&gt;
&lt;td&gt;OpenAI Compatible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Base URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Key&lt;/td&gt;
&lt;td&gt;your &lt;code&gt;cgw-sk-...&lt;/code&gt; key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Model Configuration&lt;/strong&gt; (Max Output Tokens, Context Window and so on) is optional. We don't publish values for these models here, so leave the defaults unless you know the limits you need.&lt;/p&gt;

&lt;p&gt;Other chat model IDs on the API page model table (checked 2026-10-27) that go in the same Model field:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-k3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi K3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;kimi-k2.6&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Kimi K2.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;IDs are case-sensitive. &lt;code&gt;GET /v1/models&lt;/code&gt; (with your key) returns the exact list your key can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://cloudgpu.app/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the &lt;code&gt;Authorization&lt;/code&gt; header you get &lt;code&gt;401 missing_authorization&lt;/code&gt;, which is a quick way to confirm the URL is right but the key isn't being sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Plain chat check
&lt;/h2&gt;

&lt;p&gt;This is the request from the API docs, with your model ID:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"deepseek-v4-flash","messages":[{"role":"user","content":"Say hi in one sentence."}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If this fails, fix it before touching Roo Code. The troubleshooting list at the end maps the error codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Native tool-call check (the one that matters for Roo Code)
&lt;/h2&gt;

&lt;p&gt;The Roo Code docs say it "uses native tool calling exclusively" and "If a model does not support native tool calling, it cannot be used with Roo Code." So send one request with a &lt;code&gt;tools&lt;/code&gt; array and look at what comes back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "deepseek-v4-flash",
    "messages": [{"role": "user", "content": "What is the weather in Paris? Use the tool."}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Get the weather for a city",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same check with the &lt;code&gt;openai&lt;/code&gt; Python package (&lt;code&gt;pip install openai&lt;/code&gt;), looping over the four model IDs from the table above. Check &lt;code&gt;GET /v1/models&lt;/code&gt; for the current list.&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;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&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://cloudgpu.app/v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;api_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;CGW_API_KEY&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;tools&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;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;function&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;function&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&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;get_weather&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;description&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;Get the weather for a city&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;parameters&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;object&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;properties&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;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;string&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;required&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;city&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="p"&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;model&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-v4-flash&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;deepseek-v4.1-flash&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;kimi-k3&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;kimi-k2.6&lt;/span&gt;&lt;span class="sh"&gt;"&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="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;messages&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;role&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;user&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;What is the weather in Paris? Use the tool.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
            &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2048&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# leave room: some models think before they answer
&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;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: error -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&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="k"&gt;continue&lt;/span&gt;
    &lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tool_calls&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;function&lt;/span&gt;
        &lt;span class="nf"&gt;print&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;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: tool call -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&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;call&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&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="nf"&gt;print&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;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: no tool call, text -&amp;gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;!r}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to read it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tool_calls&lt;/code&gt; present&lt;/strong&gt; (a &lt;code&gt;get_weather&lt;/code&gt; call with &lt;code&gt;{"city": "Paris"}&lt;/code&gt; or similar): the model returned a tool call for this request; try it in Roo Code on a small task before you rely on it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain text or an error&lt;/strong&gt;: Roo Code is unlikely to work with that model. Try another ID from &lt;code&gt;/v1/models&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Empty &lt;code&gt;content&lt;/code&gt; and no tool call&lt;/strong&gt;: some models spend output tokens on thinking first. Raise &lt;code&gt;max_tokens&lt;/code&gt; (and Max Output Tokens in Roo Code's Model Configuration) and run it again before you decide.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;These match the Roo Code docs' troubleshooting list and the error codes on the API docs page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"Invalid API Key"&lt;/strong&gt; (HTTP 401 &lt;code&gt;invalid_api_key&lt;/code&gt;): the key is wrong or revoked. Create a new one in the console (keys are shown once).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 401 &lt;code&gt;missing_authorization&lt;/code&gt;&lt;/strong&gt;: no &lt;code&gt;Authorization: Bearer&lt;/code&gt; header was sent. In a shell, check &lt;code&gt;echo $CGW_API_KEY&lt;/code&gt; isn't empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"Model Not Found"&lt;/strong&gt; (HTTP 400 &lt;code&gt;model_not_found&lt;/code&gt;): check the ID against &lt;code&gt;/v1/models&lt;/code&gt;; IDs are case-sensitive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connection errors&lt;/strong&gt;: the Base URL must be exactly &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool-calling errors&lt;/strong&gt;: Roo Code needs native tool calling. Re-run the Step 3 check; if it fails, try another ID and run the check again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP 429 &lt;code&gt;rate_limit_exceeded&lt;/code&gt;&lt;/strong&gt;: the API docs list 100 requests per minute per key and 10,000 requests per day per account during the beta.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does Roo Code work with an OpenAI-compatible API?&lt;/strong&gt;&lt;br&gt;
Yes. The Roo Code docs describe connecting to any OpenAI-compatible endpoint with a base URL, an API key and a model ID.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does deepseek-v4-flash support the tool calling Roo Code needs?&lt;/strong&gt;&lt;br&gt;
We haven't confirmed it inside Roo Code. Run the Step 3 check before you rely on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can I switch to Kimi without changing anything else?&lt;/strong&gt;&lt;br&gt;
Yes, change only the Model field to &lt;code&gt;kimi-k3&lt;/code&gt; or &lt;code&gt;kimi-k2.6&lt;/code&gt;. Run the tool-call check for that ID first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are the rates?&lt;/strong&gt;&lt;br&gt;
Per-model rates are in the price table on the CloudGPU API page.&lt;/p&gt;




&lt;p&gt;Facts checked 2026-10-27.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vscode</category>
      <category>deepseek</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Save Your Work Before You Destroy a Cloud GPU: Checkpoints, rsync and Hub Uploads (2026)</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Mon, 26 Oct 2026 13:13:47 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/save-your-work-before-you-destroy-a-cloud-gpu-checkpoints-rsync-and-hub-uploads-2026-4nie</link>
      <guid>https://dev.to/cloudgpu_app/save-your-work-before-you-destroy-a-cloud-gpu-checkpoints-rsync-and-hub-uploads-2026-4nie</guid>
      <description>&lt;p&gt;&lt;strong&gt;On CloudGPU, destroying an instance deletes its disk, and there is no persistent storage across instances yet, so anything you want to keep — checkpoints, LoRA adapters, generated images, logs — must be copied off the machine before you destroy it.&lt;/strong&gt; An instance is also destroyed automatically if your balance runs out. This post is the checklist and the commands we use.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens to your files, and when
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;What happens to the disk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;You click Destroy&lt;/td&gt;
&lt;td&gt;The instance stops, billing settles, the disk is deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Your balance cannot cover the next hour&lt;/td&gt;
&lt;td&gt;The instance is destroyed and its disk deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;You want to “pause” and resume tomorrow&lt;/td&gt;
&lt;td&gt;Not available yet: stopping releases the GPU; you start a new instance next time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The JupyterLab tab or SSH session closes&lt;/td&gt;
&lt;td&gt;Nothing — the instance keeps running (and billing) until destroyed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because billing is per minute, the economical pattern is: work, copy outputs off, destroy. The minutes spent copying are billed like any other minute, so plan the copy rather than leaving the instance up “just in case”.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick a destination
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Good for&lt;/th&gt;
&lt;th&gt;Command family&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Your own computer&lt;/td&gt;
&lt;td&gt;Small to medium outputs, anything private&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;rsync&lt;/code&gt; / &lt;code&gt;scp&lt;/code&gt; over SSH&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hugging Face Hub (private repo)&lt;/td&gt;
&lt;td&gt;LoRA adapters, merged models, datasets you will reuse&lt;/td&gt;
&lt;td&gt;&lt;code&gt;huggingface-cli upload&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3-compatible object storage (S3, R2, B2…)&lt;/td&gt;
&lt;td&gt;Large checkpoints, image batches, logs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rclone copy&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JupyterLab download button&lt;/td&gt;
&lt;td&gt;A handful of small files&lt;/td&gt;
&lt;td&gt;Browser&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; push large files through the instance’s public API endpoint: the tunnel is capped at 3 MB/s and is meant for API responses, not file transfer. Use SSH or an upload from the machine.&lt;/p&gt;

&lt;p&gt;Outbound speed from the supplier data centre to a given destination varies. Test with a small file first and add the copy time to your budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Commands
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Pull with rsync from your laptop (resumable)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# run on your laptop; user, host and port are on the instance page&lt;/span&gt;
rsync &lt;span class="nt"&gt;-avP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &amp;lt;ssh-port&amp;gt;"&lt;/span&gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/out/ ./run-2026-10-23/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-P&lt;/code&gt; shows progress and resumes partial files if the connection drops. Re-run the same command at the end; rsync only copies what changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Keep pulling during a long run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# laptop: pull new checkpoints every 10 minutes while training runs&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;rsync &lt;span class="nt"&gt;-aP&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"ssh -p &amp;lt;ssh-port&amp;gt;"&lt;/span&gt; &amp;lt;ssh-user&amp;gt;@&amp;lt;ssh-host&amp;gt;:/workspace/out/ ./run/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;date
  sleep &lt;/span&gt;600
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pair it with frequent saves in your trainer (for LLaMA-Factory, &lt;code&gt;save_steps&lt;/code&gt; in the YAML; for Hugging Face &lt;code&gt;Trainer&lt;/code&gt;, &lt;code&gt;save_steps&lt;/code&gt; and &lt;code&gt;save_total_limit&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Push a LoRA adapter to a private Hugging Face repo
&lt;/h3&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; &lt;span class="nt"&gt;-U&lt;/span&gt; &lt;span class="s2"&gt;"huggingface_hub[cli]"&lt;/span&gt;
huggingface-cli login            &lt;span class="c"&gt;# paste a fine-grained write token&lt;/span&gt;
huggingface-cli upload &amp;lt;your-user&amp;gt;/qwen3-8b-qlora-run1 /workspace/out/qwen3-8b-qlora &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--private&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use a fine-grained token scoped to the one repo and revoke it after the run.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Copy a large output folder to object storage
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://rclone.org/install.sh | bash          &lt;span class="c"&gt;# you are root on the instance; or: apt-get install -y rclone&lt;/span&gt;
rclone config                                       &lt;span class="c"&gt;# add your S3 / R2 / B2 remote&lt;/span&gt;
rclone copy /workspace/out remote:my-bucket/run1 &lt;span class="nt"&gt;--progress&lt;/span&gt; &lt;span class="nt"&gt;--transfers&lt;/span&gt; 8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Tar first when there are many small files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-C&lt;/span&gt; /workspace &lt;span class="nt"&gt;-czf&lt;/span&gt; /workspace/outputs.tgz out/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thousands of small PNGs or log files copy much faster as one archive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pre-destroy checklist
&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;Check&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Training or batch has finished (or you saved a checkpoint)&lt;/td&gt;
&lt;td&gt;Partial runs are lost on destroy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Outputs copied and &lt;strong&gt;verified&lt;/strong&gt; (file count, sizes, or &lt;code&gt;sha256sum&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;A failed copy looks like a successful one until you need it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Configs, scripts and the exact command line saved&lt;/td&gt;
&lt;td&gt;So you can reproduce the run on a fresh instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Environment captured (&lt;code&gt;pip freeze &amp;gt; requirements.txt&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Next instance starts clean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Tokens you pasted on the machine revoked&lt;/td&gt;
&lt;td&gt;Good hygiene, even though the disk is deleted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;Click Destroy&lt;/td&gt;
&lt;td&gt;Unused minutes of the pre-authorised hour return to your balance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Budget for the copy, and for the balance
&lt;/h2&gt;

&lt;p&gt;Two numbers keep long runs safe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Balance buffer.&lt;/strong&gt; An instance is destroyed when the balance runs out. For an 8-hour run on a card listed at $0.65/h (RTX 4090 48G, 2026-10-23 23:58 Beijing time), the run itself is $5.20; keep that plus a margin for setup and copying.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy time.&lt;/strong&gt; Estimate &lt;code&gt;size ÷ measured upload speed&lt;/code&gt;, then add it to the job. On a card listed at $0.59/h, 10 extra minutes of copying cost about $0.10.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stock and prices change during the day; check the &lt;a href="https://cloudgpu.app/deploy" rel="noopener noreferrer"&gt;Deploy page&lt;/a&gt;. Billing rules are on &lt;a href="https://cloudgpu.app/gpu-rental-per-minute-billing" rel="noopener noreferrer"&gt;per-minute billing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on where your data goes
&lt;/h2&gt;

&lt;p&gt;If your data must not leave a particular jurisdiction, do not train on it here, and choose your copy destination with the same care.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What happens to my files when I destroy a cloud GPU instance on CloudGPU?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The disk is deleted. Copy anything you need off the machine before you destroy it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is there persistent storage between instances?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not yet. Stopping releases the GPU and the next session starts on a new instance, so keep checkpoints and outputs elsewhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if my balance runs out during training?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The instance is destroyed and its disk deleted. Keep enough balance for the whole run plus a margin, and copy checkpoints off as you go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I download checkpoints from a rented GPU?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use rsync or scp over SSH, or upload from the machine to a model hub or object storage. Do not use the public API endpoint, which is capped at 3 MB/s.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Am I billed while I copy files off?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Billing runs per minute until you destroy the instance, so plan the copy and destroy promptly afterwards.&lt;/p&gt;




&lt;p&gt;If the checkpoints you are saving come from a fine-tune, the &lt;a href="https://cloudgpu.app/rent-gpu-for-lora-fine-tuning" rel="noopener noreferrer"&gt;LoRA and QLoRA fine-tuning page&lt;/a&gt; covers card choice and billing for those runs. &lt;em&gt;Originally published at &lt;a href="https://cloudgpu.app/blog/save-work-before-destroying-cloud-gpu-2026" rel="noopener noreferrer"&gt;cloudgpu.app&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prices are list prices read on 2026-10-23 23:58 Beijing time (UTC+8); current prices on &lt;a href="https://cloudgpu.app/deploy" rel="noopener noreferrer"&gt;/deploy&lt;/a&gt; take precedence.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>gpu</category>
      <category>machinelearning</category>
      <category>linux</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DeepSeek V4.1 Flash in the OpenAI SDK and Aider: change one base URL</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Mon, 26 Oct 2026 08:06:48 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/deepseek-v41-flash-in-the-openai-sdk-and-aider-change-one-base-url-3m64</link>
      <guid>https://dev.to/cloudgpu_app/deepseek-v41-flash-in-the-openai-sdk-and-aider-change-one-base-url-3m64</guid>
      <description>&lt;p&gt;&lt;strong&gt;Short answer:&lt;/strong&gt; if your code already uses the OpenAI SDK, you can call DeepSeek V4.1 Flash by changing two values: set the base URL to &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt; and set the model to &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;. Aider works the same way through its OpenAI-compatible settings.&lt;/p&gt;

&lt;p&gt;Disclosure: I work on CloudGPU, and the endpoint below is ours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three DeepSeek IDs, and which is which
&lt;/h2&gt;

&lt;p&gt;The CloudGPU API page lists three DeepSeek chat models (checked 2026-10-26):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model ID&lt;/th&gt;
&lt;th&gt;What it is&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4.1 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-flash&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Flash, served from the April checkpoint, not V4.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;deepseek-v4-pro&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DeepSeek V4 Pro, for harder reasoning and long multi-step tool use&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;IDs are case-sensitive and the dot matters: a wrong ID comes back as &lt;code&gt;400 model_not_found&lt;/code&gt;. Per-token rates change, so this post doesn't repeat them; the current rate for each model is on the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg04_codingagent&amp;amp;utm_content=devto_deepseek_v41_flash" rel="noopener noreferrer"&gt;CloudGPU API page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Sign up with an email address or a Google account.&lt;/li&gt;
&lt;li&gt;Add balance by card, Alipay, Google Pay, Apple Pay or USDT. The same balance pays for GPU rentals too.&lt;/li&gt;
&lt;li&gt;Create a key in the API console. It starts with &lt;code&gt;cgw-sk-&lt;/code&gt; and is shown only once, so put it straight into your environment:
&lt;/li&gt;
&lt;/ol&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;CGW_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgw-sk-..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Smoke test with curl
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/chat/completions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"model":"deepseek-v4.1-flash","messages":[{"role":"user","content":"hello"}]}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To list the model IDs your key can use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without the &lt;code&gt;Authorization&lt;/code&gt; header this returns &lt;code&gt;401&lt;/code&gt; with &lt;code&gt;missing_authorization&lt;/code&gt;, so check the key first if you see that.&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI Python SDK
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# pip install openai
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;api_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;CGW_API_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;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://cloudgpu.app/v1&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;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-v4.1-flash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&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;role&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;user&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;Explain the KV cache in one sentence.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;
    &lt;span class="n"&gt;stream&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="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;delta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To compare the three DeepSeek models on the same prompt, loop over the IDs and keep everything else the same:&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="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="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deepseek-v4.1-flash&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;deepseek-v4-flash&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;deepseek-v4-pro&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&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;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;messages&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;role&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;user&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;Write a Python debounce function.&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="nf"&gt;print&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;--- &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="se"&gt;\n&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;choices&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;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you later switch the same loop to a thinking model such as &lt;code&gt;glm-5.3-flash&lt;/code&gt;, don't set &lt;code&gt;max_tokens&lt;/code&gt; too low: reasoning tokens count toward it, and with a small limit the visible &lt;code&gt;content&lt;/code&gt; can come back empty with &lt;code&gt;finish_reason="length"&lt;/code&gt;. Raise the limit or pass &lt;code&gt;reasoning_effort="low"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// npm install openai; name the file index.mjs (top-level await needs an ES module)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CGW_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://cloudgpu.app/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deepseek-v4.1-flash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Write a TypeScript debounce function.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;choices&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="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tested 2026-10-26: with the &lt;code&gt;openai&lt;/code&gt; npm package, this returns normally for &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aider
&lt;/h2&gt;

&lt;p&gt;According to the official Aider docs ("OpenAI compatible APIs", read 2026-10-26), Aider connects to an OpenAI-compatible endpoint through two environment variables, and the model name gets an &lt;code&gt;openai/&lt;/code&gt; prefix:&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;# Mac/Linux&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://cloudgpu.app/v1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OPENAI_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /path/to/your/project
aider &lt;span class="nt"&gt;--model&lt;/span&gt; openai/deepseek-v4.1-flash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight batchfile"&gt;&lt;code&gt;&lt;span class="c"&gt;REM Windows, per the Aider docs (not part of our test): restart the shell after setx&lt;/span&gt;
&lt;span class="nb"&gt;setx&lt;/span&gt; &lt;span class="kd"&gt;OPENAI_API_BASE&lt;/span&gt; &lt;span class="kd"&gt;https&lt;/span&gt;://cloudgpu.app/v1
&lt;span class="nb"&gt;setx&lt;/span&gt; &lt;span class="kd"&gt;OPENAI_API_KEY&lt;/span&gt; &lt;span class="nv"&gt;%CGW_API_KEY%&lt;/span&gt;
&lt;span class="kd"&gt;aider&lt;/span&gt; &lt;span class="na"&gt;--model &lt;/span&gt;&lt;span class="kd"&gt;openai&lt;/span&gt;&lt;span class="na"&gt;/deepseek-v&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;.1&lt;span class="na"&gt;-flash
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tested 2026-10-26 with Aider 0.86.2: started with &lt;code&gt;--model openai/deepseek-v4.1-flash&lt;/code&gt; and asked to fix a bug in a small &lt;code&gt;add()&lt;/code&gt; function, Aider chose the &lt;code&gt;whole&lt;/code&gt; edit format by itself, printed "Applied edit", and the fix was correct. &lt;code&gt;openai/deepseek-v4-flash&lt;/code&gt; passed the same test, again with &lt;code&gt;whole&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On startup Aider prints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Warning for openai/deepseek-v4.1-flash: Unknown context window size and costs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The warning doesn't affect use. To skip it, add &lt;code&gt;--no-show-model-warnings&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aider &lt;span class="nt"&gt;--model&lt;/span&gt; openai/deepseek-v4.1-flash &lt;span class="nt"&gt;--no-show-model-warnings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Limits to know
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;100 requests per minute per API key and 10,000 requests per day per account. These are public-beta defaults and can be raised on request (API docs).&lt;/li&gt;
&lt;li&gt;Embeddings are not available yet.&lt;/li&gt;
&lt;li&gt;The API is in public beta with no SLA.&lt;/li&gt;
&lt;li&gt;You pay per token from a prepaid balance, with no subscription.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Does DeepSeek V4.1 Flash work with the OpenAI SDK?&lt;/strong&gt;&lt;br&gt;
Yes. Set &lt;code&gt;base_url&lt;/code&gt; to &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, use a &lt;code&gt;cgw-sk-&lt;/code&gt; key and set the model to &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is &lt;code&gt;deepseek-v4-flash&lt;/code&gt; the same as &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
No. &lt;code&gt;deepseek-v4-flash&lt;/code&gt; is served from the April V4 Flash checkpoint; &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt; is the V4.1 Flash model. Both are separate IDs on the API page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do I use it in Aider?&lt;/strong&gt;&lt;br&gt;
Set &lt;code&gt;OPENAI_API_BASE&lt;/code&gt; and &lt;code&gt;OPENAI_API_KEY&lt;/code&gt;, then start Aider with &lt;code&gt;--model openai/deepseek-v4.1-flash&lt;/code&gt;, as the Aider docs describe for OpenAI-compatible APIs. Tested 2026-10-26 with Aider 0.86.2 (it chose the &lt;code&gt;whole&lt;/code&gt; edit format by itself).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where are the current rates?&lt;/strong&gt;&lt;br&gt;
On the CloudGPU API page. Rates can change, so check there before a large run.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adapted from "DeepSeek V4 API With USDT or Card: Use the OpenAI SDK" on the CloudGPU blog.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I work on CloudGPU.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>deepseek</category>
      <category>llm</category>
      <category>python</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Use DeepSeek V4.1 Flash and Kimi in opencode via a custom OpenAI-compatible provider</title>
      <dc:creator>CloudGPU</dc:creator>
      <pubDate>Sun, 25 Oct 2026 22:22:44 +0000</pubDate>
      <link>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-and-kimi-in-opencode-via-a-custom-openai-compatible-provider-5co4</link>
      <guid>https://dev.to/cloudgpu_app/use-deepseek-v41-flash-and-kimi-in-opencode-via-a-custom-openai-compatible-provider-5co4</guid>
      <description>&lt;p&gt;opencode ships with a long list of built-in providers, but you are not limited to them. Its docs describe a &lt;strong&gt;custom provider&lt;/strong&gt; path for any OpenAI-compatible endpoint: you declare the provider in &lt;code&gt;opencode.json&lt;/code&gt;, point it at a base URL, list the model IDs, and they show up in the model picker.&lt;/p&gt;

&lt;p&gt;This post walks through that setup with two models, &lt;strong&gt;DeepSeek V4.1 Flash&lt;/strong&gt; (&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;) and &lt;strong&gt;Kimi K3&lt;/strong&gt; (&lt;code&gt;kimi-k3&lt;/code&gt;), plus two extras in the same config: &lt;code&gt;kimi-k2.6&lt;/code&gt; and &lt;code&gt;glm-5.3-flash&lt;/code&gt;. The endpoint in the examples is CloudGPU's OpenAI-compatible API at &lt;code&gt;https://cloudgpu.app/v1&lt;/code&gt;, but the same steps apply to any provider that speaks &lt;code&gt;/v1/chat/completions&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;What's tested and what isn't:&lt;/strong&gt; on 2026-10-26 we ran this setup end to end in opencode 1.19.4. The &lt;code&gt;opencode.json&lt;/code&gt; below loaded as written with no schema errors, &lt;code&gt;opencode models cloudgpu&lt;/code&gt; listed all four models, and &lt;code&gt;opencode run -m ...&lt;/code&gt; returned a reply from both &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt; and &lt;code&gt;kimi-k3&lt;/code&gt;. All four model IDs were also listed by &lt;code&gt;GET /v1/models&lt;/code&gt;, and a direct chat request to each one replied normally. Not yet verified: the interactive &lt;code&gt;/models&lt;/code&gt; screen in the TUI, the &lt;code&gt;/connect&lt;/code&gt; key flow, and how &lt;code&gt;limit.output&lt;/code&gt; maps onto requests. Those parts follow opencode's official docs, so read &lt;em&gt;"according to the docs"&lt;/em&gt; literally.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;opencode installed (see the &lt;a href="https://opencode.ai/docs/" rel="noopener noreferrer"&gt;opencode docs&lt;/a&gt; for install options).&lt;/li&gt;
&lt;li&gt;An API key for your OpenAI-compatible endpoint. For CloudGPU, keys are created on the API page and look like &lt;code&gt;cgw-sk-...&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The exact model IDs your endpoint serves. Here: &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;, &lt;code&gt;kimi-k3&lt;/code&gt;, &lt;code&gt;kimi-k2.6&lt;/code&gt;, &lt;code&gt;glm-5.3-flash&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Export your key
&lt;/h2&gt;

&lt;p&gt;opencode's config supports &lt;code&gt;{env:VARIABLE_NAME}&lt;/code&gt; substitution (&lt;a href="https://opencode.ai/docs/config#env-vars" rel="noopener noreferrer"&gt;Config → Env vars&lt;/a&gt;), so the key never has to live in the JSON file:&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;CGW_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cgw-sk-..."&lt;/span&gt;   &lt;span class="c"&gt;# add to ~/.bashrc / ~/.zshrc to keep it across shells&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2: Add the provider to &lt;code&gt;opencode.json&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Create (or edit) &lt;code&gt;opencode.json&lt;/code&gt; in your project root. Per the docs, project config takes precedence over the global file at &lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;, and config files are merged, so you can also put the &lt;code&gt;provider&lt;/code&gt; block in the global file if you want it in every project.&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://opencode.ai/config.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provider"&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;"cloudgpu"&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;"npm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@ai-sdk/openai-compatible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CloudGPU"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"options"&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;"baseURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://cloudgpu.app/v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"apiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{env:CGW_API_KEY}"&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;"models"&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;"deepseek-v4.1-flash"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DeepSeek V4.1 Flash"&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;"kimi-k3"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kimi K3"&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;"kimi-k2.6"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Kimi K2.6"&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;"glm-5.3-flash"&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GLM-5.3 Flash"&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="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="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;What each field does, per opencode's Providers docs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cloudgpu&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The provider ID. You pick it; it becomes the first half of the model reference (&lt;code&gt;cloudgpu/&amp;lt;model&amp;gt;&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The AI SDK package. &lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt; is the one the docs name for OpenAI-compatible providers that use &lt;code&gt;/v1/chat/completions&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Display name in the UI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;options.baseURL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The API endpoint. Note the &lt;code&gt;/v1&lt;/code&gt; at the end.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;options.apiKey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional; set via &lt;code&gt;{env:...}&lt;/code&gt; syntax. If you would rather store the key with &lt;code&gt;/connect&lt;/code&gt; (see below), you can drop this line.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;models&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The model IDs to expose. Each &lt;strong&gt;key&lt;/strong&gt; must match the ID the endpoint expects; &lt;code&gt;name&lt;/code&gt; is only the label.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Only need the two headline models? Delete the &lt;code&gt;kimi-k2.6&lt;/code&gt; and &lt;code&gt;glm-5.3-flash&lt;/code&gt; entries; nothing else changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative: store the key with &lt;code&gt;/connect&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The docs also describe a credential flow: run &lt;code&gt;/connect&lt;/code&gt; inside opencode, scroll to &lt;strong&gt;Other&lt;/strong&gt;, enter the provider ID (&lt;code&gt;cloudgpu&lt;/code&gt;, matching the key in &lt;code&gt;opencode.json&lt;/code&gt;), then paste your key. That stores the credential only; you still need the &lt;code&gt;provider&lt;/code&gt; block above. Use one approach or the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Pick the model
&lt;/h2&gt;

&lt;p&gt;Start opencode in the project and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/models
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;According to the docs, your custom provider and its models appear in this selection list. Choose &lt;strong&gt;DeepSeek V4.1 Flash&lt;/strong&gt; or &lt;strong&gt;Kimi K3&lt;/strong&gt;. (In our test we checked the list from the command line, not this screen.)&lt;/p&gt;

&lt;p&gt;To check from the command line that opencode picked up the provider, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode models cloudgpu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In our test with opencode 1.19.4, this listed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cloudgpu/deepseek-v4.1-flash
cloudgpu/glm-5.3-flash
cloudgpu/kimi-k2.6
cloudgpu/kimi-k3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To choose a model from the command line instead, use &lt;code&gt;--model&lt;/code&gt; / &lt;code&gt;-m&lt;/code&gt; in &lt;code&gt;provider/model&lt;/code&gt; form. In our test, &lt;code&gt;-m cloudgpu/deepseek-v4.1-flash&lt;/code&gt; (the dot in the ID is fine) and &lt;code&gt;-m cloudgpu/kimi-k3&lt;/code&gt; both returned replies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;opencode &lt;span class="nt"&gt;-m&lt;/span&gt; cloudgpu/deepseek-v4.1-flash
opencode run &lt;span class="nt"&gt;-m&lt;/span&gt; cloudgpu/kimi-k3 &lt;span class="s2"&gt;"Explain what this repo's main entry point does"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set a default in &lt;code&gt;opencode.json&lt;/code&gt; with the top-level &lt;code&gt;"model"&lt;/code&gt; key, using the same &lt;code&gt;provider_id/model_id&lt;/code&gt; form, for example &lt;code&gt;"model": "cloudgpu/deepseek-v4.1-flash"&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one for what?
&lt;/h2&gt;

&lt;p&gt;I'm not putting benchmark numbers here; try both on your own repo. A practical split:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;&lt;/strong&gt;: a sensible default for everyday edits, explanations, and small refactors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kimi-k3&lt;/code&gt;&lt;/strong&gt;: a reasoning model; switch to it with &lt;code&gt;/models&lt;/code&gt; for multi-step changes or a second opinion on the same task. It spends output tokens on reasoning before it answers (see troubleshooting #4).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;kimi-k2.6&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;glm-5.3-flash&lt;/code&gt;&lt;/strong&gt;: already in the config, so comparing is one &lt;code&gt;/models&lt;/code&gt; switch away.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;This section is based on a real question we answered: someone added a DeepSeek model as a custom provider in a coding agent, entered the endpoint, key, and two models, and &lt;strong&gt;the model never showed up in the picker&lt;/strong&gt;. The model ID they had entered was a short alias, not the exact ID the endpoint lists. We can't say for sure that was the cause in their case, but a short alias like that is a common cause, and the same checklist applies to opencode.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The model doesn't appear in &lt;code&gt;/models&lt;/code&gt; (or requests fail with "model not found")
&lt;/h3&gt;

&lt;p&gt;The keys under &lt;code&gt;models&lt;/code&gt; must match, character for character, the IDs your endpoint returns from &lt;code&gt;GET /v1/models&lt;/code&gt;. Check them directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://cloudgpu.app/v1/models &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$CGW_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the &lt;code&gt;id&lt;/code&gt; values from the response into &lt;code&gt;opencode.json&lt;/code&gt;. Common mismatches: a short alias (&lt;code&gt;deepseek-flash&lt;/code&gt;), a missing version segment (&lt;code&gt;deepseek-v4-flash&lt;/code&gt; vs &lt;code&gt;deepseek-v4.1-flash&lt;/code&gt;), or different capitalization.&lt;/p&gt;

&lt;p&gt;Also confirm the provider ID matches everywhere: the key under &lt;code&gt;provider&lt;/code&gt;, the ID you typed in &lt;code&gt;/connect&lt;/code&gt; (if you used it), and the prefix in &lt;code&gt;-m cloudgpu/...&lt;/code&gt;. opencode's own troubleshooting list calls out the &lt;code&gt;/connect&lt;/code&gt; ID mismatch, and &lt;code&gt;opencode auth list&lt;/code&gt; shows which credentials are stored.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The base URL is missing &lt;code&gt;/v1&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@ai-sdk/openai-compatible&lt;/code&gt; appends paths like &lt;code&gt;/chat/completions&lt;/code&gt; to &lt;code&gt;baseURL&lt;/code&gt;. Use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://cloudgpu.app/v1      ✅
https://cloudgpu.app         ❌
https://cloudgpu.app/v1/chat/completions   ❌ (too specific)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The environment variable isn't exported
&lt;/h3&gt;

&lt;p&gt;The docs note that if a variable referenced with &lt;code&gt;{env:...}&lt;/code&gt; is not set, it is replaced with an &lt;strong&gt;empty string&lt;/strong&gt;, so no key is sent and the request is rejected. In our test (opencode 1.19.4, &lt;code&gt;CGW_API_KEY&lt;/code&gt; empty) the error was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Provide an Authorization: Bearer &amp;lt;api_key&amp;gt; header.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check the variable in the same shell you launch opencode from:&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CGW_API_KEY&lt;/span&gt;:+set&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# prints "set" if the variable is present&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you set it in &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.bashrc&lt;/code&gt;, open a new terminal (or &lt;code&gt;source&lt;/code&gt; the file) before starting opencode. Desktop launchers and IDE terminals may not read your shell profile.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Empty replies from reasoning models
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;kimi-k3&lt;/code&gt;, &lt;code&gt;kimi-k2.6&lt;/code&gt;, and &lt;code&gt;glm-5.3-flash&lt;/code&gt; reason before they answer, and the reasoning counts against the output limit. In our 2026-10-26 test against the API directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;with &lt;code&gt;max_tokens: 20&lt;/code&gt;, all three returned &lt;strong&gt;empty or &lt;code&gt;null&lt;/code&gt; content&lt;/strong&gt;, because reasoning used up the tokens first;&lt;/li&gt;
&lt;li&gt;with &lt;code&gt;max_tokens: 800&lt;/code&gt;, all three replied normally;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kimi-k3&lt;/code&gt; used &lt;strong&gt;1027 completion tokens&lt;/strong&gt; to answer "ok".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;If the reply is empty, raise &lt;code&gt;max_tokens&lt;/code&gt; or leave it unset.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In opencode, the per-model knob the docs describe for custom providers is the optional &lt;code&gt;limit&lt;/code&gt; block (&lt;code&gt;limit.context&lt;/code&gt;, &lt;code&gt;limit.output&lt;/code&gt;). If you set &lt;code&gt;limit.output&lt;/code&gt; low, or a tool or agent config passes a small output cap, you may hit the same empty-reply behavior. If you add &lt;code&gt;limit&lt;/code&gt;, use real values from your provider's model docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"kimi-k3": {
  "name": "Kimi K3",
  "limit": { "context": &amp;lt;context tokens&amp;gt;, "output": &amp;lt;max output tokens&amp;gt; }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Replace the placeholders with numbers; the snippet is not valid JSON as written.) How opencode maps &lt;code&gt;limit.output&lt;/code&gt; onto the request is per its docs; I haven't verified it against these models inside opencode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Billing
&lt;/h2&gt;

&lt;p&gt;CloudGPU's API runs on a prepaid balance (card, Alipay, Google Pay, Apple Pay, or USDT). The model list and API key setup are on the &lt;a href="https://cloudgpu.app/api?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=202611_seg04_codingagent&amp;amp;utm_content=devto_opencode_custom_provider" rel="noopener noreferrer"&gt;CloudGPU API page&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;export CGW_API_KEY=...&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;provider&lt;/code&gt; block with &lt;code&gt;"npm": "@ai-sdk/openai-compatible"&lt;/code&gt;, &lt;code&gt;"baseURL": "https://cloudgpu.app/v1"&lt;/code&gt;, and &lt;code&gt;"apiKey": "{env:CGW_API_KEY}"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;List exact model IDs under &lt;code&gt;models&lt;/code&gt;, verified against &lt;code&gt;GET /v1/models&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;opencode models cloudgpu&lt;/code&gt; to confirm the models are listed, then pick &lt;code&gt;cloudgpu/deepseek-v4.1-flash&lt;/code&gt; or &lt;code&gt;cloudgpu/kimi-k3&lt;/code&gt; with &lt;code&gt;/models&lt;/code&gt; or &lt;code&gt;-m&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Empty reply from a reasoning model? Raise &lt;code&gt;max_tokens&lt;/code&gt; or leave it unset.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Docs used: &lt;a href="https://opencode.ai/docs/providers#custom" rel="noopener noreferrer"&gt;opencode Providers → Custom provider&lt;/a&gt;, &lt;a href="https://opencode.ai/docs/config#env-vars" rel="noopener noreferrer"&gt;opencode Config → Env vars&lt;/a&gt;, &lt;a href="https://opencode.ai/docs/models" rel="noopener noreferrer"&gt;opencode Models&lt;/a&gt;, &lt;a href="https://opencode.ai/docs/cli" rel="noopener noreferrer"&gt;opencode CLI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I work on CloudGPU.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opencode</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
