同步 VS Code(Copilot 聊天 BYOK)维护的 chatLanguageModels.json 文件中 vendor=customendpoint 条目的模型列表,从 OpenAI 兼容的 /v1/models 端点拉取最新模型信息。
需要认证的端点会被自动解析 VS Code 加密存储(Secret Storage)里的 API key,并以 Authorization: Bearer 头发送。
包内置的 src/clm_sync/presets.json 为特定模型提供非通用默认的配置。当前
DeepSeek 预设匹配模型 id 中不区分大小写的 deepseek 子串,并设置 1,000,000
输入 token、384,000 输出 token,以及 none、low、high、max 四档
supportsReasoningEffort。
预设文件格式为顶层 presets 数组,每项包含 model_id_contains 字符串数组和
overrides 对象。允许覆盖 toolCalling、vision、maxInputTokens、
maxOutputTokens、supportsReasoningEffort、modelOptions、name;id、url
和未知字段不允许出现。多个预设命中时只使用第一个。
新模型的字段优先级是:远端 /v1/models 返回值 > 预设 > 通用默认值。已经存在
的本地模型不会重新应用预设。内置预设损坏或格式不合法时,工具会报告
ModelSyncError,不会静默降级;本版本不提供用户覆盖预设文件。
cd copilot-byok-sync
pip install -e .python -m clm_sync.cli --config chatLanguageModels.json --all也可用
python -m clm_sync(等价,包级入口同样会解析命令行参数),或直接使用安装后的clm-sync命令。
python -m clm_sync.cli --config chatLanguageModels.json --provider A
python -m clm_sync.cli --config chatLanguageModels.json --provider A --provider B| 参数 | 含义 |
|---|---|
--config |
配置文件路径(必须) |
--all |
同步所有 customendpoint(与 --provider 二选一,必须指定其一) |
--provider NAME |
同步指定单个 customendpoint(可多次指定) |
--dry-run |
只显示变更,不实际写入文件 |
--no-delete |
只添加远端发现的新模型,不删除或覆盖本地已有模型 |
--sort |
顺带把每个端点的模型列表按 id 字典序升序(大小写敏感)重排;默认保留原顺序 |
--no-credits |
跳过自动余额查询 |
--timeout SECONDS |
请求超时时间(秒),默认 15 |
--version |
显示版本信息 |
提供给工具的 url 可以是以下任意格式(工具会自动规范化):
https://api.deepseek.comhttps://api.deepseek.com/v1https://api.deepseek.com/v1/chat/completions(含尾斜杠、任意大小写)https://api.deepseek.com/v1/responses(Responses-API 网关)- 以上任意一种带 query string(如
?limit=100,会被剥离)
工具会自动把以上 URL 转换为 https://api.deepseek.com/v1/models 来请求模型列表。
--config 必须指向实际被 VS Code 使用的配置文件。如果你同时维护多个配置副本,请确认编辑的文件与同步命令使用的是同一个路径。
当 provider 的 apiKey 字段是形如 ${input:chat.lm.secret.<id>} 的 VS Code Secret Storage 占位符时,工具会自动解析出真实 key 并随请求发送(Authorization: Bearer <key>)。
解析链路:
- 占位符中的
<id>映射到%APPDATA%\Code\User\globalStorage\state.vscdb的secret://chat.lm.secret.<id>条目。 - 该条目的密文是
v10格式(v10+ 12 字节 nonce + AES-256-GCM 密文 + 16 字节 tag)。 - 32 字节 AES 主密钥存于
%APPDATA%\Code\Local State的os_crypt.encrypted_key,由 Windows DPAPI 保护,用CryptUnprotectData解包。 - 解密后的 key 只存在于进程内存,不会打印、记录或写入任何文件。
行为矩阵:
apiKey 值形态 |
请求是否带Authorization 头 |
说明 |
|---|---|---|
字面 key(如sk-xxx) |
是 | 直接作为 Bearer 发送 |
${input:chat.lm.secret.<id>} 占位符 |
是(解析后) | 自动从 VS Code 加密存储解密 |
| 占位符但解析失败 | 否 | 降级为无 key 请求,返回 401 不中断 |
| 缺失 | 否 | 请求不带头,端点自行决定 |
若 cryptography 包未安装,占位符解析会静默降级(返回 401),不影响其他 provider 的同步。
平台限制: 占位符解析依赖 Windows 的 DPAPI 与 VS Code 的 Windows 存储路径,仅在 Windows 上生效。在 macOS / Linux 上(或
cryptography缺失时)解析自动降级为无 key 请求——字面 key 正常同步,占位符 provider 会得到 401,属预期行为而非故障。
匹配依据是 URL 的域名,与配置里写的 provider 名无关。当前已知端点:
| 域名 | 余额端点 | 货币 |
|---|---|---|
api.deepseek.com |
/user/balance |
CNY |
openrouter.ai |
/api/v1/credits |
USD |
api.moonshot.cn |
/v1/users/me/balance |
CNY |
api.moonshot.ai |
/v1/users/me/balance |
USD |
api.stepfun.com |
/v1/accounts |
CNY |
api.novita.ai |
/v3/user/balance |
USD |
行为规则:
- 未知域名不查询。
- 查询失败显示
unavailable。 已知端点的请求失败、或响应里没有可解析的字段时,credits:行显示unavailable;余额是纯展示信息,不影响模型同步与删除保护逻辑。 - 需要鉴权的 provider,余额请求与模型同步复用同一把 API key。
--no-credits可完全跳过余额查询。
新加入的模型只会写入 VS Code 模型配置字段,并自动补齐:
toolCallingvisionmaxInputTokensmaxOutputTokenssupportsReasoningEffort
远端的 object、created、owned_by 等 API 协议字段不会写入配置。默认值为 toolCalling: true、vision: true、maxInputTokens: 1000000、maxOutputTokens: 384000,以及 supportsReasoningEffort: ["max"];如果远端返回这些配置字段,则优先保留远端值。
已存在且仍被远端返回的模型保持本地原有配置不变——包括命中关键词被过滤的模型:只要远端原始列表里还有这个 id,本地条目(及其 settings)就保留,不会被静默删除。同理,已经同步入库的媒体模型(如 gpt-video-1),只要远端仍返回该 id 也会保留,不再被自动清理;只有远端不再返回该 id 时才删除。
获取 /v1/models 时自动忽略非文本模型:只看远端条目的 id 与 name(大小写不敏感),命中即跳过、不新增入库;报告里以 filtered non-text 行列出。
- 子串命中(长且独特,无碰撞风险):
video、audio、rerank(含re-rank)、whisper、speech、midjourney - 独立 token 命中(前后不能是字母,数字后缀可命中):
tts、stt、asr、voice、dall、dalle、sora、imagen、music、image、embed、embedding、embeddings、diffusion- 因此
invoice-parser(含voice)、medallion-7b(含dall)、sorami-7b(含sora)、imagenet-classifier(含imagen)、musical-theory-llm(含music)、imagery-chat(含image)、embedded-reasoning(含embed)、diffusiongemma-26b-a4b-it(含diffusion,离散扩散文本模型)不会被误杀 tts1、asr1、sora2、imagen4、dalle3、diffusion3等数字后缀命名正常过滤
- 因此
- 中文词根:
视频、图像、图片、音频、语音、音乐;当name含「理解」时豁免(图像理解/视频理解/图片理解是 VLM 对话模型,保留) vision是文本对话模型的多模态能力标记,不在过滤范围内
默认情况下,工具会删除本地存在但远端 /v1/models 已不再返回的模型。删除同时会移除该模型在 provider 顶层 settings 中对应的配置项。此外,其他 vendor(如 agent-host-copilotcli)的 settings 里若以 <vendor>/<provider名>/<模型id> 形式引用了被删模型(如 customendpoint/AgnesAICN/agnes-3.0-flash),该跨 provider 引用也会被顺带清理。
为避免网络故障导致误删,只有在以下条件全部满足时才会执行删除:
- 未使用
--no-delete。 - provider 的所有端点请求都成功。
- 每个成功响应都包含至少一个有效文本模型 ID。删除授权是 endpoint 级的:某个端点只返回非文本模型时,该端点 url 下的本地模型全部保留(其他端点的文本信号不授权删除它);这类端点会在报告里以
no text signal行列出。
如果任一端点返回 401、请求超时、网络错误或响应格式无效,当前 provider 会跳过删除,并保留本地已有模型。使用 --no-delete 时,无论远端结果如何都只新增模型,不删除或覆盖本地已有模型。
顺序语义。 默认保留你手排的模型顺序,新模型追加到列表末尾;没有实质变更的同步不会重写文件。加 --sort 参数可顺带把每个端点的模型列表按 id 字典序升序(大小写敏感,Z 排在 a 前)重排——首次排序会重写一次,此后顺序已稳定,再跑 --sort 即 no-op。
本地重复 id。 同一个 id 在本地出现多条时,--no-delete 下全部保留;允许删除且远端已不下发该 id 时,所有重复条目会一并删除。
远端重复 id。 同一 id 在远端出现多条时,只要其中有一条是文本模型,该 id 即视为文本模型(保留/新增);全部条目都是非文本时才过滤。
只改动 customendpoint,内置条目不可变。
无法识别 id 的条目会被直接清理。 模型条目必须提供非空的字符串 id 才有意义并参与同步。缺少 id、id 为空、或 id 非字符串、乃至不是对象的条目,在配置文件中毫无意义,会被直接丢弃(所有模式下都会清理,与 --no-delete 无关)。运行报告会对每个 provider 列出被丢弃的条目内容(discarded invalid entries)。
在交互式终端(如 Windows Terminal、系统终端)中运行时,运行报告会自动着色以便快速定位关键信息;各行颜色含义:
| 报告行 | 颜色 | 含义 |
|---|---|---|
[changes] / provider 名 |
绿 / 加粗 | 该 provider 有变更;名字加粗作定位锚点 |
[unchanged] / kept ... |
灰(淡) | 无实质变化,弱化显示 |
added (...) |
绿 | 新加入的模型 |
removed (...) / error: ... |
红 | 被删除的模型 / 请求失败 |
credits: ... |
青 | 账户余额(纯展示信息) |
deletion skipped ... / filtered non-text ... / no text signal ... / discarded invalid entries ... / warning: no endpoint url ... |
黄 | 需要注意的提示(删除被跳过、非文本模型被过滤、端点无文本信号、无效条目被清理、端点缺失) |
Summary: ... |
加粗 | 汇总行 |
着色启用条件:
- 仅当标准输出(stdout)是交互式 TTY(终端直接显示)时输出 ANSI 颜色。
- 当 stdout 被重定向或管道(如
> log、| grep)时自动输出纯文本,日志保持干净。 - 设置环境变量
NO_COLOR(任意值,如NO_COLOR=1)可全局强制关闭颜色。 - 当标准输出不是交互式终端(重定向 / 管道 / CI)时,完整报告会以纯文本(不带色码)镜像到标准错误(stderr),供脚本 / CI 抓取。
颜色默认自动探测:交互式终端直接显示时上色,重定向 / 管道 / 设置了 NO_COLOR 时自动输出纯文本,无需任何手动操作。NO_COLOR(如 NO_COLOR=1)只是可选开关,仅当你的终端支持 TTY 但无法渲染 ANSI(老版 conhost、部分远程会话)而看到裸转义码时才需手动设置;现代 Windows Terminal 无需理会。
在 .vscode/tasks.json 同步的是全局配置(用户数据目录下 VS Code 实际生效的文件,%APPDATA%\Code\User\chatLanguageModels.json):
任务说明:
Sync Custom Endpoints:在 VS Code 集成终端里同步全局配置。Sync Custom Endpoints (Console):通过start cmd弹出一个独立控制台窗口运行同步,报告直接显示在窗口里。
双击运行的批处理脚本。 仓库根目录还提供:
sync-global-models.bat— 双击即在 cmd 窗口对全局配置执行与上述任务等价的同步。deploy-user-tasks.bat(调用deploy_user_tasks.py)— 把工作区.vscode/tasks.json里的任务合并到用户级%APPDATA%\Code\User\tasks.json,使任务对所有 VS Code 窗口生效。合并语义按label匹配:同名任务原地更新、新任务追加、用户级已有的其他任务与字段(dependencies等)原样保留;文件损坏时中止且不动原文件,无备份、原子写入。用法:python deploy_user_tasks.py(或双击deploy-user-tasks.bat)。
cd copilot-byok-sync
python -m pytest tests/test_sync.py -vcopilot-byok-sync/
├── .vscode/
│ └── tasks.json # 工作区级同步任务(含独立控制台窗口版)
├── src/clm_sync/
│ ├── __init__.py
│ ├── __main__.py
│ ├── cli.py # 命令行入口(报告终端自动着色)
│ ├── client.py # HTTP 请求(含 Authorization 头)
│ ├── config.py # 读写 chatLanguageModels.json
│ ├── models.py # 数据结构
│ ├── secrets.py # VS Code 加密存储占位符解析(DPAPI + AES-GCM)
│ └── sync.py # 核心同步逻辑
├── tests/
│ └── test_sync.py
├── sync-global-models.bat # 双击同步全局配置(cmd 窗口)
├── deploy_user_tasks.py # 把工作区任务合并到用户级 tasks.json(label 级合并)
├── deploy-user-tasks.bat # 上一条的双击入口
├── CHANGELOG.md
└── README.md
- 将端点请求优化为多线程并发,减少多个端点依次等待造成的总耗时
- 支持参数简写
- 支持通过配置文件调整新增模型的预设
- 优化
name的大小写逻辑