mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-07 01:45:27 +08:00
feat: add i18n language toggle and bilingual docs
This commit is contained in:
701
API.en.md
Normal file
701
API.en.md
Normal file
@@ -0,0 +1,701 @@
|
||||
# DS2API API Reference
|
||||
|
||||
Language: [中文](API.md) | [English](API.en.md)
|
||||
|
||||
This document describes all DS2API API endpoints.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Basics](#basics)
|
||||
- [OpenAI-Compatible API](#openai-compatible-api)
|
||||
- [List Models](#list-models)
|
||||
- [Chat Completions](#chat-completions)
|
||||
- [Claude-Compatible API](#claude-compatible-api)
|
||||
- [Claude Model List](#claude-model-list)
|
||||
- [Claude Messages](#claude-messages)
|
||||
- [Token Counting](#token-counting)
|
||||
- [Admin API](#admin-api)
|
||||
- [Login](#login)
|
||||
- [Configuration](#configuration)
|
||||
- [Account Management](#account-management)
|
||||
- [Vercel Sync](#vercel-sync)
|
||||
- [Error Handling](#error-handling)
|
||||
- [Examples](#examples)
|
||||
|
||||
---
|
||||
|
||||
## Basics
|
||||
|
||||
| Item | Description |
|
||||
|-----|------|
|
||||
| **Base URL** | `https://your-domain.com` or `http://localhost:5001` |
|
||||
| **OpenAI auth** | `Authorization: Bearer <api-key>` |
|
||||
| **Claude auth** | `x-api-key: <api-key>` |
|
||||
| **Response format** | JSON |
|
||||
|
||||
---
|
||||
|
||||
## OpenAI-Compatible API
|
||||
|
||||
### List Models
|
||||
|
||||
```http
|
||||
GET /v1/models
|
||||
```
|
||||
|
||||
**Response example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{"id": "deepseek-chat", "object": "model", "owned_by": "deepseek"},
|
||||
{"id": "deepseek-reasoner", "object": "model", "owned_by": "deepseek"},
|
||||
{"id": "deepseek-chat-search", "object": "model", "owned_by": "deepseek"},
|
||||
{"id": "deepseek-reasoner-search", "object": "model", "owned_by": "deepseek"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Chat Completions
|
||||
|
||||
```http
|
||||
POST /v1/chat/completions
|
||||
Authorization: Bearer your-api-key
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----|------|:----:|------|
|
||||
| `model` | string | ✅ | Model name (see below) |
|
||||
| `messages` | array | ✅ | Chat messages |
|
||||
| `stream` | boolean | ❌ | Stream responses (default `false`) |
|
||||
| `temperature` | number | ❌ | Temperature (0-2) |
|
||||
| `max_tokens` | number | ❌ | Max output tokens |
|
||||
| `tools` | array | ❌ | Tool definitions (Function Calling) |
|
||||
| `tool_choice` | string | ❌ | Tool selection strategy |
|
||||
|
||||
**Supported models**:
|
||||
|
||||
| Model | Reasoning | Search | Notes |
|
||||
|-----|:--------:|:------:|------|
|
||||
| `deepseek-chat` | ❌ | ❌ | Standard chat |
|
||||
| `deepseek-reasoner` | ✅ | ❌ | Reasoning mode with trace |
|
||||
| `deepseek-chat-search` | ❌ | ✅ | Search enhanced |
|
||||
| `deepseek-reasoner-search` | ✅ | ✅ | Reasoning + search |
|
||||
|
||||
**Basic request example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "deepseek-chat",
|
||||
"messages": [
|
||||
{"role": "system", "content": "You are a helpful assistant."},
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Streaming request example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "deepseek-reasoner-search",
|
||||
"messages": [
|
||||
{"role": "user", "content": "What's in the news today?"}
|
||||
],
|
||||
"stream": true
|
||||
}
|
||||
```
|
||||
|
||||
**Streaming response format** (`stream: true`):
|
||||
|
||||
```
|
||||
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"role":"assistant"},"index":0}]}
|
||||
|
||||
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"reasoning_content":"Let me think..."},"index":0}]}
|
||||
|
||||
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"Based on search results..."},"index":0}]}
|
||||
|
||||
data: {"id":"...","object":"chat.completion.chunk","choices":[{"index":0,"finish_reason":"stop"}]}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
> **Note**: Reasoning models emit `reasoning_content` with the trace.
|
||||
|
||||
**Non-streaming response format** (`stream: false`):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "chatcmpl-xxx",
|
||||
"object": "chat.completion",
|
||||
"created": 1738400000,
|
||||
"model": "deepseek-reasoner",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Response text",
|
||||
"reasoning_content": "Reasoning trace (reasoner only)"
|
||||
},
|
||||
"finish_reason": "stop"
|
||||
}],
|
||||
"usage": {
|
||||
"prompt_tokens": 10,
|
||||
"completion_tokens": 50,
|
||||
"total_tokens": 60,
|
||||
"completion_tokens_details": {
|
||||
"reasoning_tokens": 20
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Tool Calling (Function Calling)
|
||||
|
||||
**Request example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "deepseek-chat",
|
||||
"messages": [{"role": "user", "content": "What's the weather in Beijing?"}],
|
||||
"tools": [{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"description": "Get the weather for a city",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"location": {"type": "string", "description": "City name"}
|
||||
},
|
||||
"required": ["location"]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
**Response example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "chatcmpl-xxx",
|
||||
"object": "chat.completion",
|
||||
"choices": [{
|
||||
"index": 0,
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": null,
|
||||
"tool_calls": [{
|
||||
"id": "call_xxx",
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "get_weather",
|
||||
"arguments": "{\"location\": \"Beijing\"}"
|
||||
}
|
||||
}]
|
||||
},
|
||||
"finish_reason": "tool_calls"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Claude-Compatible API
|
||||
|
||||
### Claude Model List
|
||||
|
||||
```http
|
||||
GET /anthropic/v1/models
|
||||
```
|
||||
|
||||
**Response example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"object": "list",
|
||||
"data": [
|
||||
{"id": "claude-sonnet-4-20250514", "object": "model", "owned_by": "anthropic"},
|
||||
{"id": "claude-sonnet-4-20250514-fast", "object": "model", "owned_by": "anthropic"},
|
||||
{"id": "claude-sonnet-4-20250514-slow", "object": "model", "owned_by": "anthropic"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Model mapping**:
|
||||
|
||||
| Claude Model | Actual | Notes |
|
||||
|------------|--------|------|
|
||||
| `claude-sonnet-4-20250514` | deepseek-chat | Standard mode |
|
||||
| `claude-sonnet-4-20250514-fast` | deepseek-chat | Fast mode |
|
||||
| `claude-sonnet-4-20250514-slow` | deepseek-reasoner | Reasoning mode |
|
||||
|
||||
---
|
||||
|
||||
### Claude Messages
|
||||
|
||||
```http
|
||||
POST /anthropic/v1/messages
|
||||
x-api-key: your-api-key
|
||||
Content-Type: application/json
|
||||
anthropic-version: 2023-06-01
|
||||
```
|
||||
|
||||
**Parameters**:
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----|------|:----:|------|
|
||||
| `model` | string | ✅ | Model name |
|
||||
| `max_tokens` | integer | ✅ | Max output tokens |
|
||||
| `messages` | array | ✅ | Chat messages |
|
||||
| `stream` | boolean | ❌ | Stream responses (default `false`) |
|
||||
| `system` | string | ❌ | System prompt |
|
||||
| `temperature` | number | ❌ | Temperature |
|
||||
|
||||
**Request example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello, please introduce yourself."}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Non-streaming response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "msg_xxx",
|
||||
"type": "message",
|
||||
"role": "assistant",
|
||||
"content": [{
|
||||
"type": "text",
|
||||
"text": "Hello! I'm an AI assistant..."
|
||||
}],
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"stop_reason": "end_turn",
|
||||
"usage": {
|
||||
"input_tokens": 10,
|
||||
"output_tokens": 50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Streaming response** (SSE):
|
||||
|
||||
```
|
||||
event: message_start
|
||||
data: {"type":"message_start","message":{"id":"msg_xxx","type":"message","role":"assistant","model":"claude-sonnet-4-20250514"}}
|
||||
|
||||
event: content_block_start
|
||||
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
|
||||
|
||||
event: content_block_delta
|
||||
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
|
||||
|
||||
event: content_block_stop
|
||||
data: {"type":"content_block_stop","index":0}
|
||||
|
||||
event: message_delta
|
||||
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":50}}
|
||||
|
||||
event: message_stop
|
||||
data: {"type":"message_stop"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Token Counting
|
||||
|
||||
```http
|
||||
POST /anthropic/v1/messages/count_tokens
|
||||
x-api-key: your-api-key
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"messages": [
|
||||
{"role": "user", "content": "Hello"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Response example**:
|
||||
|
||||
```json
|
||||
{
|
||||
"input_tokens": 5
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Admin API
|
||||
|
||||
All admin endpoints (except login) require `Authorization: Bearer <jwt-token>`.
|
||||
|
||||
### Login
|
||||
|
||||
```http
|
||||
POST /admin/login
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"key": "your-admin-key"
|
||||
}
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"token": "jwt-token-string",
|
||||
"expires_in": 86400
|
||||
}
|
||||
```
|
||||
|
||||
> Tokens are valid for 24 hours by default.
|
||||
|
||||
---
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Get configuration
|
||||
|
||||
```http
|
||||
GET /admin/config
|
||||
Authorization: Bearer <jwt-token>
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["api-key-1", "api-key-2"],
|
||||
"accounts": [
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "***",
|
||||
"token": "session-token"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Update configuration
|
||||
|
||||
```http
|
||||
POST /admin/config
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"keys": ["new-api-key"],
|
||||
"accounts": [...]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Account Management
|
||||
|
||||
#### Add account
|
||||
|
||||
```http
|
||||
POST /admin/accounts
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"password": "password123"
|
||||
}
|
||||
```
|
||||
|
||||
#### Batch import accounts
|
||||
|
||||
```http
|
||||
POST /admin/accounts/batch
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"accounts": [
|
||||
{"email": "user1@example.com", "password": "pass1"},
|
||||
{"email": "user2@example.com", "password": "pass2"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### Test one account
|
||||
|
||||
```http
|
||||
POST /admin/accounts/test
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body**:
|
||||
|
||||
```json
|
||||
{
|
||||
"email": "user@example.com"
|
||||
}
|
||||
```
|
||||
|
||||
#### Test all accounts
|
||||
|
||||
```http
|
||||
POST /admin/accounts/test-all
|
||||
Authorization: Bearer <jwt-token>
|
||||
```
|
||||
|
||||
#### Queue status
|
||||
|
||||
```http
|
||||
GET /admin/queue/status
|
||||
Authorization: Bearer <jwt-token>
|
||||
```
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"total_accounts": 5,
|
||||
"healthy_accounts": 4,
|
||||
"queue_size": 10,
|
||||
"accounts": [
|
||||
{
|
||||
"email": "user@example.com",
|
||||
"status": "healthy",
|
||||
"last_used": "2026-02-01T12:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Vercel Sync
|
||||
|
||||
```http
|
||||
POST /admin/vercel/sync
|
||||
Authorization: Bearer <jwt-token>
|
||||
Content-Type: application/json
|
||||
```
|
||||
|
||||
**Request body** (first sync only):
|
||||
|
||||
```json
|
||||
{
|
||||
"vercel_token": "your-vercel-token",
|
||||
"project_id": "your-project-id"
|
||||
}
|
||||
```
|
||||
|
||||
> After a successful first sync, credentials are stored for future syncs.
|
||||
|
||||
**Response**:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Configuration synced to Vercel"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
All error responses follow this structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"message": "Error description",
|
||||
"type": "error_type",
|
||||
"code": "error_code"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Common error codes**:
|
||||
|
||||
| HTTP Status | Error Type | Description |
|
||||
|:----------:|---------|------|
|
||||
| 400 | `invalid_request_error` | Invalid request parameters |
|
||||
| 401 | `authentication_error` | Missing or invalid API key |
|
||||
| 403 | `permission_denied` | Insufficient permissions |
|
||||
| 429 | `rate_limit_error` | Too many requests |
|
||||
| 500 | `internal_error` | Internal server error |
|
||||
| 503 | `service_unavailable` | No available accounts |
|
||||
|
||||
---
|
||||
|
||||
## Examples
|
||||
|
||||
### Python (OpenAI SDK)
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI(
|
||||
api_key="your-api-key",
|
||||
base_url="https://your-domain.com/v1"
|
||||
)
|
||||
|
||||
# Basic chat
|
||||
response = client.chat.completions.create(
|
||||
model="deepseek-chat",
|
||||
messages=[{"role": "user", "content": "Hello"}]
|
||||
)
|
||||
print(response.choices[0].message.content)
|
||||
|
||||
# Streaming + reasoning
|
||||
for chunk in client.chat.completions.create(
|
||||
model="deepseek-reasoner",
|
||||
messages=[{"role": "user", "content": "Explain relativity"}],
|
||||
stream=True
|
||||
):
|
||||
delta = chunk.choices[0].delta
|
||||
if hasattr(delta, 'reasoning_content') and delta.reasoning_content:
|
||||
print(f"[Reasoning] {delta.reasoning_content}", end="")
|
||||
if delta.content:
|
||||
print(delta.content, end="")
|
||||
```
|
||||
|
||||
### Python (Anthropic SDK)
|
||||
|
||||
```python
|
||||
import anthropic
|
||||
|
||||
client = anthropic.Anthropic(
|
||||
api_key="your-api-key",
|
||||
base_url="https://your-domain.com/anthropic"
|
||||
)
|
||||
|
||||
response = client.messages.create(
|
||||
model="claude-sonnet-4-20250514",
|
||||
max_tokens=1024,
|
||||
messages=[{"role": "user", "content": "Hello"}]
|
||||
)
|
||||
print(response.content[0].text)
|
||||
```
|
||||
|
||||
### cURL
|
||||
|
||||
```bash
|
||||
# OpenAI format
|
||||
curl https://your-domain.com/v1/chat/completions \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-api-key" \
|
||||
-d '{
|
||||
"model": "deepseek-chat",
|
||||
"messages": [{"role": "user", "content": "Hello"}]
|
||||
}'
|
||||
|
||||
# Claude format
|
||||
curl https://your-domain.com/anthropic/v1/messages \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "x-api-key: your-api-key" \
|
||||
-H "anthropic-version: 2023-06-01" \
|
||||
-d '{
|
||||
"model": "claude-sonnet-4-20250514",
|
||||
"max_tokens": 1024,
|
||||
"messages": [{"role": "user", "content": "Hello"}]
|
||||
}'
|
||||
```
|
||||
|
||||
### JavaScript / TypeScript
|
||||
|
||||
```javascript
|
||||
// OpenAI format - streaming request
|
||||
const response = await fetch('https://your-domain.com/v1/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer your-api-key'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: 'deepseek-chat-search',
|
||||
messages: [{ role: 'user', content: 'What is in the news today?' }],
|
||||
stream: true
|
||||
})
|
||||
});
|
||||
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
|
||||
|
||||
for (const line of lines) {
|
||||
const data = line.slice(6);
|
||||
if (data === '[DONE]') continue;
|
||||
|
||||
const json = JSON.parse(data);
|
||||
const content = json.choices?.[0]?.delta?.content;
|
||||
if (content) process.stdout.write(content);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Node.js (OpenAI SDK)
|
||||
|
||||
```javascript
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const client = new OpenAI({
|
||||
apiKey: 'your-api-key',
|
||||
baseURL: 'https://your-domain.com/v1'
|
||||
});
|
||||
|
||||
const stream = await client.chat.completions.create({
|
||||
model: 'deepseek-reasoner',
|
||||
messages: [{ role: 'user', content: 'Explain black holes' }],
|
||||
stream: true
|
||||
});
|
||||
|
||||
for await (const chunk of stream) {
|
||||
const content = chunk.choices[0]?.delta?.content;
|
||||
if (content) process.stdout.write(content);
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user