From 7b22303b577b6479f324aea7331586875fbdfe50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=A8=E5=85=B6=E4=BC=9F?= Date: Tue, 10 Mar 2026 20:12:19 +0800 Subject: [PATCH] refactor: remove developer chat model config --- agent_fastapi.py | 133 ++++++++++++++++++----------------- config.toml | 22 +----- docs/source/en/api-key.md | 23 +++--- docs/source/zh/api-key.md | 22 +++--- src/open_storyline/agent.py | 1 + src/open_storyline/config.py | 5 +- 6 files changed, 97 insertions(+), 109 deletions(-) diff --git a/agent_fastapi.py b/agent_fastapi.py index 0f90689..12b8872 100644 --- a/agent_fastapi.py +++ b/agent_fastapi.py @@ -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.""] - 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 variables(DEEPSEEK_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) diff --git a/config.toml b/config.toml index 7ed244c..3eca4c5 100644 --- a/config.toml +++ b/config.toml @@ -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" diff --git a/docs/source/en/api-key.md b/docs/source/en/api-key.md index 1ba117b..218c6d4 100644 --- a/docs/source/en/api-key.md +++ b/docs/source/en/api-key.md @@ -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 diff --git a/docs/source/zh/api-key.md b/docs/source/zh/api-key.md index adeb82b..ef7320e 100644 --- a/docs/source/zh/api-key.md +++ b/docs/source/zh/api-key.md @@ -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 diff --git a/src/open_storyline/agent.py b/src/open_storyline/agent.py index 9dc160b..98431ce 100644 --- a/src/open_storyline/agent.py +++ b/src/open_storyline/agent.py @@ -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. " diff --git a/src/open_storyline/config.py b/src/open_storyline/config.py index 7c74e81..fe0b04a 100644 --- a/src/open_storyline/config.py +++ b/src/open_storyline/config.py @@ -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):