wulingling / README.md
JXJBing's picture
Update README.md
25c2b7e verified
---
title: test
colorFrom: purple
colorTo: gray
sdk: docker
app_port: 8890
emoji: 🚀
---
# OpenAI兼容的Sora API服务
这是一个为Sora提供OpenAI兼容接口的API服务。该服务使用cloudscraper绕过Cloudflare验证,支持多key轮询、并发处理和标准的OpenAI接口格式。
## 功能特点
- **OpenAI兼容接口**:完全兼容OpenAI的`/v1/chat/completions`接口
- **CF验证绕过**:使用cloudscraper库成功绕过Cloudflare验证
- **多key轮询**:支持多个Sora认证token,根据权重和速率限制智能选择
- **并发处理**:支持多个并发请求
- **流式响应**:支持SSE格式的流式响应
- **图像处理**:支持文本到图像生成和图像到图像生成(Remix)
- **异步处理**:支持异步生成图像,返回立即响应,防止请求超时
- **状态查询**:提供API端点查询异步任务的状态和结果
- **优化性能**:经过代码优化,提高请求处理速度和资源利用率
- **健康检查**:支持容器健康检查功能
## 环境要求
- Python 3.8+
- FastAPI 0.95.0+
- cloudscraper 1.2.71+
- 其他依赖见requirements.txt
## 快速部署指南
你可以直接运行不指定任何环境变量,所有环境变量都可以在面板里面配置
管理员登录密钥默认:sk-123456
api请求密钥不设置默认和管理员登录密钥相同
docker 一键运行
```bash
docker run -d -p 8890:8890 --name sora-api 1hei1/sora-api:latest
```
### 方法一:直接运行
1. 克隆仓库
```bash
git clone https://github.com/1hei1/sora-api.git
cd sora-api
```
2. 安装依赖
```bash
pip install -r requirements.txt
```
3. 配置API Keys(两种方式)
- **方式1**: 创建api_keys.json文件
```json
[
{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60},
{"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}
]
```
- **方式2**: 设置环境变量
```bash
# Linux/macOS
export API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' 
# Windows (PowerShell)
$env:API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]'
# Windows (CMD)
set API_KEYS=[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}] 
```
4. 配置代理(可选,如果需要)
```bash
# Linux/macOS - 基本代理
export PROXY_HOST=127.0.0.1
export PROXY_PORT=7890
# Linux/macOS - 带认证的代理
export PROXY_HOST=127.0.0.1
export PROXY_PORT=7890
export PROXY_USER=username
export PROXY_PASS=password
# Windows (PowerShell) - 基本代理
$env:PROXY_HOST="127.0.0.1"
$env:PROXY_PORT="7890"
# Windows (PowerShell) - 带认证的代理
$env:PROXY_HOST="127.0.0.1"
$env:PROXY_PORT="7890"
$env:PROXY_USER="username"
$env:PROXY_PASS="password"
# Windows (CMD) - 基本代理
set PROXY_HOST=127.0.0.1
set PROXY_PORT=7890
# Windows (CMD) - 带认证的代理
set PROXY_HOST=127.0.0.1
set PROXY_PORT=7890
set PROXY_USER=username
set PROXY_PASS=password
```
5. 启动服务
```bash
python run.py
```
6. 访问服务
- API服务地址: http://localhost:8890
- 后台管理面板: http://localhost:8890/admin
### 方法二:Docker部署
1. 构建Docker镜像
```bash
docker build -t sora-api .
```
2. 运行Docker容器(不同配置选项)
**基本运行方式**:
```bash
docker run -d -p 8890:8890 --name sora-api sora-api
```
**使用预打包镜像**:
```bash
docker run -d -p 8890:8890 --name sora-api 1hei1/sora-api:latest
```
**使用预打包镜像并配置API密钥**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' \
--name sora-api \
1hei1/sora-api:v0.1
```
**使用预打包镜像并配置代理**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \
-e PROXY_HOST=host.docker.internal \
-e PROXY_PORT=7890 \
--name sora-api \
1hei1/sora-api:v0.1
```
**带API密钥配置**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}, {"key": "Bearer your-sora-token-2", "weight": 2, "max_rpm": 60}]' \
--name sora-api \
sora-api
```
**带基本代理配置**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \
-e PROXY_HOST=host.docker.internal \
-e PROXY_PORT=7890 \
--name sora-api \
sora-api
```
**带认证代理配置**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \
-e PROXY_HOST=host.docker.internal \
-e PROXY_PORT=7890 \
-e PROXY_USER=username \
-e PROXY_PASS=password \
--name sora-api \
sora-api
```
**使用外部配置文件**:
```bash
# 首先确保api_keys.json文件已正确配置
docker run -d -p 8890:8890 \
-v $(pwd)/api_keys.json:/app/api_keys.json \
-e PROXY_HOST=host.docker.internal \
-e PROXY_PORT=7890 \
--name sora-api \
sora-api
```
**挂载本地目录保存图片**:
```bash
docker run -d -p 8890:8890 \
-v /your/local/path:/app/src/static/images \
--name sora-api \
sora-api
```
**启用详细日志**:
```bash
docker run -d -p 8890:8890 \
-e API_KEYS='[{"key": "Bearer your-sora-token-1", "weight": 1, "max_rpm": 60}]' \
-e VERBOSE_LOGGING=true \
--name sora-api \
sora-api
```
**注意**: 在Docker中使用宿主机代理时,请使用`host.docker.internal`而不是`127.0.0.1`作为代理主机地址。
3. 检查容器状态
```bash
docker ps
docker logs sora-api
```
4. 停止和移除容器
```bash
docker stop sora-api
docker rm sora-api
```
## 环境变量说明
| 环境变量 | 描述 | 默认值 | 示例 |
|---------|------|--------|------|
| `API_HOST` | API服务监听地址 | `0.0.0.0` | `127.0.0.1` |
| `API_PORT` | API服务端口 | `8890` | `9000` |
| `BASE_URL` | API基础URL(生成图片的时候需要用到) | `http://0.0.0.0:8890` | `https://api.example.com`
| `PROXY_HOST` | HTTP代理主机 | 空(不使用代理) | `127.0.0.1` |
| `PROXY_PORT` | HTTP代理端口 | 空(不使用代理) | `7890` |
| `PROXY_USER` | HTTP代理用户名 | 空(不使用认证) | `username` |
| `PROXY_PASS` | HTTP代理密码 | 空(不使用认证) | `password` |
| `IMAGE_SAVE_DIR` | 图片保存目录 | `src/static/images` | `/data/images` |
| `IMAGE_LOCALIZATION` | 是否启用图片本地化 | `False` | `True` |
| `ADMIN_KEY` | 管理员API密钥(登录界面输入的密码) | `sk-123456` | `sk-youradminkey` |
| `API_AUTH_TOKEN` | API认证令牌(使用api服务传入的key) | 空 | `your-auth-token` |
| `VERBOSE_LOGGING` | 是否启用详细日志 | `False` | `True` |
## API密钥配置说明
API密钥配置采用JSON格式,每个密钥包含以下属性:
- `key`: Sora认证令牌(必须包含Bearer前缀)
- `weight`: 轮询权重,数字越大被选中概率越高
- `max_rpm`: 每分钟最大请求数(速率限制)
示例:
```json
[
{
"key": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MzQ0ZTY1LWJiYzktNDRkMS1hOWQwLWY5NTdiMDc5YmQwZSIsInR5cCI6IkpXVCJ9...",
"weight": 1,
"max_rpm": 60
},
{
"key": "Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjE5MzQ0ZTY1LWJiYzktNDRkMS1hOWQwLWY5NTdiMDc5YmQwZSIsInR5cCI6IkpXVCJ9...",
"weight": 2,
"max_rpm": 60
}
]
```
## 使用示例
### 使用curl发送请求
```bash
# 文本到图像请求(非流式)
curl -X POST http://localhost:8890/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "sora-1.0",
"messages": [
{"role": "user", "content": "生成一只在草地上奔跑的金毛犬"}
],
"n": 1,
"stream": false
}'
# 文本到图像请求(流式)
curl -X POST http://localhost:8890/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer your-api-key" \
-d '{
"model": "sora-1.0",
"messages": [
{"role": "user", "content": "生成一只在草地上奔跑的金毛犬"}
],
"n": 1,
"stream": true
}'
# 查询异步任务状态
curl -X GET http://localhost:8890/v1/generation/chatcmpl-123456789abcdef \
-H "Authorization: Bearer your-api-key"
```
## 常见问题排查
1. **连接超时或无法连接**
- 检查代理配置是否正确
- 如使用代理认证,确认用户名密码正确
- 确认Sora服务器是否可用
- 检查本地网络连接
2. **API密钥加载失败**
- 确认api_keys.json格式正确
- 检查环境变量API_KEYS是否正确设置
- 查看日志中的错误信息
3. **图片生成失败**
- 确认Sora令牌有效性
- 查看日志中的错误信息
- 检查是否超出账户额度限制
4. **Docker容器启动失败**
- 检查端口是否被占用
- 确认环境变量设置正确
- 查看Docker日志中的错误信息
5. **环境变量API_AUTH_TOKEN和ADMIN_KEY的区别**
- 环境变量 API_AUTH_TOKEN代表你在cheery studio 或者newapi里调用填写的令牌
- 环境变量 ADMIN_KEY 代表你管理面板的登录密码
- 当不设置API_AUTH_TOKE时 其值默认等于ADMIN_KEY的值
6. **环境变量BASE_URL的作用**
- 这个是图片本地化时设置的,比如生成了一张图片,获取到图片原始url,由于有的客户端不能访问sora,所以要本地化,这个base_url就是指定本地化图片的url前缀。
6. **token无效**
- 首次使用的token需要设置用户名,可以使用下面的脚本批量设置用户名:
```python
import random
import string
import logging
import cloudscraper
# Configure logging
tlogging = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# --- Configuration ---
PROXY = {
"http": "http://127.0.0.1:7890",
"https": "http://127.0.0.1:7890"
}
PROFILE_API = "https://sora.chatgpt.com/backend/me"
TOKENS_FILE = "tokens.txt" # 每行一个 Bearer token
RESULTS_FILE = "update_results.txt" # 保存更新结果
USERNAME_LENGTH = 8 # 随机用户名长度
# --- Utilities ---
def random_username(length: int = USERNAME_LENGTH) -> str:
"""生成全小写随机用户名"""
return ''.join(random.choices(string.ascii_lowercase, k=length))
def sanitize_headers(headers: dict) -> dict:
"""
移除所有非 Latin-1 字符,确保 headers 可以被底层 HTTP 库正确编码。
"""
new = {}
for k, v in headers.items():
if isinstance(v, str):
new[k] = v.encode('latin-1', 'ignore').decode('latin-1')
else:
new[k] = v
return new
class SoraBatchUpdater:
def __init__(self, proxy: dict = None):
self.proxy = proxy or {}
def update_username_for_token(self, token: str) -> tuple[bool, str]:
"""
针对单个 Bearer token,生成随机用户名并发送更新请求。
返回 (success, message)。
"""
scraper = cloudscraper.create_scraper()
if self.proxy:
scraper.proxies = self.proxy
headers = {
"Accept": "*/*",
"Accept-Language": "zh-CN,zh;q=0.9",
"Content-Type": "application/json",
"Authorization": f"Bearer {token}",
"sec-ch-ua": '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
}
headers = sanitize_headers(headers)
new_username = random_username()
payload = {"username": new_username}
try:
resp = scraper.post(
PROFILE_API,
headers=headers,
json=payload,
allow_redirects=False,
timeout=15
)
status = resp.status_code
if resp.ok:
msg = f"OK ({new_username})"
logging.info("Token %s: updated to %s", token[:6], new_username)
return True, msg
else:
text = resp.text.replace('\n', '')
msg = f"Failed {status}: {text}" # 简要错误信息
logging.warning("Token %s: %s", token[:6], msg)
return False, msg
except Exception as e:
msg = str(e)
logging.error("Token %s exception: %s", token[:6], msg)
return False, msg
def batch_update(self, tokens: list[str]) -> None:
"""
对一组 Bearer token 批量更新用户名,并将结果写入 RESULTS_FILE。
"""
results = []
for token in tokens:
success, message = self.update_username_for_token(token)
results.append((token, success, message))
# 写入结果文件
with open(RESULTS_FILE, 'w', encoding='utf-8') as f:
for token, success, msg in results:
status = 'SUCCESS' if success else 'ERROR'
f.write(f"{token} ---- {status} ---- {msg}\n")
logging.info("Batch update complete. Results saved to %s", RESULTS_FILE)
def load_tokens(filepath: str) -> list[str]:
"""从文件加载每行一个 token 的列表"""
try:
with open(filepath, 'r', encoding='utf-8') as f:
return [line.strip() for line in f if line.strip()]
except FileNotFoundError:
logging.error("Tokens file not found: %s", filepath)
return []
if __name__ == '__main__':
tokens = load_tokens(TOKENS_FILE)
if not tokens:
logging.error("No tokens to update. Exiting.")
else:
updater = SoraBatchUpdater(proxy=PROXY)
updater.batch_update(tokens)
```
## 性能优化
最新版本包含以下性能优化:
1. **代码重构**:简化了代码结构,提高可读性和可维护性
2. **内存优化**:减少不必要的内存使用,优化大型图像处理
3. **异步处理**:全面使用异步处理提高并发性能
4. **错误处理**:改进了错误处理和日志记录
5. **密钥管理**:优化了密钥轮询算法,提高了可靠性
6. **容器优化**:增强了Docker容器配置,支持健康检查
## 贡献
欢迎提交问题报告和改进建议!
## 许可证
MIT