<?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: xuks124</title>
    <description>The latest articles on DEV Community by xuks124 (@xuks124).</description>
    <link>https://dev.to/xuks124</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3895811%2F734594b4-460b-4961-83cc-64b938264b1a.png</url>
      <title>DEV Community: xuks124</title>
      <link>https://dev.to/xuks124</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/xuks124"/>
    <language>en</language>
    <item>
      <title>Build a Markdown Translation Tool with AI APIs in 50 Lines of Python</title>
      <dc:creator>xuks124</dc:creator>
      <pubDate>Fri, 24 Apr 2026 10:35:17 +0000</pubDate>
      <link>https://dev.to/xuks124/build-a-markdown-translation-tool-with-ai-apis-in-50-lines-of-python-4lme</link>
      <guid>https://dev.to/xuks124/build-a-markdown-translation-tool-with-ai-apis-in-50-lines-of-python-4lme</guid>
      <description>&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;I write documentation in Chinese, but my readers are everywhere. Manually translating each &lt;code&gt;.md&lt;/code&gt; file was killing my productivity. Worse, keeping translations in sync with updates was a nightmare.&lt;/p&gt;

&lt;p&gt;I needed a tool that could batch-translate entire documentation folders while preserving every &lt;code&gt;# header&lt;/code&gt;, &lt;code&gt;**bold**&lt;/code&gt;, &lt;code&gt;`code`&lt;/code&gt;, and &lt;code&gt;[link]()&lt;/code&gt; perfectly. So I built one.&lt;/p&gt;

&lt;p&gt;The result is &lt;a href="https://github.com/xuks124/md-translator" rel="noopener noreferrer"&gt;md-translator&lt;/a&gt; — a lightweight Python script that uses any OpenAI-compatible API to translate Markdown files in bulk. It's under 200 lines, supports concurrent processing, and costs pennies per project.&lt;/p&gt;

&lt;p&gt;Let me show you how it works.&lt;/p&gt;




&lt;h2&gt;
  
  
  What You'll Build
&lt;/h2&gt;

&lt;p&gt;A CLI tool that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Batch translates all &lt;code&gt;.md&lt;/code&gt; files in a directory&lt;/li&gt;
&lt;li&gt;Supports any OpenAI-compatible API (DeepSeek, GPT, Qwen, etc.)&lt;/li&gt;
&lt;li&gt;Preserves every bit of Markdown formatting&lt;/li&gt;
&lt;li&gt;Caches translations for resume support&lt;/li&gt;
&lt;li&gt;Runs concurrent translations with configurable workers&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;

&lt;p&gt;Requires Python 3.8+ and one dependency:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
git clone https://github.com/xuks124/md-translator.git
&lt;span class="nb"&gt;cd &lt;/span&gt;md-translator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set your API key:&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;MD_TRANSLATOR_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-your-api-key-here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Core Translation Logic
&lt;/h2&gt;

