← Back to dashboard
deepseekaifresh

DeepSeek Integration Guide

What is DeepSeek?

The real model

Mixture-of-Experts models, OpenAI-compat API, MIT-licensed weights.

V3 is a 671B-parameter MoE with ~37B active per token; R1 is the reasoning variant that emits visible chain-of-thought before the final answer. The hosted API uses the same `/v1/chat/completions` schema as OpenAI — point your SDK at `https://api.deepseek.com` and most code Just Works. Function calling is supported. Weights are available on Hugging Face under MIT — you can run them yourself on an 8×H100 box, distill them, or fine-tune. For codeAmani, DeepSeek is the cost-optimisation lever: route the cheapest ~80% of traffic to V3, fall back to Claude when quality matters.

Five reasons DeepSeek is worth integrating

Open weights + cheap API + OpenAI-compatible schema = unusually low switching cost.

Text
██████╗ ███████╗███████╗██████╗ ███████╗███████╗███████╗██╗  ██╗
██╔══██╗██╔════╝██╔════╝██╔══██╗██╔════╝██╔════╝██╔════╝██║ ██╔╝
██║  ██║█████╗  █████╗  ██████╔╝███████╗█████╗  █████╗  █████╔╝
██║  ██║██╔══╝  ██╔══╝  ██╔═══╝ ╚════██║██╔══╝  ██╔══╝  ██╔═██╗
██████╔╝███████╗███████╗██║     ███████║███████╗███████╗██║  ██╗
╚═════╝ ╚══════╝╚══════╝╚═╝     ╚══════╝╚══════╝╚══════╝╚═╝  ╚═╝

DeepSeek Integration Guide

Focus: Cost-effective AI inference and chain-of-thought reasoning — DeepSeek-V3 and DeepSeek-R1 via OpenAI-compatible SDK.

Overview

DeepSeek provides two flagship models: DeepSeek-V3 (fast, cost-effective chat/code) and DeepSeek-R1 (step-by-step chain-of-thought reasoning). Both are compatible with the OpenAI SDK — swap the base URL and API key, keep the same code. Used in codeAmani products as a cost-optimization alternative for tasks that don't require Anthropic's highest capability tier.

Here's the big picture — the same OpenAI SDK call simply points at DeepSeek and routes to one of two models:

Official Documentation


SDK Setup

DeepSeek is OpenAI API-compatible — use the official OpenAI SDK with a custom base URL.

Bash
npm install openai
TypeScript
import OpenAI from "openai";

const deepseek = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY!,
  baseURL: "https://api.deepseek.com",
});

Models

Model IDAliasBest ForContext
deepseek-chatDeepSeek-V3Fast chat, code generation, structured output64K tokens
deepseek-reasonerDeepSeek-R1Step-by-step reasoning, math, logic, analysis64K tokens

Core Patterns

Standard Chat Completion

TypeScript
const response = await deepseek.chat.completions.create({
  model: "deepseek-chat",
  messages: [
    { role: "system", content: "You are a helpful assistant for codeAmani Labs." },
    { role: "user", content: "Summarize this M-Pesa transaction log." },
  ],
  max_tokens: 1024,
});

console.log(response.choices[0].message.content);

Reasoning (R1) — Chain-of-Thought

TypeScript
const response = await deepseek.chat.completions.create({
  model: "deepseek-reasoner",
  messages: [
    { role: "user", content: "Why is my Supabase RLS policy blocking authenticated users?" },
  ],
});

// R1 exposes its reasoning in reasoning_content before the final answer
const choice = response.choices[0];
// @ts-expect-error — deepseek-specific field
console.log("Reasoning:", choice.message.reasoning_content);
console.log("Answer:", choice.message.content);

Streaming Response

TypeScript
const stream = await deepseek.chat.completions.create({
  model: "deepseek-chat",
  stream: true,
  messages: [{ role: "user", content: prompt }],
});

for await (const chunk of stream) {
  const delta = chunk.choices[0]?.delta?.content ?? "";
  process.stdout.write(delta);
}

Next.js App Router Streaming Route

TypeScript
// app/api/ai/deepseek/route.ts
import OpenAI from "openai";
import { NextRequest } from "next/server";

const deepseek = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY!,
  baseURL: "https://api.deepseek.com",
});

export async function POST(req: NextRequest) {
  const { messages } = await req.json();

  const stream = await deepseek.chat.completions.create({
    model: "deepseek-chat",
    stream: true,
    messages,
  });

  const encoder = new TextEncoder();
  const readable = new ReadableStream({
    async start(controller) {
      for await (const chunk of stream) {
        const text = chunk.choices[0]?.delta?.content ?? "";
        if (text) controller.enqueue(encoder.encode(text));
      }
      controller.close();
    },
  });

  return new Response(readable, {
    headers: { "Content-Type": "text/plain; charset=utf-8" },
  });
}

AI Routing: When to Use DeepSeek

In codeAmani's AI routing strategy, DeepSeek slots in as a cost-optimization tier:

This decision flow shows exactly where DeepSeek earns its place alongside Anthropic — pick the right tier and you save cost without losing quality:

TypeScript
// lib/ai.ts
type TaskComplexity = "high" | "medium" | "low";

function selectModel(complexity: TaskComplexity, requiresReasoning: boolean) {
  if (complexity === "high" || requiresReasoning) {
    return { provider: "anthropic", model: "claude-sonnet-4-6" };
  }
  if (complexity === "medium") {
    return { provider: "deepseek", model: "deepseek-chat" };
  }
  // Low complexity: fast classification / simple Q&A
  return { provider: "anthropic", model: "claude-haiku-4-5-20251001" };
}
TaskRecommended Model
Complex reasoning, agentsclaude-sonnet-4-6
Step-by-step math / logicdeepseek-reasoner
Standard Q&A, summariesdeepseek-chat
Fast classificationclaude-haiku-4-5-20251001
Structured JSON outputgpt-4o

