Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@

# ── HTTP server ──
PORT=9655
HOST=0.0.0.0
# Loopback-only by default. Use 0.0.0.0 only when remote access is intentional.
HOST=127.0.0.1
# Optional bearer token required by every non-health endpoint. Strongly
# recommended whenever HOST is not a loopback address.
# PROXY_API_KEY=replace-with-a-long-random-value
# Browser requests are accepted from loopback origins. Add exact remote UI
# origins as a comma-separated allowlist when needed.
# PROXY_CORS_ORIGINS=https://ui.example.com,http://192.168.1.20:3000

# ── Account auth source ──
# The pool loads DeepSeek accounts from files. Use either a single file or a
Expand All @@ -17,6 +24,13 @@ DEEPSEEK_AUTH_PATH=./deepseek-auth.json
# (default: 600000 = 10 minutes).
DEEPSEEK_ACCOUNT_COOLDOWN_MS=600000

# ── Long agent sessions ──
# Maximum characters sent to DeepSeek Web after compacting old context.
# Lower this if the Web endpoint reports that the content is too long.
DEEPSEEK_MAX_PROMPT_CHARS=80000
# Fresh-session retries for an empty/context-too-long response (default: 2).
DEEPSEEK_MAX_RETRIES=2

# ── Startup (deploy / CI) ──
# Set either to 1/true to start the proxy immediately and skip the menu.
NON_INTERACTIVE=0
Expand Down
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ SKIP_ACCOUNT_MENU=1 npm start
http://localhost:9655
```

По умолчанию proxy доступен только с этого компьютера. Для доступа из сети
явно задайте адрес и отдельный ключ proxy:

```bash
HOST=0.0.0.0 PROXY_API_KEY='replace-with-a-long-random-value' npm start
```

После этого передавайте ключ как `Authorization: Bearer <key>`. Без
`PROXY_API_KEY` non-health endpoints остаются без авторизации, поэтому не
публикуйте такой экземпляр в сеть.

Browser-запросы разрешены с loopback-origin. Если UI открыт на другом адресе,
добавьте его точный origin через запятую, например
`PROXY_CORS_ORIGINS=https://ui.example.com,http://192.168.1.20:3000`.

---

## 🪟 Windows запуск
Expand Down Expand Up @@ -230,6 +245,9 @@ FreeDeepseekAPI не создаёт новый DeepSeek чат на каждый
- если session id уже есть — proxy переиспользует его и продолжает chain через `parent_message_id`;
- auto-reset происходит при TTL, ошибке DeepSeek session или слишком длинной цепочке сообщений;
- локальная history сохраняется коротким контекстом, чтобы новая DeepSeek session могла продолжить разговор.
- длинные agent-запросы перед отправкой ограничиваются `DEEPSEEK_MAX_PROMPT_CHARS` (по умолчанию 80 000 символов): сохраняются начало задачи, свежие tool results и tool adapter;
- если клиент уже прислал multi-turn history, локальная recovery-history второй раз не добавляется;
- пустой ответ повторяется максимум `DEEPSEEK_MAX_RETRIES` раз (по умолчанию 2), причём на каждом retry контекст уменьшается.

Явно задать agent/session:

Expand Down Expand Up @@ -429,8 +447,9 @@ FreeDeepseekAPI принимает:
Прокси просит DeepSeek вернуть строгий JSON tool call, но также умеет парсить fallback-форматы:

- `TOOL_CALL:`
- fenced JSON
- fenced JSON with an explicit `tool_call`, `tool_calls`, or `function_call` envelope
- `<tool_call>...</tool_call>`
- DeepSeek DSML (`<|DSML|tool_calls>...`) и Web-вариант с `<||DSML|| Tool Calls>`

---

