Files
dsfree2api/config.example.toml
T
nyoungo 86492460b0 feat: 工具调用解析、Responses 状态缓存、幂等重放与模型别名
- 重写工具调用解析:XML、DSML 未加引号键、fence 内跳过、参数外溢等形状,并按请求携带的 tools 过滤工具名
- 新增 GET /v1/responses/{id} 与 previous_response_id 有界状态缓存(进程内、TTL、store=false 不写入)
- /v1/chat/completions 支持 Idempotency-Key 幂等回放(1 MiB 上限、24h TTL、并发去重)
- 模型别名与 default_model 降级;错误体统一输出 param/code
- 补 e2e 修复场景与 docs/compat-audit.md 兼容性审计
2026-10-09 00:50:01 +08:00

217 lines
8.7 KiB
TOML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dsfree2api 示例配置。
# 复制为 config.toml 后按需修改;运行 `dsfree2api -check -config config.toml` 可校验。
# 环境变量优先级高于本文件:HOST PORT LOG_LEVEL LOG_FILE API_KEYS PROXY_URL PROXY_FALLBACK_URLS
# PROXY_SLOW_START_SECONDS TURNSTILE_ENABLED TURNSTILE_API_KEY TURNSTILE_PROVIDER
# TURNSTILE_BROWSER_PATH TURNSTILE_TIMEOUT_SECONDS TURNSTILE_WARM_ENABLED
# TURNSTILE_WARM_RATIO TURNSTILE_WARM_CHECK_SECONDS
# ADMIN_ENABLED ADMIN_HOST ADMIN_PORT ADMIN_PASSWORD DATA_DIR
[server]
host = "0.0.0.0"
port = 8000
log_level = "INFO"
# 日志文件:留空写入 <data_dir>/logs/dsfree2api.log;填路径则写到该文件;
# "-" = 仅终端。日志按天轮转,旧文件压缩为 <名称>-YYYY-MM-DD.log.gz。
log_file = ""
[security]
# 下游鉴权。空数组 = 不鉴权;建议部署时换成自己的 key。
api_keys = ["sk-dsfr-local-change-me"]
[admin]
enabled = true
host = "127.0.0.1"
port = 8001
# 留空时启动会生成一次性随机密码并打印到日志。
password = "admin123"
[limits]
# 同一站点的并发上游请求数;rate_per_minute = 0 表示不限流。
max_concurrent_per_site = 4
rate_per_minute = 0
[proxy]
# 主线路,支持 http:// / https:// / socks5://,留空则直连。
url = ""
# 主线路失败或 slow-start 超时后依次尝试的备用线路。
fallback_urls = []
# 主线路允许的首事件等待秒数(>0 且存在备用线路时生效)。
slow_start_seconds = 8
[proxypool]
# 代理池:为每个站点绑定独立出口(Xray 分享链接 / 订阅 / ip:port 端点)。
# 选路为粘性:优先使用绑定列表中第一个健康节点,失败才切换;未绑定或
# 全部不健康时回退到上面的 [proxy] 主/备线路。Turnstile 求解与请求共用
# 同一出口(Cookie 与出口 IP 绑定,换出口后会自动重新求解)。
enabled = false
check_interval_seconds = 120 # 健康检查间隔
check_timeout_seconds = 10 # 单次探测超时
check_url = "https://www.gstatic.com/generate_204"
default_scheme = "http" # 裸 ip:port 的默认协议(http / socks5)
# Xray 核心:留空 xray_path 且开启自动下载时,首次使用会下载到数据目录
# (data/xray.exe;核心配置写到 data/config.json)。分享链接(vless / vmess
# / trojan / ss)由它承载;纯 ip:port / socks5:// 端点无需核心。
xray_path = ""
xray_version = "" # 留空 = 最新版
xray_auto_download = true
# 手动节点(名称限字母 / 数字 / - / _):
# [proxypool.entries.node-a]
# link = "vless://uuid@host:443?type=ws&security=tls&sni=example.com&path=/ws#node-a"
# link = "socks5://user:pass@1.2.3.4:1080"
# link = "1.2.3.4:8080" # 使用 default_scheme
# link = "1.2.3.4:8080:user:pass"
# enabled = true # 缺省即启用
# 订阅(按 interval_minutes 自动刷新;节点名形如 <订阅名>#xxxxxxxx):
# [proxypool.subscriptions.home]
# url = "https://example.com/api/v1/client/subscribe?token=…"
# interval_minutes = 60
# enabled = true
[upstream]
timeout = 60.0
stream_timeout = 300.0
config_ttl_seconds = 60
auto_refresh = true
refresh_retries = 4
retry_backoff_seconds = 1.0
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36"
# 本站点配额耗尽时自动切换到其他站点的同款模型。
cross_site_failover = true
# 站点对单次回复有约 1k token 的硬上限,大参数的工具调用 JSON 会被截断。
# 自动向上游追加「续写」轮次把 JSON 拼完整;若最终仍不闭合,会在截断处
# 强行补全闭合,返回内容写到哪算哪的部分工具调用,由客户端下一轮继续写。
# 0 = 关闭续写(每次截断都直接强行补全返回)。
continue_rounds = 20
[turnstile]
# 默认关闭,且不内置任何求解服务。Token 获取方式三选一(provider):
# api —— 调用你自己的求解服务 API(需 api_url / api_key,方式 1)
# browser —— 用本机浏览器自动求解(需 browser_path,方式 2)
# manual —— 只用控制台手动导入的 Cookie(方式 3)
enabled = false
provider = "api"
# 方式 1:求解服务 API(api_style = "sync" 为默认 CapSolver 形状;
# api_style = "ezsolver" 适配本地 EzSolver 服务,api_key 可留空)
# api_url = "https://your-solver.example.com/turnstile/sync"
# api_key = "REPLACE_ME"
# 方式 2:本地浏览器自动获取(CDP 直连,无需 Playwright / Docker)
# browser_path = "C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe"
# browser_headless = false # 默认有头窗口,过验证率更高
# browser_user_data_dir = "" # 可选:指向已有 profile(需先关掉正在运行的浏览器)
# browser_timezone = "" # 可选:时区,与代理出口对齐,如 "Asia/Tokyo"
# browser_locale = "" # 可选:如 "ja-JP",留空取站点语言
sitekey = "0x4AAAAAADlLZ3ljqZP6cQwq"
action = "chat"
# 求解超时:单次求解(浏览器 / 求解服务 API)的最长等待秒数,5–600。
timeout_seconds = 90
cookie_ttl_seconds = 10800
retries = 5
retry_backoff_seconds = 1.5
# ── Cookie 池(后台预热)─────────────────────────────────
# 开启后,后台按 warm_check_seconds 巡检:Cookie 缺失 / 过期 /
# 剩余 TTL 低于 warm_ratio 时主动重解并写回缓存,请求路径不再等待求解。
# provider = "manual" 时不预热(手动导入的 Cookie 无法自动续期)。
warm_enabled = false
warm_ratio = 0.3 # 剩余 TTL 低于该比例(0.05–0.95)时触发预热
warm_check_seconds = 60 # 后台巡检间隔秒数(5–3600)
# ── 配额哨兵(站点免费额度实时监控)──────────────
# 用与聊天相同的出口 Cookie 定期查询站点余额接口(每日免费额度),结果
# 写入日志与后台 /api/overview 的 quota 区域;剩余低于 warn_ratio 时告警。
[quota]
enabled = false
check_seconds = 300
warn_ratio = 0.2
[runtime]
data_dir = "./data"
# ── Responses API 状态缓存(previous_response_id / GET /v1/responses/{id})──
# 进程内、有界、带 TTL;store=false 的请求不写入。重启即失效。
[responses]
store_capacity = 256 # 最多保存的响应条数(1–100000)
store_ttl_seconds = 3600 # 条目存活秒数(1–604800)
[sites.de]
base_url = "https://deepseek.de"
ajax_url = "https://deepseek.de/wp-admin/admin-ajax.php"
sitekey = "0x4AAAAAADlLZ3ljqZP6cQwq"
language = "de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7"
# 站点级出口绑定(代理池条目名或 "sub:订阅名",按顺序粘性选路);
# 留空 = 使用 [proxy] 主/备线路。三个站点可各自独立配置。
# proxies = ["node-a", "sub:home"]
# 只预热代理池线路(不预热 [proxy] 主/备;请求时仍可回落):
# warm_pool_only = true
[sites.es]
base_url = "https://deepseek.es"
ajax_url = "https://deepseek.es/wp-admin/admin-ajax.php"
sitekey = "0x4AAAAAADlLZ3ljqZP6cQwq"
language = "es-ES,es;q=0.9,en-US;q=0.8,en;q=0.7"
[sites.fr]
base_url = "https://deepseek.fr"
ajax_url = "https://deepseek.fr/wp-admin/admin-ajax.php"
sitekey = "0x4AAAAAADlLZ3ljqZP6cQwq"
language = "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7"
[models.deepseek-v4-flash-de]
site = "de"
upstream_id = "deepseek-v4-flash"
label = "DeepSeek V4-Flash DE"
page_path = "/"
bot_id = 27487
post_id = 106
[models.deepseek-v4-pro-de]
site = "de"
upstream_id = "deepseek-v4-pro"
label = "DeepSeek V4-Pro DE"
page_path = "/pro/"
bot_id = 27533
post_id = 27177
[models.deepseek-v4-flash-es]
site = "es"
upstream_id = "deepseek-v4-flash"
label = "DeepSeek V4-Flash ES"
page_path = "/"
bot_id = 27623
post_id = 27568
[models.deepseek-v4-pro-es]
site = "es"
upstream_id = "deepseek-v4-pro"
label = "DeepSeek V4-Pro ES"
page_path = "/pro/"
bot_id = 27637
post_id = 27615
[models.deepseek-v4-flash-fr]
site = "fr"
upstream_id = "deepseek-v4-flash"
label = "DeepSeek V4-Flash FR"
page_path = "/"
bot_id = 27645
post_id = 27569
[models.deepseek-v4-pro-fr]
site = "fr"
upstream_id = "deepseek-v4-pro"
label = "DeepSeek V4-Pro FR"
page_path = "/pro/"
bot_id = 27647
post_id = 27616
# 模型别名:把客户端发来的任意模型名(Codex CLI / Claude Code 会发 gpt-* 、claude-* 等)
# 映射到上面 [models.*] 里已启用的 id。键不区分大小写。
[model_aliases]
"gpt-5" = "deepseek-v4-flash-de"
"claude-sonnet-4-5" = "deepseek-v4-pro-de"
# 默认模型:model 和 model_aliases 都没命中时降级到此模型(留空 = 未知模型仍 404)。
# 注意:别名解析是尽力而为的兼容手段,显式配置别名优于依赖降级。
# default_model = "deepseek-v4-flash-de"