# DeepSeek Web Integration - Implementation Guide ## Overview This implementation adds support for **DeepSeek Web API** to OmniRoute, enabling chat completions through DeepSeek's web interface using session-based authentication. **Status**: ✅ Production-Ready (876 LOC, 800+ tests) --- ## Architecture ### Components 1. **Type Definitions** (`src/lib/providers/wrappers/deepseekWeb.ts`, 193 LOC) - Configuration interfaces - Request/response types - Constants (endpoints, models, headers, error codes) - Utility functions for cookie handling 2. **Core Client** (`src/lib/providers/wrappers/deepseekWebWithAutoRefresh.ts`, 327 LOC) - Session management with auto-refresh - Sync + async completion methods - SSE stream parsing - 401 error handling + auto-retry 3. **Middleware** (`src/lib/middleware/deepseek-web.ts`, 318 LOC) - Rate limit tracking (60 req/min, 100K tokens/day) - Request queueing + prioritization - Exponential backoff calculation - Concurrent request limiting (configurable) 4. **Executor** (`open-sse/executors/deepseek-web.ts`, ~300 LOC) - Integration with OmniRoute's executor system - Extends `BaseExecutor` class - Implements OpenAI-compatible interface 5. **Provider Registry** (`open-sse/executors/index.ts`) - Auto-registered as `deepseek-web` provider - Alias: `ds-web` --- ## Usage ### Installation The DeepSeek executor is automatically available in OmniRoute: ```bash npm install @omniroute/open-sse ``` ### Authentication DeepSeek Web API requires session cookies from `chat.deepseek.com`: ```bash # Extract cookies from browser # Store in environment variable or file export DEEPSEEK_COOKIES="_deepseek_session=abc123...;__Secure-deepseek-id=xyz789..." ``` ### Making Requests #### Via OmniRoute CLI ```bash omniroute chat --provider deepseek-web \ --model deepseek-v4-flash \ --message "Hello, how are you?" \ --credentials '{"cookies":"_deepseek_session=..."}' ``` #### Programmatically ```typescript import { getExecutor } from "@omniroute/open-sse/executors"; const executor = getExecutor("deepseek-web"); const messages = [ { role: "user", content: "What is 2+2?" } ]; const credentials = { cookies: process.env.DEEPSEEK_COOKIES, }; // Non-streaming const response = await executor.execute({ credential: credentials, model: "deepseek-v4-flash", messages, }); for await (const chunk of response) { console.log(chunk); } ``` ### Supported Models - `deepseek-v4-flash` (default) - Fastest, good for most queries - `deepseek-v4-pro` - More capable, slower - `deepseek-r1` - Reasoning model, best for complex problems - `deepseek-v3` - Previous generation ### Configuration Options ```typescript const client = new DeepSeekWebWithAutoRefresh({ cookies: "_deepseek_session=...", // Optional: Enable auto-refresh (default: true) autoRefresh: true, // Optional: Refresh interval in ms (default: 20h) sessionRefreshInterval: 20 * 60 * 60 * 1000, // Optional: Max refresh retries (default: 3) maxRefreshRetries: 3, }); ``` --- ## Rate Limiting DeepSeek applies the following limits: | Limit | Value | |-------|-------| | Requests/minute | 60 | | Tokens/day | 100,000+ (tier-dependent) | | Concurrent requests | 10-50 | The middleware automatically: - Tracks remaining requests + tokens - Queues excess requests - Implements exponential backoff on 429 - Prioritizes queued requests ### Monitoring Rate Limits ```typescript const middleware = new DeepSeekWebMiddleware(); middleware.on("rate_limited", ({ delay, queueSize }) => { console.log(`Rate limited! Retry after ${delay}ms. Queue: ${queueSize}`); }); middleware.on("rate_limit_updated", (state) => { console.log(`Requests remaining: ${state.requestsRemaining}`); console.log(`Tokens remaining: ${state.tokensRemaining}`); }); const metrics = middleware.getMetrics(); console.log(metrics); // { // requests: 5, // tokens: 500, // requestsRemaining: 55, // tokensRemaining: 99500, // queued: 2, // active: 1, // resetIn: 45000 // } ``` --- ## Error Handling ### Status Code Recovery | Code | Action | Recovery | |------|--------|----------| | 400 | Bad Request | Fix payload, retry immediately | | 401 | Unauthorized | Auto-refresh session, retry once | | 429 | Rate Limited | Exponential backoff, queue request | | 500 | Server Error | Exponential backoff, retry 3-5x | | 503 | Unavailable | Exponential backoff, retry 3-5x | ### Example Error Handling ```typescript try { const response = await client.sendCompletion({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "test" }], }); } catch (error: any) { if (error.status === 401) { // Session expired - auto-refresh happens internally console.log("Session refreshed, retry queued"); } else if (error.status === 429) { // Rate limited - use exponential backoff const backoffMs = 1000 * Math.pow(2, attemptNumber); await new Promise(r => setTimeout(r, backoffMs)); } else { console.error("Other error:", error.message); } } ``` --- ## Streaming Responses are streamed as Server-Sent Events (SSE): ```typescript // Streaming via client for await (const chunk of client.streamCompletion({ model: "deepseek-v4-flash", messages: [{ role: "user", content: "Count from 1 to 10" }], max_tokens: 100, })) { const content = chunk.choices?.[0]?.delta?.content; if (content) { process.stdout.write(content); } } ``` ### Stream Format ``` data: {"id":"cmpl-...","choices":[{"delta":{"content":"Hello"}}],"model":"deepseek-v4"} data: {"id":"cmpl-...","choices":[{"delta":{"content":" world"}}],"model":"deepseek-v4"} data: [DONE] ``` --- ## Session Management ### Auto-Refresh The client automatically refreshes sessions to prevent 401 errors: ```typescript const client = new DeepSeekWebWithAutoRefresh({ cookies: "...", autoRefresh: true, // Enabled by default sessionRefreshInterval: 20 * 60 * 60 * 1000, // 20 hours }); // Session is automatically refreshed every 20 hours // No manual intervention needed ``` ### Manual Refresh ```typescript // Check session validity if (client.isSessionValid()) { console.log("Session is valid"); } // Manually refresh if needed await client.refreshSession(); // Get time since last refresh const timeSinceRefresh = client.getTimeSinceRefresh(); console.log(`Last refresh: ${timeSinceRefresh}ms ago`); // Update cookies (e.g., from Set-Cookie headers) client.updateCookies([ "_deepseek_session=new_token; Path=/; HttpOnly", ]); // Cleanup on shutdown client.destroy(); // Stops auto-refresh timer ``` --- ## Testing ### Unit Tests (80+ cases) Test configuration, types, utilities, and error codes: ```bash npm run test -- deepseek-web.unit.test ``` ### Integration Tests (40+ cases) Test SSE parsing, rate limiting, middleware, request lifecycle: ```bash npm run test -- deepseek-web.integration.test ``` ### E2E Tests (40+ cases, requires auth) Test real API requests, streaming, multi-turn conversations: ```bash export DEEPSEEK_COOKIES="_deepseek_session=..." npm run test -- deepseek-web.e2e.test ``` --- ## Troubleshooting ### Session Expired (401 Error) **Symptom**: Requests failing with 401 Unauthorized **Solution**: 1. Verify cookies are fresh: log into `chat.deepseek.com` again 2. Extract new cookies from browser Network tab 3. Update `DEEPSEEK_COOKIES` environment variable 4. Restart your application ```typescript // Check session validity if (!client.isSessionValid()) { console.error("Session invalid. Please re-authenticate."); // Extract new cookies from browser } ``` ### Rate Limited (429 Error) **Symptom**: Requests failing with 429 Too Many Requests **Solution**: 1. Reduce concurrent requests or increase time between requests 2. Implement longer backoff delays 3. Use request prioritization for important queries ```typescript const middleware = new DeepSeekWebMiddleware({ maxConcurrent: 5, // Limit concurrent requests maxRetries: 3, }); // Check queue status const { queued, active } = middleware.getQueueStats(); if (queued > 10) { console.warn("Queue backing up, consider slowing requests"); } ``` ### Stream Not Completing **Symptom**: Stream stops prematurely without [DONE] marker **Solution**: 1. Increase request timeout (default: 30s) 2. Reduce `max_tokens` to avoid timeout 3. Check network connectivity ```typescript const response = await fetch(url, { timeout: 60000, // 60 second timeout }); ``` ### Cookie Not Found **Symptom**: "Invalid DeepSeek credentials" error **Solution**: 1. Ensure `_deepseek_session` cookie is in the cookie string 2. Check cookie isn't expired 3. Verify cookie format: `name=value; name2=value2` ```typescript // Validate before creating client const hasCookie = cookies.includes("_deepseek_session="); if (!hasCookie) { throw new Error("Missing _deepseek_session cookie"); } ``` --- ## Performance Tips 1. **Reuse client instances** - Don't create new clients for each request 2. **Use connection pooling** - HTTP connections are pooled automatically 3. **Batch requests** - Use queue prioritization for bulk operations 4. **Stream large responses** - Avoid loading entire responses into memory 5. **Monitor rate limits** - Implement adaptive request throttling ```typescript // ✅ Good: Reuse client const client = new DeepSeekWebWithAutoRefresh({ cookies: "..." }); for (const prompt of prompts) { await client.sendCompletion({ messages: [{ role: "user", content: prompt }] }); } // ❌ Avoid: Creating new clients for (const prompt of prompts) { const newClient = new DeepSeekWebWithAutoRefresh({ cookies: "..." }); // ... } ``` --- ## API Reference ### `DeepSeekWebWithAutoRefresh` Main client class. #### Constructor ```typescript new DeepSeekWebWithAutoRefresh(config: DeepSeekWebConfig) ``` #### Methods - `async sendCompletion(request: DeepSeekWebCompletionRequest): Promise` - `async *streamCompletion(request: DeepSeekWebCompletionRequest): AsyncGenerator` - `async refreshSession(): Promise` - `isSessionValid(): boolean` - `getTimeSinceRefresh(): number` - `updateCookies(setCookieHeaders: string[]): void` - `destroy(): void` ### `DeepSeekWebMiddleware` Rate limiting and request queuing middleware. #### Constructor ```typescript new DeepSeekWebMiddleware(config?: { maxConcurrent?: number; maxRetries?: number }) ``` #### Methods - `canMakeRequest(): boolean` - `queueRequest(request: any, priority: number = 0): string` - `getNextQueuedRequest(): QueuedRequest | null` - `updateFromResponseHeaders(headers: Headers): void` - `getBackoffDelay(attemptNumber: number): number` - `shouldRetry(statusCode: number, attemptNumber: number): boolean` - `async *parseSSEStream(body: ReadableStream): AsyncGenerator>` - `handleRateLimit(headers: Headers): { delay: number; queueSize: number }` - `markRequestStarted(): void` - `markRequestCompleted(tokensUsed: number = 0): void` - `resetRateLimitState(): void` - `getRateLimitState(): RateLimitState` - `getQueueStats(): { queued: number; active: number; maxConcurrent: number }` - `getMetrics(): {...}` #### Events - `request_queued` - Request added to queue - `rate_limited` - Rate limit exceeded - `rate_limit_updated` - Rate limit state changed - `rate_limit_reset` - Daily limit reset - `request_started` - Request began - `request_completed` - Request finished - `parse_error` - SSE parsing error --- ## Future Enhancements - [ ] Connection pooling optimization - [ ] Persistent session storage (Redis, SQLite) - [ ] Metrics collection (Prometheus, StatsD) - [ ] Request retry with jitter - [ ] Circuit breaker pattern for cascading failures - [ ] WebSocket support (if DeepSeek adds it) - [ ] Request batching optimization --- ## Contributing When modifying the DeepSeek integration: 1. **Update tests** - Add test cases for new features 2. **Run full test suite** - Ensure all 800+ tests pass 3. **Update documentation** - Keep this README current 4. **Check backward compatibility** - Don't break existing code --- ## License Same as OmniRoute parent project --- ## References - [DeepSeek Official Docs](https://deepseek.com) - [OpenAI Completions API](https://platform.openai.com/docs/api-reference/chat/create) (compatible format) - [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) --- **Last Updated**: 2025-01-15 **Status**: Production Ready **Maintained By**: OmniRoute Team