SPB Git

spb/zyquo-cloud Public MIT

Native macOS AI chat client for 12 cloud providers — your keys, every cloud model, one beautiful chat.

Swift 97.4% Shell 1.7% Makefile 1%
8.4 KB

# DeepSeek — API Research (Phase 0)

Researched 2026-07-30 against official docs (api-docs.deepseek.com) and a live /models probe with a real key (/tmp/zyquo-probe/deepseek.json). Major change vs older integrations: deepseek-chat and deepseek-reasoner are RETIRED — only deepseek-v4-flash and deepseek-v4-pro exist now.


# 1. Base URL & Endpoints

Base: https://api.deepseek.com (OpenAI-compatible). An Anthropic-Messages-compatible surface also exists at https://api.deepseek.com/anthropic (not needed — Zyquo Cloud uses the OpenAI surface via OpenAICompatibleClient).

Endpoint Method Purpose
/chat/completions (also /v1/chat/completions) POST Chat (streaming + non-streaming)
/models GET List models
/user/balance GET Account balance (nice-to-have for the usage footer)

Docs: https://api-docs.deepseek.com/ and https://api-docs.deepseek.com/api/create-chat-completion

# 2. Authentication

Authorization: Bearer $DEEPSEEK_API_KEY. Keys from platform.deepseek.com.

# 3. Chat Model Catalog

Live probe (2026-07-30) returns exactly two models. Specs from https://api-docs.deepseek.com/quick_start/pricing. Both support streaming, vision — NO (text-only; unverified whether V4 added image input — docs do not document vision input; treat as text-only), tools/function calling (up to 128 functions), JSON mode (json_object), and thinking mode (default ON).

Model ID Context Max output $/1M input (cache hit) $/1M input (cache miss) $/1M output Thinking Concurrency
deepseek-v4-flash 1,000,000 384K $0.0028 $0.14 $0.28 ✅ default on (also non-thinking) 2,500
deepseek-v4-pro 1,000,000 384K $0.003625 $0.435 $0.87 ✅ default on (also non-thinking) 500

Retired legacy IDs (do NOT ship in ModelCatalog): deepseek-chat and deepseek-reasoner were routing aliases (chat = non-thinking mode, reasoner = thinking mode of the current generation, latterly resolving to deepseek-v4-flash). They stopped resolving on 2026-07-24 15:59 UTC and now return errors (404-class). V4 launched in preview 2026-04-24, GA 2026-07-20. If migrating user configs: deepseek-chatdeepseek-v4-flash with thinking.type: "disabled"; deepseek-reasonerdeepseek-v4-flash with thinking enabled (note: reasoner mapped to Flash, not Pro). Sources: DeepSeek V4 migration, Developers Digest guide, TheRouter.ai (third-party corroboration of official release notes).

Max-output default vs maximum split is not documented beyond "384K max" — default max_tokens when omitted: unverified.

# 4. Request / Response Format