Expand Down Expand Up @@ -502,7 +521,9 @@ http://host.docker.internal:9655/v1
http://localhost:9655/v1
```

API key можно указать любой: proxy сам ходит в DeepSeek Web через сохранённую browser-сессию.
Если `PROXY_API_KEY` не задан, API key можно указать любой. Если ключ задан,
клиент должен передавать именно его — proxy проверяет bearer token перед
доступом к моделям, сессиям и completions.

---

Expand Down
8 changes: 1 addition & 7 deletions chrome-extension/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@
"service_worker": "background.js"
},
"action": {
"default_popup": "popup.html",
"default_icon": {
"48": "icon.png"
}
},
"icons": {
"48": "icon.png"
"default_popup": "popup.html"
}
}
21 changes: 12 additions & 9 deletions docs/api-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,8 +359,9 @@ DeepSeek Web does not expose native OpenAI tool calls, so the proxy prompt-emula

- strict JSON: `{"tool_call":{"name":"tool","arguments":{...}}}`
- legacy format: `TOOL_CALL: tool\narguments: {...}`
- fenced JSON blocks
- fenced JSON blocks with an explicit `tool_call`, `tool_calls`, or `function_call` envelope
- XML-ish `<tool_call>{...}</tool_call>` wrappers
- DeepSeek DSML (`<|DSML|tool_calls>...`) and the doubled-bar Web variant

### 3.7 List Active Sessions

Expand Down Expand Up @@ -439,8 +440,8 @@ The proxy uses `user` from the request body. If not set, it falls back to the cl
id: "uuid", // DeepSeek web session ID
parentMessageId: <int|null>, // Last message ID for threading
createdAt: <timestamp>, // Session creation time
messageCount: 0-50, // Messages in this session
history: [ // Last 5 exchanges for context recovery
messageCount: 0-100, // Messages in this session
history: [ // Last 15 exchanges for context recovery
{ user: "...", assistant: "..." }
]
}
Expand Down Expand Up @@ -514,14 +515,15 @@ The parser traverses character by character tracking brace depth:

| Condition | Action |
|---|---|
| Message count >= 50 | Auto-reset DeepSeek session, keep history buffer |
| Message count >= 100 | Auto-reset DeepSeek session, keep history buffer |
| Session age > 2 hours | Auto-reset (DeepSeek web session TTL) |
| HTTP 400/404/500 response | Reset and retry once |
| Empty content response | Return HTTP 502 (no retry) |
| Empty content response | Compact context, reset session, retry up to `DEEPSEEK_MAX_RETRIES` (default 2) |
| Context/content too long | Pre-compact to `DEEPSEEK_MAX_PROMPT_CHARS`, then retry with a smaller budget |

### 6.2 History Buffer

When a session is reset, the proxy preserves the **last 5 exchanges** (capped at ~3000 chars). On the next request, it injects them as context:
When a session is reset, the proxy preserves the **last 15 exchanges** (capped at 10,000 chars). It injects this recovery context only when the client did not already send multi-turn history:

```
[Previous conversation]
Expand Down Expand Up @@ -553,9 +555,10 @@ If DeepSeek's web session expires (HTTP 400/404/500):
### 7.1 Proxy Configuration (in deepseek-api-server.js)

```javascript
const MAX_HISTORY_LENGTH = 5; // Keep last 5 exchanges
const MAX_HISTORY_CHARS = 3000; // Max chars for history buffer
const MAX_MESSAGE_DEPTH = 50; // Auto-reset after 50 messages
const MAX_HISTORY_LENGTH = 15; // Keep last 15 exchanges
const MAX_HISTORY_CHARS = 10000; // Max chars for history buffer
const MAX_MESSAGE_DEPTH = 100; // Auto-reset after 100 messages
const MAX_UPSTREAM_PROMPT_CHARS = 80000; // Configurable via DEEPSEEK_MAX_PROMPT_CHARS
const SESSION_TTL_MS = 2 * 60 * 60 * 1000; // 2 hours

const DS_CONFIG = {
Expand Down
Loading