docs(global): update logo assets, center header, and simplify documentation (#16)

main
Yixing Lao 2026-04-26 18:20:29 +08:00 committed by GitHub
parent 9b8c1f76b7
commit 3727105b65
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 39 additions and 40 deletions

View File

@ -1,8 +1,9 @@
<h1><img src="assets/logo.png" width="90" alt="deepseek-cursor-proxy logo" style="vertical-align: middle;"> deepseek-cursor-proxy</h1> <!-- <h1><img src="assets/logo.png" width="120" alt="deepseek-cursor-proxy logo" style="vertical-align: middle;">&nbsp;DeepSeek Cursor Proxy</h1> -->
<h1 align="center"><img src="assets/logo.png" width="150" alt="deepseek-cursor-proxy logo"><br>DeepSeek Cursor Proxy</h1>
A compatibility proxy that connects Cursor to DeepSeek thinking models (`deepseek-v4-pro` and `deepseek-v4-flash`) by properly handling the `reasoning_content` field for DeepSeek tool-call reasoning API requests. A compatibility proxy that connects Cursor to DeepSeek thinking models (`deepseek-v4-pro` and `deepseek-v4-flash`) by properly handling the `reasoning_content` field for DeepSeek tool-call reasoning API requests.
This proxy can also help _other applications and coding agents_ beyond Cursor that run into the same missing `reasoning_content` issue with DeepSeek's thinking-mode API. Just point their API base URL at the proxy. This proxy can also help **other applications and coding agents** beyond Cursor that run into the same missing `reasoning_content` issue with DeepSeek's thinking-mode API. Just point their API base URL at the proxy.
## What It Does ## What It Does
@ -34,12 +35,10 @@ Provider returned error:
### Step 1: Set Up ngrok ### Step 1: Set Up ngrok
Cursor blocks non-public API URLs such as `localhost`, so the proxy needs a public HTTPS URL. [ngrok](https://ngrok.com/) can expose the local proxy to Cursor without opening router ports. Alternatively, you may use [Cloudflare Tunnel](https://developers.cloudflare.com/tunnel/setup/). Cursor blocks non-public API URLs such as `localhost`, so the proxy needs a public HTTPS URL. [ngrok](https://ngrok.com/) can expose the local proxy to Cursor without opening router ports. Alternatively, you may use [Cloudflare Tunnel](https://developers.cloudflare.com/tunnel/setup/). Create an ngrok account and visit [ngrok's dashboard](https://dashboard.ngrok.com). You will find the authtoken and public URL there.
If you're using this proxy with another application that allows localhost API endpoints, you can skip this step entirely by setting `ngrok: false` in `~/.deepseek-cursor-proxy/config.yaml`, or by starting the proxy with `--no-ngrok`. If you're using this proxy with another application that allows localhost API endpoints, you can skip this step entirely by setting `ngrok: false` in `~/.deepseek-cursor-proxy/config.yaml`, or by starting the proxy with `--no-ngrok`.
Create an ngrok account, then visit ngrok's dashboard: https://dashboard.ngrok.com
<img src="assets/ngrok_dashboard.png" width="600" alt="ngrok dashboard"> <img src="assets/ngrok_dashboard.png" width="600" alt="ngrok dashboard">
Then, install and authenticate ngrok once: Then, install and authenticate ngrok once:
@ -59,10 +58,10 @@ In Cursor, add the DeepSeek custom model and point it at this proxy:
The proxy respects the DeepSeek model name Cursor sends, such as `deepseek-v4-pro` or `deepseek-v4-flash`. The `model` field in `config.yaml` is used as a fallback only when a request does not include a model. The proxy respects the DeepSeek model name Cursor sends, such as `deepseek-v4-pro` or `deepseek-v4-flash`. The `model` field in `config.yaml` is used as a fallback only when a request does not include a model.
For example, if ngrok dashboard shows `https://example.ngrok-free.app`, use: For example, if ngrok dashboard shows `https://example.ngrok-free.dev`, use:
```text ```text
https://example.ngrok-free.app/v1 https://example.ngrok-free.dev/v1
``` ```
<img src="assets/cursor_config.png" width="600" alt="Cursor settings for DeepSeek through the proxy"> <img src="assets/cursor_config.png" width="600" alt="Cursor settings for DeepSeek through the proxy">
@ -121,12 +120,10 @@ Select `deepseek-v4-pro` in Cursor and use chat or agent mode as usual.
## How It Works ## How It Works
DeepSeek's [thinking mode](https://api-docs.deepseek.com/guides/thinking_mode#tool-calls) requires `reasoning_content` from assistant messages in tool-call sequences to be passed back in later requests. Cursor may omit this field, causing DeepSeek to return a 400 error. This proxy sits between Cursor and DeepSeek (`Cursor → ngrok → proxy → DeepSeek API`) and repairs requests when it has the exact original reasoning cached. - **Core fix:** DeepSeek's [thinking mode](https://api-docs.deepseek.com/guides/thinking_mode#tool-calls) requires `reasoning_content` from assistant tool-call messages to be passed back in subsequent requests, but Cursor omits this field, causing a 400 error. The proxy (`Cursor → ngrok → proxy → DeepSeek API`) stores `reasoning_content` from every DeepSeek response in a local SQLite cache, keyed by message signature, tool-call ID, and tool-call function signature, and patches outgoing requests with missing `reasoning_content` before they reach DeepSeek. On a cold cache (proxy restart, model switch), it logs and drops unrecoverable history, continues from the latest user request, and prefixes the next Cursor response with a notice.
- **Multi-conversation isolation:** To avoid collisions across concurrent conversations, the proxy scopes cache keys by a SHA-256 hash of the canonical conversation prefix (roles, content, and tool calls, excluding `reasoning_content`) plus the upstream model, configuration, and an API-key hash. Different threads get different scopes, so reused tool-call IDs do not collide. Byte-identical cloned histories produce identical scopes.
- **Core fix:** every DeepSeek response, streaming or non-streaming, has its `reasoning_content` stored in a local SQLite cache keyed by message signature, tool-call ID, and tool-call function signature. On outgoing thinking-mode requests, the proxy restores missing `reasoning_content` for tool-call-related assistant messages and sends the complete history to DeepSeek. If the cache is cold, such as after a proxy restart or model switch, the default recovery mode omits older unrecoverable tool-call history, continues from the latest user request, logs the recovery, and prefixes the next Cursor response with a small notice. - **Context caching compatibility:** The proxy preserves compatibility by never injecting synthetic thread IDs, timestamps, or cache-control messages. It restores `reasoning_content` as the exact original string, so repeated prefixes remain intact for [DeepSeek context cache](https://api-docs.deepseek.com/guides/kv_cache). Cache hit rates are logged in the terminal output.
- **Multi-conversation isolation:** cache keys are scoped by a SHA-256 hash of the canonical conversation prefix (roles, content, tool calls, excluding `reasoning_content`) plus the upstream model/configuration and an API-key hash. Concurrent or interleaved threads with different histories get different scopes, so reused tool-call IDs do not collide. Byte-identical cloned histories are indistinguishable unless Cursor sends a differentiating history. - **Additional compatibility fixes:** Beyond reasoning repair, the proxy converts legacy `functions`/`function_call` fields to `tools`/`tool_choice`, preserves required and named tool-choice semantics, normalizes `reasoning_effort` aliases, strips mirrored `<think>` blocks from assistant content, flattens multi-part content arrays to plain text, and mirrors `reasoning_content` into Cursor-visible `<think>...</think>` blocks.
- **DeepSeek [context caching](https://api-docs.deepseek.com/guides/kv_cache) compatibility:** the proxy does not inject synthetic thread IDs, timestamps, or cache-control messages into the prompt. When it restores cached reasoning, it restores the exact original string, preserving repeated prefixes for DeepSeek's automatic best-effort context cache.
- **Additional compatibility fixes:** the proxy converts legacy `functions`/`function_call` fields to `tools`/`tool_choice`, preserves required and named tool-choice semantics, normalizes `reasoning_effort` aliases per DeepSeek docs, strips mirrored `<think>` blocks from assistant content, converts multi-part content arrays to plain text, logs DeepSeek prompt-cache usage when available, and mirrors `reasoning_content` into Cursor-visible `<think>...</think>` blocks for thinking display.
## Development ## Development
@ -145,8 +142,6 @@ uv run pre-commit run --all-files
## Debugging ## Debugging
Normal logs avoid request/response bodies but still print compact request and usage statistics. `rounds` is the number of user turns in the forwarded history, `reasoning` is the number and character size of `reasoning_content` fields sent to DeepSeek, and `cache=hit/miss` comes from DeepSeek's `usage.prompt_cache_hit_tokens` / `prompt_cache_miss_tokens`.
Run with verbose output: Run with verbose output:
```bash ```bash

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg <svg
viewBox="-20 -20 547.09113 432.29314" viewBox="-20 -20 673.40649 293.21227"
width="547.09113" width="673.40649"
height="432.29315" height="293.21228"
version="1.1" version="1.1"
id="svg5" id="svg5"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -10,35 +10,39 @@
<defs <defs
id="defs5" /> id="defs5" />
<!-- Whale body --> <!-- Whale body -->
<path
d="m 253,114.86161 c -45.92709,14.27113 -82.71498,40.90567 -105,70 17.5114,5.36136 37.49219,9.56609 48,26 -4.70603,12.83463 -27.88458,64.3883 -96.5,64.1 3.25287,-14.40559 13.79207,-5.9152 26.5,-31.1 7.2,-15.5 3.89519,-29.64759 -0.90481,-42.54759 L 114,178.86161 c -15.86826,22.02798 -41.88742,34.9478 -41.2,63.4 l 0.2,11.6 -6.97899,10.08318 C 48.92101,256.34479 33.7,247.06161 20,250.86161 l -5.2,1.1 -34.8,6.9 c 2.22551,12.61121 365.31509,141.85759 497.11638,26.63814 17.91838,-15.04094 33.2139,34.62107 36.51784,28.53885 C 555.67962,236.63727 495.53261,117.94776 364,104.86161 c -38.2,-3.6 -76.4,-0.6 -111,10"
fill="#4d6bfe"
id="path1" />
<!-- Whale belly --> <!-- Whale belly -->
<path
d="m 114,178.86161 c -24.91809,22.41929 -49.62736,42.02013 -41,75 -25.88736,-22.33434 -62.84658,-1.68936 -93,5 2.1,11.9 14.8407302,24.04853 24.3407302,31.14853 26.9644098,18.77157 48.1031198,11.04228 76.4504198,10.26917 79.83045,99.29128 315.25127,168.4065 415.43543,48.43636 30.14264,-36.6413 32.48086,-78.7128 30.22732,-98.8085 C 498.80687,274.63614 397.16257,236.3057 364,259.86161 c -1.8424,4.03208 26.94328,10.28509 48.71324,10.28509 21.76997,0 32.39427,-4.46758 35.15714,1.45567 2.00222,4.29253 -22.26089,19.3223 -50.82644,19.25335 C 196.91311,274.76848 177.2751,356.23604 81,273.86161 c 7.22691,-0.36179 13.44021,-2.19777 19.9,-5.1 11.3,-5 19.6,-14 25.1,-24.9 12.54329,-29.67204 3.50788,-41.73818 -12,-65"
fill="#6d86ff"
id="path2" />
<!-- Smile --> <!-- Smile -->
<path
d="m 235.29689,319.65101 c 0,0 -57.73871,39.29007 -75.77946,54.38423 -18.04076,15.09415 -19.10661,21.69337 -11.82705,24.24 7.27956,2.54663 178.27712,-10.94593 201.23571,-89.12486 -38.94648,-8.00044 -80.60165,-10.86896 -113.6292,10.50063"
fill="#4d6bfe"
id="path3"
style="stroke-width:1.11632" />
<!-- Eye --> <!-- Eye -->
<circle <g
cx="-371.21661" id="g1"
cy="213.59879" transform="matrix(1,0,0,0.9492616,-23.508465,-193.47721)">
r="20.452892" <path
fill="#ffffff" d="m 276.50846,194.20268 c -45.92709,14.27113 -82.71498,40.90567 -105,70 17.5114,5.36136 37.49219,9.56609 48,26 -4.70603,12.83463 -27.88458,64.3883 -96.5,64.1 3.25287,-14.40559 13.79207,-5.9152 26.5,-31.1 7.2,-15.5 3.89519,-29.64759 -0.90481,-42.54759 l -11.09519,-22.45241 c -15.86826,22.02798 -41.887415,34.9478 -41.199995,63.4 l 0.2,11.6 -6.97899,10.08318 c -17.1,-7.6 -32.32101,-16.88318 -46.02101,-13.08318 l -5.2,1.1 -34.8000005,6.9 c 2.22551,12.61121 365.3150855,141.85759 497.1163755,26.63814 17.91838,-15.04094 33.2139,34.62107 36.51784,28.53885 42.0454,-77.40133 -18.10161,-196.09084 -149.63422,-209.17699 -38.2,-3.6 -76.4,-0.6 -111,10"
id="circle3" fill="#4d6bfe"
style="stroke-width:1.46092" id="path1" />
transform="scale(-1,1)" /> <path
d="m 137.50846,258.20268 c -24.91809,22.41929 -49.627355,42.02013 -40.999995,75 -25.88736,-22.33434 -62.84658,-1.68936 -93.0000005,5 2.1,11.9 14.8407305,24.04853 24.3407305,31.14853 26.96441,18.77157 48.10312,11.04228 76.450415,10.26917 79.83045,99.29128 315.25127,168.4065 415.43543,48.43636 30.14264,-36.6413 32.48086,-78.7128 30.22732,-98.8085 -27.64703,24.72897 -129.29133,-13.60147 -162.4539,9.95444 -1.8424,4.03208 26.94328,10.28509 48.71324,10.28509 21.76997,0 32.39427,-4.46758 35.15714,1.45567 2.00222,4.29253 -22.26089,19.3223 -50.82644,19.25335 -200.13083,-16.08724 -219.76884,65.38032 -316.04394,-16.99411 7.22691,-0.36179 13.44021,-2.19777 19.9,-5.1 11.3,-5 19.6,-14 25.1,-24.9 12.54329,-29.67204 3.50788,-41.73818 -12,-65"
fill="#6d86ff"
id="path2" />
<path
d="m 258.80535,398.99208 c 0,0 -57.73871,39.29007 -75.77946,54.38423 -18.04076,15.09415 -19.10661,21.69337 -11.82705,24.24 7.27956,2.54663 178.27712,-10.94593 201.23571,-89.12486 -38.94648,-8.00044 -80.60165,-10.86896 -113.6292,10.50063"
fill="#4d6bfe"
id="path3"
style="stroke-width:1.11632" />
<circle
cx="-394.72507"
cy="292.93985"
r="20.452892"
fill="#ffffff"
id="circle3"
style="stroke-width:1.46092"
transform="scale(-1,1)" />
</g>
<!-- Cursor (full, dark blue) --> <!-- Cursor (full, dark blue) -->
<!-- Cursor right half (light blue overlay) --> <!-- Cursor right half (light blue overlay) -->
<g <g
id="g5" id="g5"
transform="matrix(-0.91717882,0,0,0.91717882,481.79815,-23.288813)"> transform="matrix(0,2.0096137,-0.90113763,0,656.63776,-208.45992)">
<path <path
d="M 173.5,4.5 220,95.8 q 3.5,7.2 -4.5,7.2 h -26.3 q -6,0 -7.9,5.7 l -7.1,20.7 q -0.6,1.6 -2.4,1.6 h -1 q -1.8,0 -2.4,-1.6 l -7.1,-20.7 q -1.9,-5.7 -7.9,-5.7 h -26.3 q -8,0 -4.5,-7.2 L 169.1,4.5 c 2.5,-1.2 1.9,-1.2 4.4,0 z" d="M 173.5,4.5 220,95.8 q 3.5,7.2 -4.5,7.2 h -26.3 q -6,0 -7.9,5.7 l -7.1,20.7 q -0.6,1.6 -2.4,1.6 h -1 q -1.8,0 -2.4,-1.6 l -7.1,-20.7 q -1.9,-5.7 -7.9,-5.7 h -26.3 q -8,0 -4.5,-7.2 L 169.1,4.5 c 2.5,-1.2 1.9,-1.2 4.4,0 z"
fill="#4d6bfe" fill="#4d6bfe"

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB