refactor: remove developer chat model config

This commit is contained in:
杨其伟
2026-03-10 20:12:19 +08:00
parent 8eb45a06e6
commit 7b22303b57
6 changed files with 97 additions and 109 deletions
+69 -64
View File
@@ -68,18 +68,6 @@ USE_SESSION_SUBDIR = True
CUSTOM_MODEL_KEY = "__custom__"
# Load keys
DEFAULT_LLM_API_KEY = os.getenv("DEEPSEEK_API_KEY")
DEFAULT_LLM_API_URL = os.getenv("DEEPSEEK_API_URL")
DEFAULT_LLM_API_NAME = os.getenv("DEEPSEEK_API_NAME", "deepseek-chat")
DEFAULT_VLM_API_KEY = os.getenv("GLM_V4_6_API_KEY")
DEFAULT_VLM_API_URL = os.getenv("GLM_V4_6_API_URL")
DEFAULT_VLM_API_NAME = os.getenv("GLM_V4_6_API_NAME", "qwen3-vl-8b-instruct")
print("DEEPSEEK_API_KEY exists:", bool(os.getenv("DEEPSEEK_API_KEY")))
print("QWEN3_VL_8B_API_KEY exists:", bool(os.getenv("QWEN3_VL_8B_API_KEY")))
print("DEEPSEEK_API_URL:", repr(os.getenv("DEEPSEEK_API_URL")))
print("QWEN3_VL_8B_API_URL:", repr(os.getenv("QWEN3_VL_8B_API_URL")))
def debug_traceback_print(cfg: Settings):
if cfg.developer.developer_mode:
traceback.print_exc()
@@ -91,65 +79,82 @@ def _norm_url(u: Any) -> str:
u = _s(u)
return u.rstrip("/") if u else ""
def _env_fallback_for_model(model_name: str) -> Tuple[str, str]:
MODEL_ENV_KEYS = {
"llm": {
"model": "OPENSTORYLINE_LLM_MODEL",
"base_url": "OPENSTORYLINE_LLM_BASE_URL",
"api_key": "OPENSTORYLINE_LLM_API_KEY",
},
"vlm": {
"model": "OPENSTORYLINE_VLM_MODEL",
"base_url": "OPENSTORYLINE_VLM_BASE_URL",
"api_key": "OPENSTORYLINE_VLM_API_KEY",
},
}
def _read_model_env(kind: str) -> Dict[str, str]:
keys = MODEL_ENV_KEYS[kind]
return {
"model": os.getenv(keys.get("model", "")) or "",
"base_url": _norm_url(os.getenv(keys.get("base_url", ""))),
"api_key": os.getenv(keys.get("api_key", "")) or "",
}
def _resolve_builtin_model_override(kind: str, cfg_block: Any) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
"""
- deepseek* -> DEEPSEEK_API_URL / DEEPSEEK_API_KEY
- qwen3* -> QWEN3_VL_8B_API_URL / QWEN3_VL_8B_API_KEY
Default model parsing rules:
1. Prioritize using `[llm]/[vlm]` from `config.toml`
2. If any of `model`, `base_url`, or `api_key` is missing, the entire set will fall back to environment variables.
3. Runtime parameters such as `timeout`, `temperature`, and `max_retries` are still read from the config block.
"""
m = _s(model_name).lower()
if "deepseek" in m:
return (_s(os.getenv("DEEPSEEK_API_URL")), _s(os.getenv("DEEPSEEK_API_KEY")))
if m.startswith("qwen3-vl-8b-instruct") or "qwen3-vl-8b-instruct" in m:
return (_s(os.getenv("QWEN3_VL_8B_API_URL")), _s(os.getenv("QWEN3_VL_8B_API_KEY")))
return ("", "")
kind = kind.strip().lower()
if kind not in ("llm", "vlm"):
return None, f"unknown model kind: {kind}"
def _resolve_default_model_override(cfg: Settings, model_name: str) -> Tuple[Optional[Dict[str, Any]], Optional[str]]:
"""
1. get config from [developer.chat_models_config."<model_name>"]
2. rollback to env
"""
model_name = _s(model_name)
if not model_name:
return None, "default model name is empty"
model = _s(getattr(cfg_block, "model", None))
base_url = _norm_url(getattr(cfg_block, "base_url", None))
api_key = _s(getattr(cfg_block, "api_key", None))
model_cfg: Dict[str, Any] = {}
try:
model_cfg = (cfg.developer.chat_models_config.get(model_name) or {}) if getattr(cfg, "developer", None) else {}
except Exception:
model_cfg = {}
config_complete = bool(model and base_url and api_key)
if not isinstance(model_cfg, dict):
model_cfg = {}
if not config_complete:
env_cfg = _read_model_env(kind)
model = env_cfg["model"]
base_url = env_cfg["base_url"]
api_key = env_cfg["api_key"]
base_url = _norm_url(model_cfg.get("base_url"))
api_key = _s(model_cfg.get("api_key"))
if not base_url or not api_key:
env_url, env_key = _env_fallback_for_model(model_name)
if not base_url:
base_url = _norm_url(env_url)
if not api_key:
api_key = _s(env_key)
override: Dict[str, Any] = {"model": model_name}
if base_url:
override["base_url"] = base_url
if api_key:
override["api_key"] = api_key
for k in ("timeout", "temperature", "max_retries", "top_p", "max_tokens"):
if k in model_cfg and model_cfg.get(k) not in (None, ""):
override[k] = model_cfg.get(k)
if not override.get("base_url") or not override.get("api_key"):
if not (model and base_url and api_key):
keys = MODEL_ENV_KEYS[kind]
return None, (
f"cannot find base_url/api_key of default model: {model_name}. "
f"please fill in base_url/api_key of [developer.chat_models_config.\"{model_name}\" in config.toml]"
f"or set environment variablesDEEPSEEK_API_URL/DEEPSEEK_API_KEY / QWEN3_VL_8B_API_URL/QWEN3_VL_8B_API_KEY)。"
f"{kind} config incomplete. "
f"Please either fill [{kind}].model/base_url/api_key in config.toml, "
f"or set env vars: "
f"{keys['model']}, {keys['base_url']}, {keys['api_key']}"
f"\nand then rerun."
)
override: Dict[str, Any] = {
"model": model,
"base_url": base_url,
"api_key": api_key,
}
for k in ("timeout", "temperature", "max_retries", "top_p", "max_tokens"):
v = getattr(cfg_block, k, None)
if v not in (None, ""):
override[k] = v
return override, None
def _peek_builtin_model_name(kind: str, cfg: Settings) -> str:
# Get the model name displayed on the front end
cfg_block = cfg.llm if kind == "llm" else cfg.vlm
resolved, _ = _resolve_builtin_model_override(kind, cfg_block)
if resolved and resolved.get("model"):
return _s(resolved["model"])
return "unknown model"
def _stable_dict_key(d: Optional[Dict[str, Any]]) -> str:
try:
return json.dumps(d or {}, sort_keys=True, ensure_ascii=False)
@@ -1024,8 +1029,8 @@ class ChatSession:
self.cfg = cfg
self.lang = "zh"
default_llm = _s(getattr(getattr(cfg, "developer", None), "default_llm", "")) or "deepseek-chat"
default_vlm = _s(getattr(getattr(cfg, "developer", None), "default_vlm", "")) or "qwen3-vl-8b-instruct"
default_llm = _peek_builtin_model_name("llm", self.cfg)
default_vlm = _peek_builtin_model_name("vlm", self.cfg)
self.chat_models = [default_llm, CUSTOM_MODEL_KEY]
self.chat_model_key = default_llm
@@ -1187,7 +1192,7 @@ class ChatSession:
raise RuntimeError("please fill in model/base_url/api_key of custom LLM")
llm_override = self.custom_llm_config
else:
llm_override, err = _resolve_default_model_override(self.cfg, self.chat_model_key)
llm_override, err = _resolve_builtin_model_override("llm", self.cfg.llm)
if err:
raise RuntimeError(err)
@@ -1197,7 +1202,7 @@ class ChatSession:
raise RuntimeError("please fill in model/base_url/api_key of custom VLM")
vlm_override = self.custom_vlm_config
else:
vlm_override, err = _resolve_default_model_override(self.cfg, self.vlm_model_key)
vlm_override, err = _resolve_builtin_model_override("vlm", self.cfg.vlm)
if err:
raise RuntimeError(err)
+3 -19
View File
@@ -1,23 +1,8 @@
# ============= 开发者选项 / Developer Options ===============
[developer]
developer_mode = false
default_llm = "deepseek-chat"
default_vlm = "qwen3-vl-8b-instruct"
developer_mode = true
print_context = false # 在拦截器打印模型拿到的全部上下文,会很长 / Print full context in interceptor (output will be very long)
# ============= 模型配置 for 体验网页 ===============
[developer.chat_models_config."deepseek-chat"]
base_url = ""
api_key = ""
temperature = 0.1
[developer.chat_models_config."qwen3-vl-8b-instruct"]
base_url = ""
api_key = ""
timeout = 20.0
temperature = 0.1
max_retries = 2
# ============= 项目路径 / Project Paths ======================
[project]
media_dir = "./outputs/media"
@@ -26,7 +11,7 @@ outputs_dir = "./outputs"
# ============= 模型配置 for user / Model Config for User =============
[llm]
model = "deepseek-chat"
model = ""
base_url = ""
api_key = ""
timeout = 30.0 # 单位:秒
@@ -34,14 +19,13 @@ temperature = 0.1
max_retries = 2
[vlm]
model = "qwen3-vl-8b-instruct"
model = ""
base_url = ""
api_key = ""
timeout = 20.0 # 单位:秒
temperature = 0.1
max_retries = 2
# ============= MCP Server 相关 / MCP Server Related =============
[local_mcp_server]
server_name = "storyline"
+12 -11
View File
@@ -21,8 +21,12 @@ Note: For users outside China, we recommend using large language models such as
- **API Key**: Fill in the Key obtained in the previous step
3. **API Configuration**
- **Web Usage**: Select "Use Custom Model" in the LLM model form, and fill in the model according to the configuration parameters
- **Local Deployment**: In config.toml, locate `[developer.chat_models_config."deepseek-chat"]` and fill in the configuration parameters to make the default configuration accessible from the Web page. Locate `[llm]` and configure model, base_url, and api_key
- **Web Usage**:
- In the LLM model dropdown, select **Custom Model**, then fill in the model settings according to your configuration parameters.
- Or, open `config.toml`, locate `[llm]`, and configure `model`, `base_url`, and `api_key`. The model you entered will then appear in the dropdown on the Web page.
- **CLI**:
- If you prefer the CLI entry point, you need to open `config.toml`, locate `[llm]`, and configure `model`, `base_url`, and `api_key`.
## 2. Multimodal Large Language Model (VLM)
@@ -42,15 +46,12 @@ Note: For users outside China, we recommend using large language models such as
- **Model Name**: `qwen3-vl-8b-instruct`
- **Base URL**: `https://dashscope.aliyuncs.com/compatible-mode/v1`
- Parameter Configuration: Select "Use Custom Model" in the VLM Model form and fill in the parameters. For local deployment, locate `[vlm]` and configure model, base_url, and api_key. Add the following fields in config.toml as the default Web API configuration:
```
[developer.chat_models_config."qwen3-vl-8b-instruct"]
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
api_key = "YOUR_API_KEY"
timeout = 20.0
temperature = 0.1
max_retries = 2
```
- Parameter Configuration:
- **Web Usage**:
- In the VLM model dropdown, select **Custom Model**, then fill in the model settings according to your configuration parameters.
- Or, open `config.toml`, locate `[vlm]`, and configure `model`, `base_url`, and `api_key`. The model you entered will then appear in the dropdown on the Web page.
- **CLI**:
- If you prefer the CLI entry point, you need to open `config.toml`, locate `[vlm]`, and configure `model`, `base_url`, and `api_key`.
### 2.3 Using Qwen3-Omni
+11 -11
View File
@@ -21,8 +21,11 @@
- **API Key**:填写上一步获取的 Key
3. **API填写**
- **Web使用**: 在LLM模型表单中选择使用自定义模型,模型按照配置参数进行填写
- **本地部署**: 在config.toml中 找到`[developer.chat_models_config."deepseek-chat"]` 将配置参数填写上去,使得Web页面可以访问到该默认配置。 找到`[llm]`并配置model、base_url、api_key
- **Web使用**:
- 在LLM模型下拉框中选择使用自定义模型,模型按照配置参数进行填写
- 或是在`config.toml`中 找到`[llm]`并配置model、base_url、api_key。Web页面下拉框会出现你填写的模型。
- **CLI**
- 如果你偏好 CLI 入口,需要在`config.toml`中找到`[llm]`并配置model、base_url、api_key。
## 二、多模态大模型 (VLM)
@@ -42,15 +45,12 @@
- **模型名称**`qwen3-vl-8b-instruct`
- **Base URL**`https://dashscope.aliyuncs.com/compatible-mode/v1`
- **参数填写**在VLM Model表单中选择"使用自定义模型"进行参数填写。本地部署时,找到`[vlm]`并配置model、base_url、api_key,在config.toml中新增以下字段作为Web的API默认配置:
```
[developer.chat_models_config."qwen3-vl-8b-instruct"]
base_url = "https://dashscope.aliyuncs.com/compatible-mode/v1"
api_key = "YOUR_API_KEY"
timeout = 20.0
temperature = 0.1
max_retries = 2
```
- **参数填写**
- **Web使用**:
- 在VLM模型下拉框中选择使用自定义模型,模型按照配置参数进行填写。
- 或是在`config.toml`中 找到`[vlm]`并配置model、base_url、api_key。Web页面下拉框会出现你填写的模型。
- **CLI**
- 如果你偏好 CLI 入口,需要在`config.toml`中找到`[vlm]`并配置model、base_url、api_key。
### 2.3 使用Qwen3-Omni
+1
View File
@@ -57,6 +57,7 @@ async def validate_api_key(base_url: str, api_key: str, model: str, provider: st
raise ValueError(f"{provider} returned non-JSON response. Check base_url/gateway.")
choices = data.get("choices")
if isinstance(choices, list) and len(choices) > 0:
print(f"{model} validation successful")
return True
raise ValueError(
f"{provider} returned a non-OpenAI-compatible response for chat.completions. "
+1 -4
View File
@@ -1,4 +1,4 @@
# open_storyline/configuration_utils.py
# /src/open_storyline/config.py
from __future__ import annotations
import os
from pathlib import Path
@@ -76,9 +76,6 @@ class ConfigBaseModel(BaseModel):
class DeveloperConfig(ConfigBaseModel):
developer_mode: bool = False
default_llm: str = "deepseek-chat"
default_vlm: str = "qwen3-vl-8b-instruct"
chat_models_config: dict[str, dict[str, Any]] = Field(default_factory=dict)
print_context: bool = False
class ProjectConfig(ConfigBaseModel):