Error handling, retries & fallback

The routing section above treats DeepSeek as a cost tier, not a hard dependency — so any call that hits DeepSeek must be able to fall back to Anthropic Claude when DeepSeek throttles or errors. DeepSeek's own docs explicitly suggest this: on a 429, they recommend you "temporarily switch to alternative LLM providers."

Documented status codes

These are the status codes DeepSeek documents on its error codes page. Treat the transient ones as retry-then-fallback, and the terminal ones as fail-fast (retrying won't help):

CodeMeaningClassAction
400Invalid request body formatterminalFix the request — do not retry
401Authentication fails (wrong API key)terminalFix DEEPSEEK_API_KEY
402Insufficient balanceterminalTop up; fall back immediately
422Invalid parametersterminalFix params — do not retry
429Rate limit reached (concurrency limit)transientBack off, then fall back
500Server errortransientRetry after a brief wait
503Server overloaded (high traffic)transientRetry after a brief wait

DeepSeek does not publish a fixed requests-per-second limit. Instead it documents a per-user_id concurrency limit (rate limit docs); exceeding the number of in-flight connections is what returns 429. The docs give no prescribed backoff schedule, so the pattern below uses standard exponential backoff with jitter.

Try DeepSeek with backoff, then fall back to Claude

This mirrors the router in lib/ai.tsselectModel chooses the tier, this wrapper makes the DeepSeek tier resilient. Terminal errors (4xx except 429) skip retries and fall straight through to Claude.

TypeScript
// lib/ai-resilient.ts
import OpenAI from "openai";
import Anthropic from "@anthropic-ai/sdk";

const deepseek = new OpenAI({
  apiKey: process.env.DEEPSEEK_API_KEY!,
  baseURL: "https://api.deepseek.com",
});

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });

// Transient per DeepSeek docs: 429 (rate limit), 500 (server error), 503 (overloaded).
const RETRYABLE = new Set([429, 500, 503]);
const MAX_RETRIES = 3;

const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));

/**
 * Run a prompt through DeepSeek with exponential backoff on transient errors,
 * then fall back to Anthropic Claude if DeepSeek is unavailable or non-retryable.
 */
export async function completeWithFallback(prompt: string): Promise<string> {
  for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
    try {
      const res = await deepseek.chat.completions.create({
        model: "deepseek-chat",
        messages: [{ role: "user", content: prompt }],
        max_tokens: 2048,
      });
      return res.choices[0]?.message.content ?? "";
    } catch (err) {
      // OpenAI SDK surfaces the HTTP status on err.status
      const status = (err as { status?: number }).status;

      // Non-retryable (400/401/402/422) or out of attempts → break to fallback.
      if (!status || !RETRYABLE.has(status) || attempt === MAX_RETRIES) break;

      // Exponential backoff with full jitter: ~0.5s, 1s, 2s (+ jitter).
      const base = 500 * 2 ** attempt;
      await sleep(base + Math.random() * base);
    }
  }

  // Fallback tier — Anthropic Claude (consistent with lib/ai.ts router).
  const msg = await anthropic.messages.create({
    model: "claude-haiku-4-5-20251001",
    max_tokens: 2048,
    messages: [{ role: "user", content: prompt }],
  });
  const block = msg.content[0];
  return block.type === "text" ? block.text : "";
}

Gotcha — empty lines are not errors. While a request waits to be scheduled, DeepSeek keeps the TCP connection alive by sending empty lines (non-streaming) or : keep-alive SSE comments (streaming) rather than data. The OpenAI SDK handles these for you, but if you parse the raw HTTP/SSE stream yourself, skip those blank/comment lines — do not treat them as a malformed response or trip your retry logic on them. Connections also close after ~10 minutes if inference never starts, so set a client timeout below that and let the fallback path catch it.


JSON / Structured Output

TypeScript
const response = await deepseek.chat.completions.create({
  model: "deepseek-chat",
  response_format: { type: "json_object" },
  messages: [
    {
      role: "system",
      content: "Respond only with valid JSON.",
    },
    {
      role: "user",
      content: "Extract: name, amount, phone from this SMS: 'Confirmed. Ksh500 sent to 0712345678 on 12/5/26'",
    },
  ],
});

const data = JSON.parse(response.choices[0].message.content ?? "{}");
// { name: null, amount: 500, phone: "0712345678" }

Environment Variables

Bash
# Required
DEEPSEEK_API_KEY=sk-...

# No separate base URL needed — set in code: https://api.deepseek.com

Cost Reference

DeepSeek is significantly cheaper than GPT-4o / Claude for many tasks. Check current pricing at platform.deepseek.com — rates change frequently.

ModelApprox InputApprox Output
deepseek-chat (V3)~$0.07 / 1M tokens~$0.28 / 1M tokens
deepseek-reasoner (R1)~$0.14 / 1M tokens~$2.19 / 1M tokens

R1 reasoning tokens (thinking) are also billed. Use for tasks where quality justifies cost.


Troubleshooting

IssueFix
Authentication failsVerify DEEPSEEK_API_KEY — starts with sk-
model not foundUse exact IDs: deepseek-chat or deepseek-reasoner
reasoning_content undefinedOnly available on deepseek-reasoner model
Streaming stops mid-responseCheck max_tokens — default is low; increase to 4096+
TypeScript errors on reasoning_contentUse // @ts-expect-error — field not in OpenAI types