- 添加完整的项目基础结构,包括配置、类型定义和常量 - 实现OAuth认证流程和令牌管理 - 开发请求转换和响应处理逻辑 - 添加SSE流处理和ChatCompletions API转换 - 实现模型映射和提示指令系统 - 包含Docker部署配置和快速启动文档 - 添加自动登录功能和测试脚本
775 lines
21 KiB
Markdown
775 lines
21 KiB
Markdown
# chatgpt-codex-router 项目计划
|
||
|
||
## 项目概览
|
||
|
||
**项目目标:** 创建一个独立的 OpenAI 兼容 API 服务器,通过 OAuth 认证将请求转发到 ChatGPT 后端,支持流式传输和详细的日志记录。
|
||
|
||
**支持的模型(GPT-5.x 系列):**
|
||
- `gpt-5.1` (none/low/medium/high)
|
||
- `gpt-5.2` (none/low/medium/high/xhigh)
|
||
- `gpt-5.1-codex` (low/medium/high)
|
||
- `gpt-5.1-codex-max` (low/medium/high/xhigh)
|
||
- `gpt-5.1-codex-mini` (medium/high)
|
||
- `gpt-5.2-codex` (low/medium/high/xhigh)
|
||
|
||
---
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
chatgpt-codex-router/
|
||
├── src/
|
||
│ ├── index.ts # 服务器入口
|
||
│ ├── server.ts # Hono 服务器配置
|
||
│ ├── router.ts # API 路由定义
|
||
│ ├── config.ts # 配置管理
|
||
│ ├── logger.ts # 日志系统
|
||
│ ├── auth/ # 认证模块
|
||
│ │ ├── oauth.ts # OAuth 流程逻辑
|
||
│ │ ├── token-storage.ts # Token 本地 JSON 存储
|
||
│ │ ├── token-refresh.ts # Token 刷新逻辑
|
||
│ │ ├── server.ts # 本地 OAuth 回调服务器
|
||
│ │ └── browser.ts # 浏览器打开工具
|
||
│ ├── request/ # 请求处理
|
||
│ │ ├── transformer.ts # 请求体转换
|
||
│ │ ├── headers.ts # Header 生成
|
||
│ │ ├── validator.ts # 请求验证
|
||
│ │ ├── model-map.ts # 模型映射
|
||
│ │ └── reasoning.ts # 推理参数配置
|
||
│ ├── response/ # 响应处理
|
||
│ │ ├── handler.ts # 响应处理器
|
||
│ │ ├── sse-parser.ts # SSE 流解析
|
||
│ │ ├── converter.ts # 响应格式转换
|
||
│ │ └── chat-completions.ts # Chat Completions 格式转换器
|
||
│ ├── prompts/ # 内置 Codex Prompts
|
||
│ │ ├── gpt-5-1.md # GPT-5.1 系统提示
|
||
│ │ ├── gpt-5-2.md # GPT-5.2 系统提示
|
||
│ │ ├── gpt-5-1-codex.md # GPT-5.1 Codex 系统提示
|
||
│ │ ├── gpt-5-1-codex-max.md # GPT-5.1 Codex Max 系统提示
|
||
│ │ ├── gpt-5-1-codex-mini.md # GPT-5.1 Codex Mini 系统提示
|
||
│ │ ├── gpt-5-2-codex.md # GPT-5.2 Codex 系统提示
|
||
│ │ └── index.ts # Prompt 加载器
|
||
│ ├── constants.ts # 常量定义
|
||
│ └── types.ts # TypeScript 类型定义
|
||
├── public/
|
||
│ └── oauth-success.html # OAuth 成功页面
|
||
├── docker/
|
||
│ ├── Dockerfile # Docker 镜像配置
|
||
│ ├── docker-compose.yml # Docker Compose 配置
|
||
│ └── .dockerignore # Docker 忽略文件
|
||
├── logs/ # 日志输出目录(.gitignore)
|
||
├── data/ # 数据目录(.gitignore,存储 tokens)
|
||
├── package.json
|
||
├── tsconfig.json
|
||
├── .gitignore
|
||
└── README.md
|
||
```
|
||
|
||
---
|
||
|
||
## 核心功能模块详解
|
||
|
||
### 1. 认证模块 (`src/auth/`)
|
||
|
||
#### 1.1 OAuth 流程 (`oauth.ts`)
|
||
**功能:**
|
||
- PKCE challenge 生成
|
||
- 授权 URL 构建
|
||
- Authorization code 交换为 tokens
|
||
- JWT 解析获取 account_id
|
||
|
||
**关键常量:**
|
||
```typescript
|
||
CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann"
|
||
AUTHORIZE_URL = "https://auth.openai.com/oauth/authorize"
|
||
TOKEN_URL = "https://auth.openai.com/oauth/token"
|
||
REDIRECT_URI = "http://localhost:1455/auth/callback"
|
||
SCOPE = "openid profile email offline_access"
|
||
```
|
||
|
||
#### 1.2 Token 存储 (`token-storage.ts`)
|
||
**功能:**
|
||
- 读取/保存/删除 tokens
|
||
- 存储路径:`data/tokens.json`
|
||
- 存储格式:
|
||
```json
|
||
{
|
||
"access_token": "...",
|
||
"refresh_token": "...",
|
||
"expires_at": 1234567890,
|
||
"account_id": "...",
|
||
"updated_at": 1234567890
|
||
}
|
||
```
|
||
|
||
#### 1.3 Token 刷新 (`token-refresh.ts`)
|
||
**功能:**
|
||
- 检查 token 是否过期(提前 5 分钟刷新)
|
||
- 自动刷新过期的 token
|
||
- 更新本地存储
|
||
- 刷新失败时抛出错误
|
||
|
||
#### 1.4 本地 OAuth 服务器 (`server.ts`)
|
||
**功能:**
|
||
- 监听 `http://127.0.0.1:1455/auth/callback`
|
||
- 接收并验证 authorization code
|
||
- 返回 OAuth 成功页面
|
||
- Polling 机制(最多等待 60 秒)
|
||
|
||
#### 1.5 浏览器工具 (`browser.ts`)
|
||
**功能:**
|
||
- 跨平台浏览器打开(macOS/Linux/Windows)
|
||
- 静默失败(用户可手动复制 URL)
|
||
|
||
---
|
||
|
||
### 2. 请求处理模块 (`src/request/`)
|
||
|
||
#### 2.1 请求体转换 (`transformer.ts`)
|
||
**核心转换逻辑:**
|
||
|
||
```typescript
|
||
// 原始请求
|
||
{
|
||
"model": "gpt-5.2-codex",
|
||
"messages": [...],
|
||
"stream": false,
|
||
"temperature": 0.7
|
||
}
|
||
|
||
// 转换后请求
|
||
{
|
||
"model": "gpt-5.2-codex",
|
||
"input": [...], // messages 转换为 input 格式
|
||
"stream": true, // 强制 stream=true
|
||
"store": false, // 添加 store=false
|
||
"instructions": "...", // 添加 Codex 系统提示
|
||
"reasoning": {
|
||
"effort": "high",
|
||
"summary": "auto"
|
||
},
|
||
"text": {
|
||
"verbosity": "medium"
|
||
},
|
||
"include": ["reasoning.encrypted_content"]
|
||
}
|
||
```
|
||
|
||
**转换步骤:**
|
||
1. 模型名称标准化(使用 model-map)
|
||
2. `messages` → `input` 格式转换
|
||
3. 过滤 `item_reference` 和 IDs
|
||
4. 添加 `store: false`, `stream: true`
|
||
5. 添加 Codex 系统提示(从内置 prompts 加载)
|
||
6. 配置 reasoning 参数(根据模型类型)
|
||
7. 配置 text verbosity
|
||
8. 添加 include 参数
|
||
9. 移除不支持的参数(`max_output_tokens`, `max_completion_tokens`)
|
||
|
||
#### 2.2 模型映射 (`model-map.ts`)
|
||
**支持的模型:**
|
||
```typescript
|
||
const MODEL_MAP: Record<string, string> = {
|
||
// GPT-5.1
|
||
"gpt-5.1": "gpt-5.1",
|
||
"gpt-5.1-none": "gpt-5.1",
|
||
"gpt-5.1-low": "gpt-5.1",
|
||
"gpt-5.1-medium": "gpt-5.1",
|
||
"gpt-5.1-high": "gpt-5.1",
|
||
|
||
// GPT-5.2
|
||
"gpt-5.2": "gpt-5.2",
|
||
"gpt-5.2-none": "gpt-5.2",
|
||
"gpt-5.2-low": "gpt-5.2",
|
||
"gpt-5.2-medium": "gpt-5.2",
|
||
"gpt-5.2-high": "gpt-5.2",
|
||
"gpt-5.2-xhigh": "gpt-5.2",
|
||
|
||
// GPT-5.1 Codex
|
||
"gpt-5.1-codex": "gpt-5.1-codex",
|
||
"gpt-5.1-codex-low": "gpt-5.1-codex",
|
||
"gpt-5.1-codex-medium": "gpt-5.1-codex",
|
||
"gpt-5.1-codex-high": "gpt-5.1-codex",
|
||
|
||
// GPT-5.1 Codex Max
|
||
"gpt-5.1-codex-max": "gpt-5.1-codex-max",
|
||
"gpt-5.1-codex-max-low": "gpt-5.1-codex-max",
|
||
"gpt-5.1-codex-max-medium": "gpt-5.1-codex-max",
|
||
"gpt-5.1-codex-max-high": "gpt-5.1-codex-max",
|
||
"gpt-5.1-codex-max-xhigh": "gpt-5.1-codex-max",
|
||
|
||
// GPT-5.1 Codex Mini
|
||
"gpt-5.1-codex-mini": "gpt-5.1-codex-mini",
|
||
"gpt-5.1-codex-mini-medium": "gpt-5.1-codex-mini",
|
||
"gpt-5.1-codex-mini-high": "gpt-5.1-codex-mini",
|
||
|
||
// GPT-5.2 Codex
|
||
"gpt-5.2-codex": "gpt-5.2-codex",
|
||
"gpt-5.2-codex-low": "gpt-5.2-codex",
|
||
"gpt-5.2-codex-medium": "gpt-5.2-codex",
|
||
"gpt-5.2-codex-high": "gpt-5.2-codex",
|
||
"gpt-5.2-codex-xhigh": "gpt-5.2-codex"
|
||
};
|
||
```
|
||
|
||
#### 2.3 Header 生成 (`headers.ts`)
|
||
**Headers 列表:**
|
||
```typescript
|
||
{
|
||
"Authorization": `Bearer ${accessToken}`,
|
||
"chatgpt-account-id": accountId,
|
||
"OpenAI-Beta": "responses=experimental",
|
||
"originator": "codex_cli_rs",
|
||
"session_id": promptCacheKey,
|
||
"conversation_id": promptCacheKey,
|
||
"accept": "text/event-stream",
|
||
"content-type": "application/json"
|
||
}
|
||
```
|
||
|
||
#### 2.4 Reasoning 配置 (`reasoning.ts`)
|
||
**Reasoning effort 配置:**
|
||
- `gpt-5.2`, `gpt-5.1`: 支持 `none/low/medium/high`(默认 `none`)
|
||
- `gpt-5.2-codex`, `gpt-5.1-codex-max`: 支持 `low/medium/high/xhigh`(默认 `high`)
|
||
- `gpt-5.1-codex`: 支持 `low/medium/high`(默认 `medium`)
|
||
- `gpt-5.1-codex-mini`: 支持 `medium/high`(默认 `medium`)
|
||
|
||
#### 2.5 请求验证 (`validator.ts`)
|
||
**验证内容:**
|
||
- `model` 参数是否存在且有效
|
||
- `messages` 或 `input` 是否存在
|
||
- 消息格式是否正确
|
||
- 必需字段是否存在
|
||
|
||
---
|
||
|
||
### 3. 响应处理模块 (`src/response/`)
|
||
|
||
#### 3.1 响应处理器 (`handler.ts`)
|
||
**处理流程:**
|
||
|
||
```typescript
|
||
async function handleResponse(response: Response, isStreaming: boolean): Promise<Response> {
|
||
if (isStreaming) {
|
||
// 流式响应:直接转发 SSE
|
||
return forwardStream(response);
|
||
} else {
|
||
// 非流式:解析 SSE 并转换为 JSON
|
||
return parseSseToJson(response);
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 3.2 SSE 解析器 (`sse-parser.ts`)
|
||
**解析逻辑:**
|
||
```typescript
|
||
function parseSseStream(sseText: string): Response | null {
|
||
const lines = sseText.split('\n');
|
||
for (const line of lines) {
|
||
if (line.startsWith('data: ')) {
|
||
const data = JSON.parse(line.substring(6));
|
||
if (data.type === 'response.done' || data.type === 'response.completed') {
|
||
return data.response;
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
```
|
||
|
||
#### 3.3 响应格式转换 (`converter.ts` & `chat-completions.ts`)
|
||
|
||
**ChatGPT Responses API 格式 → OpenAI Chat Completions 格式:**
|
||
|
||
**原始格式(ChatGPT):**
|
||
```json
|
||
{
|
||
"type": "response.done",
|
||
"response": {
|
||
"id": "resp_...",
|
||
"status": "completed",
|
||
"output": [
|
||
{
|
||
"type": "message",
|
||
"role": "assistant",
|
||
"content": [
|
||
{
|
||
"type": "output_text",
|
||
"text": "Hello, world!"
|
||
}
|
||
]
|
||
}
|
||
],
|
||
"usage": {
|
||
"input_tokens": 100,
|
||
"output_tokens": 50,
|
||
"total_tokens": 150
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**转换后格式(OpenAI):**
|
||
```json
|
||
{
|
||
"id": "resp_...",
|
||
"object": "chat.completion",
|
||
"created": 1736153600,
|
||
"model": "gpt-5.2-codex",
|
||
"choices": [
|
||
{
|
||
"index": 0,
|
||
"message": {
|
||
"role": "assistant",
|
||
"content": "Hello, world!"
|
||
},
|
||
"finish_reason": "stop"
|
||
}
|
||
],
|
||
"usage": {
|
||
"prompt_tokens": 100,
|
||
"completion_tokens": 50,
|
||
"total_tokens": 150
|
||
}
|
||
}
|
||
```
|
||
|
||
**流式响应格式转换:**
|
||
|
||
**原始 SSE 事件(ChatGPT):**
|
||
```
|
||
data: {"type": "response.output_item.add.delta", "delta": {"content": [{"type": "output_text", "text": "Hello"}]}}
|
||
data: {"type": "response.output_item.add.delta", "delta": {"content": [{"type": "output_text", "text": ", world!"}]}}
|
||
data: {"type": "response.done", "response": {...}}
|
||
```
|
||
|
||
**转换后 SSE 事件(OpenAI):**
|
||
```
|
||
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {"content": "Hello"}, "finish_reason": null}]}
|
||
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {"content": ", world!"}, "finish_reason": null}]}
|
||
data: {"id": "...", "object": "chat.completion.chunk", "created": 1736153600, "model": "gpt-5.2-codex", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}
|
||
```
|
||
|
||
---
|
||
|
||
### 4. 内置 Prompts (`src/prompts/`)
|
||
|
||
**文件结构:**
|
||
- `gpt-5-1.md` - GPT-5.1 通用模型系统提示
|
||
- `gpt-5-2.md` - GPT-5.2 通用模型系统提示
|
||
- `gpt-5-1-codex.md` - GPT-5.1 Codex 系统提示
|
||
- `gpt-5-1-codex-max.md` - GPT-5.1 Codex Max 系统提示
|
||
- `gpt-5-1-codex-mini.md` - GPT-5.1 Codex Mini 系统提示
|
||
- `gpt-5-2-codex.md` - GPT-5.2 Codex 系统提示
|
||
|
||
**Prompt 加载器 (`index.ts`):**
|
||
```typescript
|
||
export function getPrompt(modelFamily: ModelFamily): string {
|
||
const promptFile = PROMPT_FILES[modelFamily];
|
||
return readFileSync(join(__dirname, promptFile), 'utf-8');
|
||
}
|
||
```
|
||
|
||
**Prompts 内容:** 需要从 Codex CLI GitHub 仓库下载最新的 prompts 并嵌入到项目中。
|
||
|
||
---
|
||
|
||
### 5. 日志系统 (`src/logger.ts`)
|
||
|
||
**日志级别:**
|
||
- `ERROR` - 错误日志(始终记录)
|
||
- `WARN` - 警告日志(始终记录)
|
||
- `INFO` - 信息日志(通过 `LOG_LEVEL=info` 启用)
|
||
- `DEBUG` - 调试日志(通过 `LOG_LEVEL=debug` 启用)
|
||
|
||
**日志配置:**
|
||
- 日志目录:`logs/`
|
||
- 日志文件格式:`{date}-{level}.log`
|
||
- 日志格式:`[timestamp] [level] [request-id] message`
|
||
- 控制台输出:带颜色和结构化数据
|
||
|
||
**日志内容:**
|
||
- 请求开始/结束
|
||
- Token 刷新
|
||
- 请求转换前后
|
||
- 响应状态
|
||
- 错误详情(请求/响应体)
|
||
|
||
**示例日志:**
|
||
```
|
||
[2025-01-06 10:30:00] [INFO] [req-001] POST /v1/chat/completions
|
||
[2025-01-06 10:30:00] [DEBUG] [req-001] Before transform: {"model": "gpt-5.2-codex", "messages": [...]}
|
||
[2025-01-06 10:30:00] [DEBUG] [req-001] After transform: {"model": "gpt-5.2-codex", "input": [...], "stream": true, "store": false}
|
||
[2025-01-06 10:30:01] [INFO] [req-001] Response: 200 OK, 150 tokens
|
||
[2025-01-06 10:30:01] [INFO] [req-001] Request completed in 1234ms
|
||
```
|
||
|
||
---
|
||
|
||
### 6. API 端点
|
||
|
||
#### 6.1 `POST /v1/chat/completions`
|
||
**请求:** OpenAI Chat Completions 格式
|
||
**响应:** OpenAI Chat Completions 格式(转换后)
|
||
|
||
**流程:**
|
||
1. 验证请求体
|
||
2. 检查 token,自动刷新
|
||
3. 转换请求体(messages → input)
|
||
4. 生成 Headers
|
||
5. 转发到 `https://chatgpt.com/backend-api/codex/responses`
|
||
6. 处理响应(流式/非流式)
|
||
7. 转换响应格式(ChatGPT → OpenAI)
|
||
|
||
#### 6.2 `POST /v1/responses`
|
||
**请求:** OpenAI Responses API 格式
|
||
**响应:** OpenAI Responses API 格式(部分转换)
|
||
|
||
**流程:**
|
||
1. 验证请求体
|
||
2. 检查并刷新 token
|
||
3. 转换请求体(添加必需字段)
|
||
4. 生成 Headers
|
||
5. 转发到 `https://chatgpt.com/backend-api/codex/responses`
|
||
6. 处理响应(流式/非流式)
|
||
7. 返回转换后的 Responses API 格式
|
||
|
||
#### 6.3 `POST /auth/login`
|
||
**请求:** 无需参数
|
||
**响应:** 授权 URL 和说明
|
||
|
||
**流程:**
|
||
1. 生成 PKCE challenge 和 state
|
||
2. 构建授权 URL
|
||
3. 启动本地 OAuth 服务器
|
||
4. 尝试打开浏览器
|
||
5. 返回授权信息
|
||
|
||
#### 6.4 `POST /auth/callback`
|
||
**内部端点:** 仅用于本地 OAuth 服务器
|
||
- 接收 authorization code
|
||
- 验证 state
|
||
- 交换 tokens
|
||
- 保存到 `data/tokens.json`
|
||
- 返回成功页面
|
||
|
||
---
|
||
|
||
### 7. 配置管理 (`src/config.ts`)
|
||
|
||
**配置文件:** `~/.chatgpt-codex-router/config.json`
|
||
|
||
**默认配置:**
|
||
```json
|
||
{
|
||
"server": {
|
||
"port": 3000,
|
||
"host": "0.0.0.0"
|
||
},
|
||
"oauth": {
|
||
"clientId": "app_EMoamEEZ73f0CkXaXp7hrann",
|
||
"redirectUri": "http://localhost:1455/auth/callback",
|
||
"localServerPort": 1455
|
||
},
|
||
"backend": {
|
||
"url": "https://chatgpt.com/backend-api",
|
||
"timeout": 120000
|
||
},
|
||
"logging": {
|
||
"level": "info",
|
||
"dir": "logs",
|
||
"enableRequestLogging": false
|
||
},
|
||
"codex": {
|
||
"mode": true,
|
||
"defaultReasoningEffort": "medium",
|
||
"defaultTextVerbosity": "medium"
|
||
}
|
||
}
|
||
```
|
||
|
||
**环境变量:**
|
||
- `PORT` - 服务器端口(默认 3000)
|
||
- `CONFIG_PATH` - 配置文件路径
|
||
- `LOG_LEVEL` - 日志级别(error/warn/info/debug)
|
||
- `ENABLE_REQUEST_LOGGING` - 启用请求日志(true/false)
|
||
|
||
---
|
||
|
||
### 8. Docker 支持 (`docker/`)
|
||
|
||
#### 8.1 Dockerfile
|
||
```dockerfile
|
||
FROM node:20-alpine
|
||
|
||
WORKDIR /app
|
||
|
||
# 安装依赖
|
||
COPY package.json package-lock.json ./
|
||
RUN npm ci --only=production
|
||
|
||
# 复制源代码
|
||
COPY src/ ./src/
|
||
COPY public/ ./public/
|
||
COPY tsconfig.json ./
|
||
|
||
# 构建项目
|
||
RUN npm run build
|
||
|
||
# 创建数据和日志目录
|
||
RUN mkdir -p /app/data /app/logs
|
||
|
||
# 暴露端口
|
||
EXPOSE 3000
|
||
|
||
# 健康检查
|
||
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||
CMD node -e "require('http').get('http://localhost:3000/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||
|
||
# 启动服务
|
||
CMD ["npm", "start"]
|
||
```
|
||
|
||
#### 8.2 docker-compose.yml
|
||
```yaml
|
||
version: '3.8'
|
||
|
||
services:
|
||
chatgpt-codex-router:
|
||
build: .
|
||
container_name: chatgpt-codex-router
|
||
ports:
|
||
- "3000:3000"
|
||
- "1455:1455"
|
||
volumes:
|
||
- ./data:/app/data
|
||
- ./logs:/app/logs
|
||
- ./config.json:/app/.chatgpt-codex-router/config.json:ro
|
||
environment:
|
||
- PORT=3000
|
||
- LOG_LEVEL=info
|
||
restart: unless-stopped
|
||
healthcheck:
|
||
test: ["CMD", "node", "-e", "require('http').get('http://localhost:3000/health')"]
|
||
interval: 30s
|
||
timeout: 10s
|
||
retries: 3
|
||
start_period: 5s
|
||
```
|
||
|
||
#### 8.3 .dockerignore
|
||
```
|
||
node_modules
|
||
npm-debug.log
|
||
.git
|
||
.gitignore
|
||
README.md
|
||
.dockerignore
|
||
Dockerfile
|
||
docker-compose.yml
|
||
test
|
||
vitest.config.ts
|
||
logs/*
|
||
data/*
|
||
!data/.gitkeep
|
||
```
|
||
|
||
---
|
||
|
||
## 开发步骤
|
||
|
||
### 阶段 1:项目初始化(第 1-2 天)
|
||
- [ ] 创建项目结构
|
||
- [ ] 配置 TypeScript
|
||
- [ ] 配置 package.json(依赖、脚本)
|
||
- [ ] 创建 .gitignore
|
||
- [ ] 设置开发环境(ESLint、Prettier)
|
||
|
||
### 阶段 2:基础设施(第 3-4 天)
|
||
- [ ] 实现日志系统 (`logger.ts`)
|
||
- [ ] 实现配置管理 (`config.ts`)
|
||
- [ ] 实现常量定义 (`constants.ts`)
|
||
- [ ] 实现类型定义 (`types.ts`)
|
||
- [ ] 创建基础服务器框架 (`server.ts`, `index.ts`)
|
||
|
||
### 阶段 3:认证模块(第 5-7 天)
|
||
- [ ] 实现 OAuth 流程 (`oauth.ts`)
|
||
- [ ] 实现 Token 存储 (`token-storage.ts`)
|
||
- [ ] 实现 Token 刷新 (`token-refresh.ts`)
|
||
- [ ] 实现本地 OAuth 服务器 (`server.ts`)
|
||
- [ ] 实现浏览器工具 (`browser.ts`)
|
||
- [ ] 创建 OAuth 成功页面 (`public/oauth-success.html`)
|
||
|
||
### 阶段 4:请求处理(第 8-10 天)
|
||
- [ ] 实现模型映射 (`model-map.ts`)
|
||
- [ ] 实现 Reasoning 配置 (`reasoning.ts`)
|
||
- [ ] 实现请求体转换 (`transformer.ts`)
|
||
- [ ] 实现 Header 生成 (`headers.ts`)
|
||
- [ ] 实现请求验证 (`validator.ts`)
|
||
- [ ] 从 GitHub 下载并内置 Codex prompts
|
||
|
||
### 阶段 5:响应处理(第 11-13 天)
|
||
- [ ] 实现 SSE 解析器 (`sse-parser.ts`)
|
||
- [ ] 实现响应格式转换 (`converter.ts`)
|
||
- [ ] 实现 Chat Completions 格式转换器 (`chat-completions.ts`)
|
||
- [ ] 实现响应处理器 (`handler.ts`)
|
||
- [ ] 实现流式/非流式响应处理
|
||
|
||
### 阶段 6:API 端点(第 14-16 天)
|
||
- [ ] 实现 `/v1/chat/completions`
|
||
- [ ] 实现 `/v1/responses`
|
||
- [ ] 实现 `/auth/login`
|
||
- [ ] 实现 `/health` 健康检查端点
|
||
- [ ] 测试所有端点
|
||
|
||
### 阶段 7:Docker 支持(第 17-18 天)
|
||
- [ ] 创建 Dockerfile
|
||
- [ ] 创建 docker-compose.yml
|
||
- [ ] 创建 .dockerignore
|
||
- [ ] 测试 Docker 构建
|
||
- [ ] 编写 Docker 使用文档
|
||
|
||
### 阶段 8:测试和优化(第 19-21 天)
|
||
- [ ] 单元测试
|
||
- [ ] 集成测试
|
||
- [ ] 性能测试
|
||
- [ ] 日志优化
|
||
- [ ] 错误处理优化
|
||
|
||
### 阶段 9:文档和发布(第 22-23 天)
|
||
- [ ] 编写 README
|
||
- [ ] 编写 API 文档
|
||
- [ ] 编写 Docker 文档
|
||
- [ ] 准备 NPM 发布
|
||
|
||
---
|
||
|
||
## 依赖项
|
||
|
||
### 生产依赖
|
||
```json
|
||
{
|
||
"hono": "^4.10.4",
|
||
"@openauthjs/openauth": "^0.4.3",
|
||
"dotenv": "^16.4.5"
|
||
}
|
||
```
|
||
|
||
### 开发依赖
|
||
```json
|
||
{
|
||
"@types/node": "^24.6.2",
|
||
"typescript": "^5.9.3",
|
||
"vitest": "^3.2.4",
|
||
"eslint": "^9.15.0",
|
||
"prettier": "^3.4.2",
|
||
"@typescript-eslint/eslint-plugin": "^8.15.0"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 关键实现细节
|
||
|
||
### 1. Messages → Input 转换
|
||
|
||
**OpenAI Chat Completions 格式:**
|
||
```json
|
||
{
|
||
"messages": [
|
||
{"role": "user", "content": "Hello"}
|
||
]
|
||
}
|
||
```
|
||
|
||
**ChatGPT Responses API 格式:**
|
||
```json
|
||
{
|
||
"input": [
|
||
{"type": "message", "role": "user", "content": [{"type": "input_text", "text": "Hello"}]}
|
||
]
|
||
}
|
||
```
|
||
|
||
**转换逻辑:**
|
||
```typescript
|
||
function messagesToInput(messages: Message[]): InputItem[] {
|
||
return messages.map(msg => ({
|
||
type: "message",
|
||
role: msg.role,
|
||
content: Array.isArray(msg.content)
|
||
? msg.content.map(c => ({ type: "input_text", text: c.text }))
|
||
: [{ type: "input_text", text: msg.content }]
|
||
}));
|
||
}
|
||
```
|
||
|
||
### 2. 流式 SSE 转换
|
||
|
||
**转换逻辑:**
|
||
```typescript
|
||
async function transformSseStream(
|
||
reader: ReadableStreamDefaultReader,
|
||
model: string
|
||
): ReadableStream {
|
||
const encoder = new TextEncoder();
|
||
|
||
return new ReadableStream({
|
||
async start(controller) {
|
||
let buffer = '';
|
||
|
||
while (true) {
|
||
const { done, value } = await reader.read();
|
||
if (done) break;
|
||
|
||
buffer += decoder.decode(value, { stream: true });
|
||
const lines = buffer.split('\n');
|
||
buffer = lines.pop() || '';
|
||
|
||
for (const line of lines) {
|
||
if (line.startsWith('data: ')) {
|
||
const data = JSON.parse(line.substring(6));
|
||
const transformed = transformChunk(data, model);
|
||
controller.enqueue(encoder.encode(`data: ${JSON.stringify(transformed)}\n\n`));
|
||
}
|
||
}
|
||
}
|
||
|
||
controller.close();
|
||
}
|
||
});
|
||
}
|
||
```
|
||
|
||
### 3. Token 刷新时机
|
||
|
||
**刷新策略:**
|
||
- 提前 5 分钟刷新(`expires_at - 300000 < Date.now()`)
|
||
- 每次请求前检查
|
||
- 刷新失败时返回 401
|
||
- 刷新成功后更新本地存储
|
||
|
||
---
|
||
|
||
## 测试计划
|
||
|
||
### 单元测试
|
||
- [ ] OAuth 流程测试
|
||
- [ ] Token 存储测试
|
||
- [ ] 请求转换测试
|
||
- [ ] 响应转换测试
|
||
- [ ] SSE 解析测试
|
||
|
||
### 集成测试
|
||
- [ ] 完整 OAuth 流程测试
|
||
- [ ] Chat Completions 端点测试(流式/非流式)
|
||
- [ ] Responses API 端点测试(流式/非流式)
|
||
- [ ] Token 自动刷新测试
|
||
- [ ] 错误处理测试
|
||
|
||
### 手动测试
|
||
- [ ] 使用 curl 测试所有端点
|
||
- [ ] 使用 OpenAI SDK 测试兼容性
|
||
- [ ] 使用不同模型测试
|
||
- [ ] Docker 部署测试
|