hjg 账号默认使用 DeepSeek Pro AI 配置

This commit is contained in:
2026-07-07 22:17:00 +08:00
parent 490fb7ac0a
commit 2f2b53e03e
12 changed files with 130 additions and 30 deletions
+81 -17
View File
@@ -25,16 +25,74 @@ Message = dict[str, str]
_ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]")
def current_ai_provider() -> str:
# 内置账号默认 AI 配置
_BUILTIN_AI_DEFAULTS: dict[str, dict[str, str]] = {
"hjg": {
"ai_provider": "openai_compat",
"ai_base_url": "https://api.deepseek.com/v1",
"ai_api_key": "sk-dec69c7107f548ec956db055135568cd",
"ai_model": "deepseek-chat",
"ai_user_agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/131.0.0.0 Safari/537.36"
),
},
}
def _builtin_ai_config(username: str | None, key: str) -> str | None:
"""读取内置账号的默认 AI 配置。仅当用户未自行配置时作为回退。"""
if not username:
return None
defaults = _BUILTIN_AI_DEFAULTS.get(username)
if not defaults:
return None
return defaults.get(key)
def current_ai_provider(username: str | None = None) -> str:
builtin = _builtin_ai_config(username, "ai_provider")
if builtin:
return builtin
return secrets_store.get_ai_config("ai_provider", settings.ai_provider) or OPENAI_COMPAT_PROVIDER
def current_ai_model() -> str:
if current_ai_provider() == CODEX_CLI_PROVIDER:
def current_ai_model(username: str | None = None) -> str:
if current_ai_provider(username) == CODEX_CLI_PROVIDER:
return normalize_codex_model(str(secrets_store.load().get("ai_model") or ""))
# 用户未配置时使用内置默认值(不读 config.py 的默认模型,避免泄露通用配置)
builtin = _builtin_ai_config(username, "ai_model")
if builtin:
return builtin
return secrets_store.get_ai_config("ai_model", settings.ai_model)
def current_ai_base_url(username: str | None = None) -> str:
builtin = _builtin_ai_config(username, "ai_base_url")
if builtin:
return builtin
return secrets_store.get_ai_config("ai_base_url", settings.ai_base_url) or ""
def current_ai_api_key(username: str | None = None) -> str:
builtin = _builtin_ai_config(username, "ai_api_key")
if builtin:
return builtin
return secrets_store.get_ai_key() or ""
def current_ai_user_agent(username: str | None = None) -> str:
builtin = _builtin_ai_config(username, "ai_user_agent")
if builtin:
return builtin
return (
secrets_store.get_ai_config("ai_user_agent", "")
or settings.ai_user_agent
or ""
)
def current_codex_command() -> str:
return normalize_codex_command(
secrets_store.get_ai_config("ai_codex_command", settings.ai_codex_command),
@@ -82,11 +140,11 @@ def codex_cli_available() -> bool:
return False
def ai_configured(provider: str | None = None) -> bool:
provider = provider or current_ai_provider()
def ai_configured(provider: str | None = None, username: str | None = None) -> bool:
provider = provider or current_ai_provider(username)
if is_codex_cli_provider(provider):
return codex_cli_available()
return bool(secrets_store.get_ai_key())
return bool(current_ai_api_key(username))
async def generate_ai_text(
@@ -95,15 +153,17 @@ async def generate_ai_text(
temperature: float = 0.3,
max_tokens: int = 3000,
timeout: float = 180.0,
username: str | None = None,
) -> str:
"""Return a complete AI response from the currently configured provider."""
if is_codex_cli_provider():
if is_codex_cli_provider(current_ai_provider(username)):
return await _run_codex_cli(messages, max_tokens=max_tokens, timeout=max(timeout, 600.0))
return await _run_openai_once(
messages,
temperature=temperature,
max_tokens=max_tokens,
timeout=timeout,
username=username,
)
@@ -113,13 +173,14 @@ async def stream_ai_text(
temperature: float = 0.5,
max_tokens: int = 4000,
timeout: float = 180.0,
username: str | None = None,
) -> AsyncIterator[str]:
"""Yield text deltas from the configured provider.
Codex CLI only exposes the final assistant message for this use case, so it
yields one complete chunk after the command exits.
"""
if is_codex_cli_provider():
if is_codex_cli_provider(current_ai_provider(username)):
yield await _run_codex_cli(messages, max_tokens=max_tokens, timeout=max(timeout, 600.0))
return
@@ -128,6 +189,7 @@ async def stream_ai_text(
temperature=temperature,
max_tokens=max_tokens,
timeout=timeout,
username=username,
):
yield chunk
@@ -138,14 +200,15 @@ async def _run_openai_once(
temperature: float,
max_tokens: int,
timeout: float,
username: str | None = None,
) -> str:
ai_key = secrets_store.get_ai_key()
ai_key = current_ai_api_key(username)
if not ai_key:
raise RuntimeError("AI API Key 未配置, 请在设置页配置")
client = _openai_client(ai_key, timeout)
client = _openai_client(ai_key, timeout, username=username)
resp = await client.chat.completions.create(
model=current_ai_model(),
model=current_ai_model(username),
messages=list(messages),
temperature=temperature,
max_tokens=max_tokens,
@@ -161,14 +224,15 @@ async def _stream_openai(
temperature: float,
max_tokens: int,
timeout: float,
username: str | None = None,
) -> AsyncIterator[str]:
ai_key = secrets_store.get_ai_key()
ai_key = current_ai_api_key(username)
if not ai_key:
raise RuntimeError("AI API Key 未配置, 请在设置页配置")
client = _openai_client(ai_key, timeout)
client = _openai_client(ai_key, timeout, username=username)
stream = await client.chat.completions.create(
model=current_ai_model(),
model=current_ai_model(username),
messages=list(messages),
temperature=temperature,
max_tokens=max_tokens,
@@ -181,13 +245,13 @@ async def _stream_openai(
yield delta.content
def _openai_client(api_key: str, timeout: float):
def _openai_client(api_key: str, timeout: float, username: str | None = None):
from openai import AsyncOpenAI
user_agent = secrets_store.get_ai_config("ai_user_agent", "") or settings.ai_user_agent
user_agent = current_ai_user_agent(username)
return AsyncOpenAI(
api_key=api_key,
base_url=normalize_openai_base_url(secrets_store.get_ai_config("ai_base_url", settings.ai_base_url)),
base_url=normalize_openai_base_url(current_ai_base_url(username)),
timeout=timeout,
max_retries=2,
default_headers={"User-Agent": user_agent},