OpenAI-compatible with DeepSeek extensions/deviations (https://api-docs.deepseek.com/api/create-chat-completion):

  • model: "deepseek-v4-flash" | "deepseek-v4-pro"
  • messages: roles system / user / assistant / tool; optional name
  • temperature: 0–2, default 1; top_p: 0–1, default 1
  • max_tokens: nullable int (input + output ≤ context)
  • thinking: { "type": "enabled" | "disabled" } — default enabled; plus reasoning_effort: "high" | "max" (defaults vary by request type; exact default unverified)
  • stream, stream_options: {"include_usage": true}
  • response_format: {"type": "text" | "json_object"} (no json_schema documented)
  • tools (≤128 functions), tool_choice, logprobs + top_logprobs (0–20), stop (≤16 sequences)
  • Deprecated/removed: frequency_penalty and presence_penalty — "no longer supported". Do not surface these sliders for DeepSeek in the per-conversation parameters UI.
json
POST /chat/completions
{
  "model": "deepseek-v4-flash",
  "messages": [
    { "role": "system", "content": "You are a helpful assistant." },
    { "role": "user", "content": "Reply with exactly: OK" }
  ],
  "thinking": { "type": "enabled" },
  "reasoning_effort": "high",
  "max_tokens": 4096,
  "stream": false
}

Response deviations from vanilla OpenAI:

  • choices[0].message.reasoning_content (nullable string): "For thinking mode only. The reasoning contents of the assistant message, before the final answer." → this feeds Zyquo Cloud's collapsible "Thinking…" section.
  • finish_reason values: stop, length, content_filter, tool_calls, insufficient_system_resource (DeepSeek-specific — surface as "DeepSeek servers overloaded").
  • usage: prompt_tokens (= prompt_cache_hit_tokens + prompt_cache_miss_tokens), completion_tokens, total_tokens, prompt_cache_hit_tokens, prompt_cache_miss_tokens, completion_tokens_details.reasoning_tokens. Cost calc must use hit/miss split (cache-hit input is ~50× cheaper).

Vision: not documented for V4 — treat DeepSeek as text-only (no image_url support; unverified whether it errors or ignores).

Legacy V3-era rule ("do not pass reasoning_content back in the next request or the API errors") is unverified for V4 — the V4 docs no longer state it. Safe client behavior: never send reasoning_content back in messages (matches OpenAI-compat semantics either way). Whether temperature/top_p are ignored in thinking mode (as with the old reasoner): unverified for V4 — docs list them without model restrictions.

# 5. Streaming

Standard OpenAI SSE: data: {chunk} lines, terminated by data: [DONE]. DeepSeek specifics:

  • Thinking mode streams choices[0].delta.reasoning_content first (while reasoning), then choices[0].delta.content for the final answer. The parser must watch both fields and switch the UI from the "Thinking…" section to the answer body when content starts.
  • With stream_options.include_usage: true, one extra chunk with full usage arrives just before [DONE].
  • Keep-alive under load (https://api-docs.deepseek.com/quick_start/rate_limit): non-streaming responses may pad with empty lines; streaming sends SSE comments : keep-alive. The SSE parser must ignore comment lines and empty lines. If inference hasn't started after 10 minutes, the server closes the connection — use long read timeouts.

# 6. Special Parameters & Notes

  • thinking: {"type": "enabled"|"disabled"} — per-request toggle; default enabled on both models. Map Zyquo Cloud's "reasoning" toggle to this.
  • reasoning_effort: "high" | "max" — extra thinking depth control (thinking mode).
  • Context caching is automatic (no opt-in): repeated prefixes hit the cache and bill at the cache-hit input rate; observable via prompt_cache_hit_tokens/prompt_cache_miss_tokens.
  • user_id (optional): scheduling isolation; on expanded-capacity accounts each distinct user_id gets its own concurrency budget.
  • No frequency_penalty/presence_penalty; no json_schema response format; n not documented (unverified).

# 7. Rate Limits & Errors

(https://api-docs.deepseek.com/quick_start/rate_limit, https://api-docs.deepseek.com/quick_start/error_codes) DeepSeek uses concurrency limits, not RPM/TPM: 2,500 concurrent requests for v4-flash, 500 for v4-pro (a slot is held from send until the response completes). Exceeding → HTTP 429. Free capacity-expansion requests available. Under load, expect keep-alive padding (§5) rather than fast failures.

HTTP Meaning Handling
400 Invalid request body format fix request
401 Wrong API key "Invalid API key for DeepSeek"
402 Insufficient balance (DeepSeek-specific) tell user to top up
422 Invalid parameters fix params per message
429 Concurrency limit backoff + retry
500 Server error retry with backoff
503 Server overloaded retry after wait

Error body: OpenAI-style {"error": {"message", "type", "code"}} — exact JSON shape not specified in official docs (unverified; confirm in Phase 7).

# 8. /models Listing

Yes. GET /models → minimal OpenAI shape (verified live): {"object":"list","data":[{"id":"deepseek-v4-flash","object":"model","owned_by":"deepseek"},{"id":"deepseek-v4-pro","object":"model","owned_by":"deepseek"}]}. IDs only — context/pricing/capabilities must come from the built-in catalog.