&lt;p&gt;The core is an API call to any OpenAI-compatible chat endpoint. Here's the key function:&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;def&lt;/span&gt; &lt;span class="nf"&gt;translate_chunk&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;source_lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_url&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;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a professional translator. Translate from &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source_lang&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; to &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target_lang&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.
Rules:
1. Keep ALL Markdown syntax unchanged (```
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;endraw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;
, **, [], ![], #, -, etc.)
2. Only translate text content
3. Keep code blocks and URLs unchanged
4. Return ONLY the translated content

Content:
---
&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;chunk&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="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Authorization&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Bearer &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&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;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="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.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;60&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="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&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;message&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="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The prompt is the secret sauce. By telling the model to keep syntax unchanged, we get clean Markdown out every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Batch Processing with Resume Support
&lt;/h2&gt;

&lt;p&gt;Translation can take time, so the tool caches results by file hash. If you rerun it, already-translated files are skipped instantly:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
def process_file(md_path, source_lang, target_lang, api_key, api_url, model, output_dir, force):
    content = open(md_path, encoding='utf-8').read()
    content_hash = hashlib.md5(content.encode()).hexdigest()[:8]

    # Load cache — resume from breakpoint
    cache_file = Path(md_path.parent / '.md_translator_cache' / f"{md_path.stem}.json")
    cache = json.load(open(cache_file)) if cache_file.exists() else {}

    if not force and content_hash in cache:
        translated = cache[content_hash]  # Cache hit!
    else:
        # Split long files into chunks
        if len(content) &amp;gt; 4000:
            chunks = [content[i:i+3000] for i in range(0, len(content), 3000)]
            translated = '\n'.join(translate_chunk(c, ...) for c in chunks)
        else:
            translated = translate_chunk(content, ...)

        cache[content_hash] = translated
        json.dump(cache, open(cache_file, 'w'))

    # Output: suffix-based naming (e.g., doc.en.md)
    output_path = md_path.parent / md_path.name.replace('.md', f'.{target_lang}.md')
    open(output_path, 'w', encoding='utf-8').write(translated)


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For large documentation sites, the tool uses &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to translate multiple files concurrently:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
python
with ThreadPoolExecutor(max_workers=args.workers) as executor:
    futures = {executor.submit(process_file, f, ...): f for f in md_files}
    for future in as_completed(futures):
        output = future.result()


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;h2&gt;
  
  
  Running It
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
bash
# Translate all files in ./docs from Chinese to English
python translate.py --input ./docs --source zh --target en

# Use GPT-4o instead of default DeepSeek
python translate.py -i ./docs -s zh -t en -m gpt-4o -u https://api.openai.com/v1

# Force re-translate everything
python translate.py -i ./docs -s zh -t en -f

# 5 concurrent workers for big projects
python translate.py -i ./docs -s zh -t en -w 5 -o ./translations


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why OpenAI-Compatible APIs?
&lt;/h2&gt;

&lt;p&gt;Lock-in is annoying. This tool works with any provider that speaks the OpenAI chat format:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;DeepSeek&lt;/strong&gt; (free tier available)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenAI&lt;/strong&gt; (GPT-4o / GPT-4o-mini)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-API&lt;/strong&gt; (self-hosted unified gateway)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Qwen / Moonshot / Groq&lt;/strong&gt; (all OpenAI-compatible)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just swap &lt;code&gt;--api-url&lt;/code&gt; and &lt;code&gt;--model&lt;/code&gt; — no code changes needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Usage
&lt;/h2&gt;

&lt;p&gt;I use this to keep my &lt;a href="https://github.com/xuks124/md-translator" rel="noopener noreferrer"&gt;programming handbook&lt;/a&gt; in 3 languages. A full translation of 400+ files runs in about 15 minutes and costs less than $2 with DeepSeek.&lt;/p&gt;

&lt;p&gt;Format preservation is the killer feature — tables, code blocks with syntax highlighting, nested lists, embedded images — everything stays intact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The full source is on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/xuks124/md-translator" rel="noopener noreferrer"&gt;https://github.com/xuks124/md-translator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's MIT licensed, so fork it, tweak it, use it for your docs. If you build something cool with it, drop a star or open an issue.&lt;/p&gt;




&lt;p&gt;Happy translating!&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Building an AI API Gateway with One-API: A Practical Guide</title>
      <dc:creator>xuks124</dc:creator>
      <pubDate>Fri, 24 Apr 2026 10:32:29 +0000</pubDate>
      <link>https://dev.to/xuks124/building-an-ai-api-gateway-with-one-api-a-practical-guide-3232</link>
      <guid>https://dev.to/xuks124/building-an-ai-api-gateway-with-one-api-a-practical-guide-3232</guid>
      <description>&lt;h1&gt;
  
  
  Building an AI API Gateway with One-API: A Practical Guide
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Why You Need an AI API Gateway
&lt;/h2&gt;

&lt;p&gt;It's 2026. Every AI company ships its own API — OpenAI, Claude, Gemini, DeepSeek, Qwen... If you're building anything real, you're likely juggling 3+ different API keys, billing dashboards, and SDKs.&lt;/p&gt;

&lt;p&gt;One-API is an open-source unified gateway that solves this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single endpoint for all major LLM providers&lt;/li&gt;
&lt;li&gt;Load balancing and failover between models&lt;/li&gt;
&lt;li&gt;Token quota management per user/group&lt;/li&gt;
&lt;li&gt;Usage analytics and cost tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setup in Under 5 Minutes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Docker Deployment
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull justsong/one-api
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--restart&lt;/span&gt; always &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; one-api &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 3000:3000 &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-v&lt;/span&gt; /data/one-api:/data &lt;span class="se"&gt;\\&lt;/span&gt;
  justsong/one-api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Visit &lt;code&gt;http://your-server:3000&lt;/code&gt;. Default login: &lt;code&gt;root / 123456&lt;/code&gt; — change this immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Your First Channel (Aliyun Bailian Example)
&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;# Login&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/user/login &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&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;'{"username":"root", "password": "***"}'&lt;/span&gt;

&lt;span class="c"&gt;# Add channel&lt;/span&gt;
curl http://localhost:3000/api/channel/ &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s1"&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;'{
    "name": "Aliyun Bailian",
    "type": 41,
    "key": "sk-your-bailian-key",
    "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
    "models": "qwen-plus,qwen-turbo"
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: One-API v0.12.x has a bug where POST to &lt;code&gt;/api/channel/&lt;/code&gt; panics. Use PUT to update an existing channel instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Client Usage
&lt;/h3&gt;

&lt;p&gt;Once configured, all models are accessed via a single OpenAI-compatible endpoint:&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;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;your-one-api-token&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;http://your-server:3000/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;response&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;qwen-plus&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;Hello&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;response&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;
  
  
  Essential Security Hardening
&lt;/h2&gt;

&lt;p&gt;After deployment, do this immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Disable open registration&lt;/strong&gt; — turn off public signups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable CORS restrictions&lt;/strong&gt; — don't leave &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiting&lt;/strong&gt; — set 60 req/min per user as a baseline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTPS&lt;/strong&gt; — put Nginx in front with a Let's Encrypt cert&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regular admin password rotation&lt;/strong&gt; — use strong 16+ char passwords&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Cost Optimization Strategy
&lt;/h2&gt;

&lt;p&gt;With a unified gateway, you can route tasks to the cheapest adequate model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chat/QA&lt;/strong&gt;: qwen-turbo or deepseek-chat ($0.02-0.05/1M tokens)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code generation&lt;/strong&gt;: claude-3-haiku or gpt-4o-mini&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex reasoning&lt;/strong&gt;: claude-3.5-sonnet or gpt-4o&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Batch processing&lt;/strong&gt;: route to cheapest model with retry fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Real-world result: 40-70% cost reduction vs. using premium models for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Built This
&lt;/h2&gt;

&lt;p&gt;I needed a way to serve multiple users across my team without giving each one 5 different API keys. One-API handles quotas, tracks usage, and lets me add new model providers in 30 seconds via the dashboard.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Check out my open-source tools: &lt;a href="https://github.com/xuks124/md-translator" rel="noopener noreferrer"&gt;md-translator&lt;/a&gt; — batch translate Markdown files using AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>tutorial</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
