ds2api / internal /deepseek /protocol /constants.go
204848
feat: 添加平台配置支持,包括Android和Web版本及API级别的自定义设置
0c46526
Raw
History Blame Contribute Delete
7.39 kB
package protocol
import (
_ "embed"
"encoding/json"
"fmt"
"strings"
)
const (
DeepSeekHost = "chat.deepseek.com"
DeepSeekLoginURL = "https://chat.deepseek.com/api/v0/users/login"
DeepSeekCreateSessionURL = "https://chat.deepseek.com/api/v0/chat_session/create"
DeepSeekCreatePowURL = "https://chat.deepseek.com/api/v0/chat/create_pow_challenge"
DeepSeekCompletionURL = "https://chat.deepseek.com/api/v0/chat/completion"
DeepSeekContinueURL = "https://chat.deepseek.com/api/v0/chat/continue"
DeepSeekUploadFileURL = "https://chat.deepseek.com/api/v0/file/upload_file"
DeepSeekFetchFilesURL = "https://chat.deepseek.com/api/v0/file/fetch_files"
DeepSeekFetchSessionURL = "https://chat.deepseek.com/api/v0/chat_session/fetch_page"
DeepSeekDeleteSessionURL = "https://chat.deepseek.com/api/v0/chat_session/delete"
DeepSeekDeleteAllSessionsURL = "https://chat.deepseek.com/api/v0/chat_session/delete_all"
DeepSeekCompletionTargetPath = "/api/v0/chat/completion"
DeepSeekUploadTargetPath = "/api/v0/file/upload_file"
)
var defaultStaticBaseHeaders = map[string]string{
"Host": "chat.deepseek.com",
"Accept": "application/json",
"Content-Type": "application/json",
"accept-charset": "UTF-8",
}
var defaultSkipContainsPatterns = []string{
"quasi_status",
"elapsed_secs",
"token_usage",
"pending_fragment",
"conversation_mode",
"fragments/-1/status",
"fragments/-2/status",
"fragments/-3/status",
}
var defaultSkipExactPaths = []string{
"response/search_status",
}
var ClientVersion string
var currentPlatform = "android"
var currentAndroidAPILevel = "35" // default from normalizeClientConstants
var BaseHeaders = map[string]string{}
var SkipContainsPatterns = cloneStringSlice(defaultSkipContainsPatterns)
var SkipExactPathSet = toStringSet(defaultSkipExactPaths)
type clientConstants struct {
Name string `json:"name"`
Platform string `json:"platform"`
Version string `json:"version"`
AndroidAPILevel string `json:"android_api_level"`
Locale string `json:"locale"`
}
type sharedConstants struct {
Client clientConstants `json:"client"`
BaseHeaders map[string]string `json:"base_headers"`
SkipContainsPattern []string `json:"skip_contains_patterns"`
SkipExactPaths []string `json:"skip_exact_paths"`
}
//go:embed constants_shared.json
var sharedConstantsJSON []byte
//go:embed constants_web.json
var webConstantsJSON []byte
func init() {
cfg := sharedConstants{}
if err := json.Unmarshal(sharedConstantsJSON, &cfg); err != nil {
panic(fmt.Errorf("load DeepSeek shared constants: %w", err))
}
applySharedConstants(cfg)
}
func applySharedConstants(cfg sharedConstants) {
client := normalizeClientConstants(cfg.Client)
ClientVersion = client.Version
if client.Platform == "android" {
currentAndroidAPILevel = client.AndroidAPILevel
}
BaseHeaders = buildBaseHeaders(client, cfg.BaseHeaders)
SkipContainsPatterns = cloneStringSlice(defaultSkipContainsPatterns)
if len(cfg.SkipContainsPattern) > 0 {
SkipContainsPatterns = cloneStringSlice(cfg.SkipContainsPattern)
}
SkipExactPathSet = toStringSet(defaultSkipExactPaths)
if len(cfg.SkipExactPaths) > 0 {
SkipExactPathSet = toStringSet(cfg.SkipExactPaths)
}
}
func normalizeClientConstants(in clientConstants) clientConstants {
if in.Name == "" {
in.Name = "DeepSeek"
}
if in.Platform == "" {
in.Platform = "android"
}
if in.AndroidAPILevel == "" {
in.AndroidAPILevel = "35"
}
if in.Locale == "" {
in.Locale = "zh_CN"
}
return in
}
func buildBaseHeaders(client clientConstants, overrides map[string]string) map[string]string {
out := cloneStringMap(defaultStaticBaseHeaders)
for k, v := range overrides {
if k == "" || v == "" {
continue
}
out[k] = v
}
out["User-Agent"] = userAgentForPlatform(client)
if client.Platform != "" {
out["x-client-platform"] = client.Platform
}
if client.Version != "" {
out["x-client-version"] = client.Version
}
if client.Locale != "" {
out["x-client-locale"] = client.Locale
}
return out
}
const webUserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
func userAgentForPlatform(client clientConstants) string {
if client.Platform == "web" {
return webUserAgent
}
if client.Name != "" && client.Version != "" {
ua := client.Name + "/" + client.Version
if client.Platform == "android" && client.AndroidAPILevel != "" {
ua += " Android/" + client.AndroidAPILevel
}
return ua
}
return ""
}
// ApplyPlatform reloads constants for the given platform ("android" or "web").
func ApplyPlatform(platform string) {
p := strings.ToLower(platform)
var data []byte
switch p {
case "web":
data = webConstantsJSON
default:
data = sharedConstantsJSON
p = "android"
}
cfg := sharedConstants{}
if err := json.Unmarshal(data, &cfg); err != nil {
panic(fmt.Errorf("load DeepSeek %s constants: %w", p, err))
}
currentPlatform = p
applySharedConstants(cfg)
}
// ApplyVersionOverrides patches ClientVersion and BaseHeaders with
// user-specified version overrides from config. Empty strings mean
// "keep the JSON default".
func ApplyVersionOverrides(androidVersion, webVersion, androidAPILevel string) {
if currentPlatform == "android" {
ver := androidVersion
if ver == "" {
ver = ClientVersion
}
apiLevel := androidAPILevel
if apiLevel == "" {
apiLevel = currentAndroidAPILevel
}
ClientVersion = ver
BaseHeaders["x-client-version"] = ver
BaseHeaders["x-client-platform"] = "android"
BaseHeaders["User-Agent"] = "DeepSeek/" + ver + " Android/" + apiLevel
} else if currentPlatform == "web" {
ver := webVersion
if ver == "" {
ver = ClientVersion
}
ClientVersion = ver
BaseHeaders["x-client-version"] = ver
BaseHeaders["x-app-version"] = ver
}
}
// IsWebPlatform returns whether the current platform is web.
func IsWebPlatform() bool {
return currentPlatform == "web"
}
// UserAgent returns the appropriate User-Agent string based on the current platform.
func UserAgent() string {
if currentPlatform == "web" {
return webUserAgent
}
return "DeepSeek/" + ClientVersion
}
// WebExtraHeaders returns browser-specific headers that are not in the JSON
// config but need to be added dynamically for the web platform.
func WebExtraHeaders() map[string]string {
return map[string]string{
"sec-ch-ua": `"Chromium";v="137", "Not/A)Brand";v="24"`,
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": `"Windows"`,
"origin": "https://chat.deepseek.com",
"referer": "https://chat.deepseek.com/",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
}
}
func cloneStringMap(in map[string]string) map[string]string {
out := make(map[string]string, len(in))
for k, v := range in {
out[k] = v
}
return out
}
func cloneStringSlice(in []string) []string {
out := make([]string, len(in))
copy(out, in)
return out
}
func toStringSet(in []string) map[string]struct{} {
out := make(map[string]struct{}, len(in))
for _, v := range in {
if v == "" {
continue
}
out[v] = struct{}{}
}
return out
}
const (
KeepAliveTimeout = 5
StreamIdleTimeout = 300
MaxKeepaliveCount = 40
)