@wuyaos/pi-model-config 0.0.0-stage → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +200 -2
- package/extensions/model-config.ts +27 -0
- package/model-config.schema.json +269 -0
- package/package.json +52 -4
- package/src/command.ts +1053 -0
- package/src/config.ts +438 -0
- package/src/context-probe.ts +90 -0
- package/src/discovery.ts +427 -0
- package/src/i18n.ts +824 -0
- package/src/model-editor.ts +326 -0
- package/src/model-test.ts +212 -0
- package/src/panel.ts +477 -0
- package/src/runtime.ts +298 -0
- package/src/selector.ts +433 -0
- package/src/thinking-defaults.ts +39 -0
- package/src/thinking-probe.ts +84 -0
- package/src/transport.ts +94 -0
- package/src/types.ts +150 -0
- package/src/ui-config.ts +44 -0
- package/src/ui.ts +179 -0
package/src/i18n.ts
ADDED
|
@@ -0,0 +1,824 @@
|
|
|
1
|
+
export type UiLanguage = "zh-CN" | "en";
|
|
2
|
+
export type TranslationParams = Record<string, string | number | boolean>;
|
|
3
|
+
|
|
4
|
+
const ZH = {
|
|
5
|
+
commandDescription: "模型与服务商配置 / Models and providers",
|
|
6
|
+
mainTitle: "模型与服务商配置",
|
|
7
|
+
mainCancel: "取消",
|
|
8
|
+
back: "返回",
|
|
9
|
+
panelBack: "[B] 返回",
|
|
10
|
+
languageTitle: "语言 / Language",
|
|
11
|
+
languageZh: "简体中文",
|
|
12
|
+
languageEn: "English",
|
|
13
|
+
languageSaved: "界面语言已保存:{language}(立即生效)",
|
|
14
|
+
tuiRequired: "/model-config 需要交互式 TUI;RPC/print/json 模式不支持自定义多选界面",
|
|
15
|
+
startupIssues: "部分服务商因配置错误未注册,可继续编辑:{issues}{more}",
|
|
16
|
+
startupMore: ";另有 {count} 项",
|
|
17
|
+
newProviderTitle: "选择内置服务商(内置不代表已认证或可用)",
|
|
18
|
+
providerTagCustom: "自定义",
|
|
19
|
+
providerTagBuiltin: "内置",
|
|
20
|
+
providerLabelCustom: "★ {display} [{status}]",
|
|
21
|
+
providerLabelConfigured: "◆ {display} [{status}]",
|
|
22
|
+
providerLabelBuiltin: "{display} [{status}]",
|
|
23
|
+
newProviderId: "新服务商 ID",
|
|
24
|
+
providerExists: "服务商 ID {id} 已存在,将进入现有配置编辑,不会按新服务商覆盖",
|
|
25
|
+
panelLanguage: "界面语言:{language}",
|
|
26
|
+
panelSearchProviderPrompt: "搜索服务商:",
|
|
27
|
+
panelSearchProviderPlaceholder: "名称或 ID",
|
|
28
|
+
panelNoConfiguredProviders: "没有已配置服务商",
|
|
29
|
+
panelNoProviderSelected: "选择服务商后查看详情",
|
|
30
|
+
overviewEnter: "[Enter] 进入服务商",
|
|
31
|
+
overviewSearch: "[/] 搜索",
|
|
32
|
+
overviewDisable: "[Ctrl+X] 禁用",
|
|
33
|
+
overviewEnable: "[Ctrl+X] 启用",
|
|
34
|
+
overviewDelete: "[Ctrl+Del] 删除服务商",
|
|
35
|
+
overviewAdd: "[Ctrl+N] 添加服务商",
|
|
36
|
+
overviewClose: "[Esc] 关闭",
|
|
37
|
+
overviewLanguage: "[F2] 界面语言",
|
|
38
|
+
detailConnectionTitle: "连接摘要",
|
|
39
|
+
detailModelsTitle: "模型概况",
|
|
40
|
+
detailFetchHint: "进入“配置模型”后按 Ctrl+R 获取远程模型列表",
|
|
41
|
+
detailEditConnection: "[Ctrl+E] 编辑连接",
|
|
42
|
+
detailConfigureModels: "[Ctrl+M] 配置模型",
|
|
43
|
+
detailTakeover: "[Ctrl+T] 接管已有模型",
|
|
44
|
+
detailDisable: "[Ctrl+X] 禁用服务商",
|
|
45
|
+
detailEnable: "[Ctrl+X] 启用服务商",
|
|
46
|
+
detailTestModels: "[Ctrl+P] 批量测试模型",
|
|
47
|
+
detailBack: "[Esc] 返回总览",
|
|
48
|
+
connectionModify: "[Enter] 修改字段",
|
|
49
|
+
connectionPreview: "[Ctrl+S] 预览更改",
|
|
50
|
+
connectionBack: "[Esc] 返回服务商",
|
|
51
|
+
editorTitle: "编辑模型参数:{model}",
|
|
52
|
+
editorName: "名称",
|
|
53
|
+
editorContext: "Context Window",
|
|
54
|
+
editorMaxTokens: "Max Tokens",
|
|
55
|
+
editorReasoning: "Reasoning",
|
|
56
|
+
editorThinkingMap: "思考档位映射",
|
|
57
|
+
editorInput: "输入类型",
|
|
58
|
+
editorOutput: "输出类型",
|
|
59
|
+
editorCost: "费用",
|
|
60
|
+
editorReview: "审阅并应用",
|
|
61
|
+
editorCancel: "取消",
|
|
62
|
+
editorModify: "[Enter] 修改字段",
|
|
63
|
+
editorReturn: "[Esc] 返回",
|
|
64
|
+
editorInlineHint: "输入值后 Enter 确认",
|
|
65
|
+
editorMapHint: "思考映射:留空恢复默认;输入 - 或 null 禁用该档位",
|
|
66
|
+
editorMapDefault: "协议/目录默认",
|
|
67
|
+
editorMapDisabled: "已禁用",
|
|
68
|
+
editorDone: "完成",
|
|
69
|
+
editorModalities: "选择模态",
|
|
70
|
+
valueYes: "是",
|
|
71
|
+
valueNo: "否",
|
|
72
|
+
editorKeep: "保持",
|
|
73
|
+
editorEnable: "启用并设置映射",
|
|
74
|
+
editorDisable: "禁用",
|
|
75
|
+
editorPositiveError: "请输入正整数,支持K/M换算",
|
|
76
|
+
editorMaxError: "maxTokens不能超过contextWindow",
|
|
77
|
+
editorCostError: "费用必须是非负十进制数",
|
|
78
|
+
editorModalityEmpty: "至少选择一种模态",
|
|
79
|
+
editorModalityRequired: "当前字段必须包含 {modality}",
|
|
80
|
+
editorThinkingSummary: "思考默认档位:{level}(Pi 基线,会话/设置可覆盖)· 映射来源:{source}",
|
|
81
|
+
thinkingSourceExisting: "现有配置",
|
|
82
|
+
thinkingSourceRemote: "远程目录",
|
|
83
|
+
thinkingSourceCatalog: "运行时目录",
|
|
84
|
+
thinkingSourceProtocol: "协议默认",
|
|
85
|
+
thinkingSourceUser: "用户编辑",
|
|
86
|
+
thinkingSourceProbed: "实测",
|
|
87
|
+
thinkingSourceDefault: "Provider 默认/未显式配置",
|
|
88
|
+
editorConfirmTitle: "确认应用模型参数到草稿?",
|
|
89
|
+
editorApply: "[Ctrl+S] 应用到草稿",
|
|
90
|
+
thinkingProbeBlocked: "仅已保存/reload、enabled、clean、持久认证的managed chat模型可探测思考档位",
|
|
91
|
+
thinkingProbeTitle: "探测思考档位",
|
|
92
|
+
thinkingProbeWarning: "将按档位串行发送真实最小请求并可能计费;服务端接受不代表模型具备更强推理能力。",
|
|
93
|
+
modelSearch: "[/] 搜索",
|
|
94
|
+
modelSelectFiltered: "[Ctrl+A] 勾选搜索结果",
|
|
95
|
+
modelDeselectFiltered: "[Ctrl+D] 取消勾选搜索结果",
|
|
96
|
+
modelCycleType: "[Ctrl+T] 切换模型类型",
|
|
97
|
+
modelEditParameters: "[Ctrl+E] 编辑模型参数",
|
|
98
|
+
editorExternalTakeover: "此模型来自 models.json;请先在连接页执行“接管已有模型”后再编辑",
|
|
99
|
+
editorManagedOnly: "仅当前草稿中已勾选、由本插件管理的模型可编辑参数",
|
|
100
|
+
modelProbeContext: "[Ctrl+K] 探测上下文容量",
|
|
101
|
+
modelProbeThinking: "[Ctrl+L] 探测思考档位",
|
|
102
|
+
modelFetch: "[Ctrl+R] 获取远程模型列表",
|
|
103
|
+
modelPreview: "[Ctrl+S] 预览更改",
|
|
104
|
+
modelBack: "[Esc] 返回服务商",
|
|
105
|
+
addProviderTitle: "添加服务商",
|
|
106
|
+
addBuiltinChoice: "选择内置服务商",
|
|
107
|
+
addCustomChoice: "添加自定义服务商",
|
|
108
|
+
discoveryRequired: "尚未配置模型列表地址(Discovery URL)",
|
|
109
|
+
discoveryDerivedTitle: "可从模型调用地址推导候选(不代表服务商官方 Discovery endpoint)",
|
|
110
|
+
discoveryUseDerived: "使用推导地址(加入当前草稿,最终保存后才落盘):{url}",
|
|
111
|
+
discoveryUseCandidates: "使用首选地址:{url}(同源备选 {count} 个;成功地址加入草稿,最终保存后才落盘)",
|
|
112
|
+
discoveryRequiredEdit: "进入编辑连接设置",
|
|
113
|
+
discoveryRequiredCancel: "取消获取",
|
|
114
|
+
dirtyLeaveTitle: "放弃尚未保存的更改?",
|
|
115
|
+
dirtyLeaveBody: "返回总览会放弃当前服务商的连接草稿、模型选择和接管状态;不会写入配置。",
|
|
116
|
+
confirmCancel: "[Esc] 取消/返回",
|
|
117
|
+
confirmSave: "[Ctrl+S] 确认保存",
|
|
118
|
+
confirmEmpty: "[Ctrl+S] 确认清空",
|
|
119
|
+
confirmTakeover: "[Ctrl+T] 确认接管",
|
|
120
|
+
dirtyReturnEdit: "[Esc] 返回编辑",
|
|
121
|
+
dirtyPreviewSave: "[Ctrl+S] 预览并保存",
|
|
122
|
+
dirtyDiscard: "[Ctrl+D] 放弃更改",
|
|
123
|
+
enableConfirmTitle: "启用服务商 {provider}?",
|
|
124
|
+
disableConfirmTitle: "禁用服务商 {provider}?",
|
|
125
|
+
enableConfirmSummary: "保存并 /reload 后恢复该服务商的底层模型目录。",
|
|
126
|
+
disableConfirmSummary: "保存并 /reload 后隐藏该服务商的 chat/classifier/image 目录,但保留模型和认证。",
|
|
127
|
+
confirmEnable: "[Ctrl+X] 确认启用",
|
|
128
|
+
confirmDisable: "[Ctrl+X] 确认禁用",
|
|
129
|
+
disabledRetainAuth: "不会删除 models.json、model-config.json 模型或 auth。",
|
|
130
|
+
toggleProviderSuccess: "服务商 {provider} 已直接更新;生成 {count} 个备份。运行 /reload 使启停生效。",
|
|
131
|
+
deleteProviderTitle: "删除服务商 {provider}?",
|
|
132
|
+
deleteProviderSummary: "这是危险操作:将删除服务商 {provider} 的配置与模型目录覆盖。",
|
|
133
|
+
deleteProviderModels: "将移除 models.json 中该 provider 条目(含其 chat 模型与 API key 引用)。",
|
|
134
|
+
deleteProviderOverlay: "将移除 model-config.json 中该 provider 配置、managed overlay 与 classifier/image 目录。",
|
|
135
|
+
deleteProviderAuthRetained: "不会删除 auth.json 中的 Pi 凭据。",
|
|
136
|
+
deleteProviderOtherExtension: "若该 provider 也由其他扩展管理,删除本地配置条目后其运行时目录可能仍由该扩展决定。",
|
|
137
|
+
transactionRecovered: "已从中断的配置事务 {txId} 恢复两份配置的旧版本;请检查后重试操作。",
|
|
138
|
+
deleteProviderBuiltinRestore: "这是 Pi 内置服务商;保存并 /reload 后将恢复 Pi 内置目录。",
|
|
139
|
+
confirmDeleteProvider: "[Ctrl+S] 确认删除服务商",
|
|
140
|
+
deleteProviderSuccess: "服务商 {provider} 已通过双文件事务删除;生成 {count} 个备份。运行 /reload 应用目录恢复。",
|
|
141
|
+
deleteCurrentModelBlocked: "当前会话正在使用该服务商的模型;请先切换模型再删除。",
|
|
142
|
+
deleteThirdPartyBlocked: "未接管的第三方 runtime extension 服务商不能安全删除。",
|
|
143
|
+
discoveryCandidateSafety: "候选并非服务商官方保证;只有实际成功 URL 才进入草稿。",
|
|
144
|
+
discoveryUseAction: "[Ctrl+R] 使用首选候选",
|
|
145
|
+
discoveryNoCandidate: "没有可安全推导的目录;可进入连接设置显式配置。",
|
|
146
|
+
finalSaveTitle: "确认保存",
|
|
147
|
+
finalSaveBody: "确认后将通过双文件事务写入 models.json 与 model-config.json。",
|
|
148
|
+
discardConfirmTitle: "放弃全部更改?",
|
|
149
|
+
discardConfirmBody: "这会清空当前服务商的内存草稿和模型选择,不会写入配置。",
|
|
150
|
+
temporarySaveBlocked: "临时密钥只能用于本次获取列表;请在连接设置中选择 API 密钥、环境变量引用、Pi 已有认证或无需认证后再预览保存",
|
|
151
|
+
providerEnabled: "已启用",
|
|
152
|
+
providerDisabled: "已禁用",
|
|
153
|
+
providerEnabledStatus: "运行状态:{value}",
|
|
154
|
+
disableCurrentModelBlocked: "当前会话正在使用该服务商的模型;请先切换模型再禁用",
|
|
155
|
+
disableThirdPartyBlocked: "未接管的第三方 runtime extension 服务商不能通过同名注册安全禁用",
|
|
156
|
+
disabledSaveHint: "禁用状态将在保存并 /reload 后生效;不会删除模型或认证",
|
|
157
|
+
testProviderDisabled: "服务商已禁用;请先启用并保存 /reload",
|
|
158
|
+
testDraftDirty: "存在尚未保存的更改;请先保存并 /reload,再测试当前运行时模型",
|
|
159
|
+
testNoModels: "当前运行时没有该服务商可测试的模型",
|
|
160
|
+
testSelectionTitle: "选择要测试的模型",
|
|
161
|
+
testSelectionHelp: "↑↓ 选择 · Enter/Space 勾选 · Ctrl+S 执行 · Esc 取消;image 默认不选",
|
|
162
|
+
testImageConfirmTitle: "确认测试图像模型?",
|
|
163
|
+
testImageRun: "[Ctrl+I] 确认测试图像",
|
|
164
|
+
testImageConfirmBody: "将真实调用 {count} 个图像模型生成红点,可能计费且超时上限为 120 秒。",
|
|
165
|
+
testRunConfirmTitle: "确认真实调用模型?",
|
|
166
|
+
testRunAction: "[Ctrl+S] 开始真实测试",
|
|
167
|
+
testRunConfirmBody: "将真实调用 {count} 个当前运行时模型,可能产生费用;默认并发为 2。",
|
|
168
|
+
testRunning: "正在测试;Esc 取消全部剩余请求…",
|
|
169
|
+
testResultsTitle: "模型可用性测试结果",
|
|
170
|
+
testResultsHelp: "←/→ 水平切换操作 · Enter 执行",
|
|
171
|
+
testResultsBack: "[Esc] 返回",
|
|
172
|
+
testResultsRetry: "重试失败项",
|
|
173
|
+
panelSummaryName: "服务商名称:{value}",
|
|
174
|
+
panelSummaryId: "服务商 ID:{value}",
|
|
175
|
+
panelSummaryProtocol: "调用协议:{value}",
|
|
176
|
+
panelSummaryBaseUrl: "模型调用地址:{value}",
|
|
177
|
+
panelSummaryDiscoveryUrl: "模型列表地址:{value}",
|
|
178
|
+
panelSummaryAuth: "认证方式:{value}",
|
|
179
|
+
panelSummaryProxy: "专属代理:{value}",
|
|
180
|
+
panelSummaryModels: "模型:聊天 {chat} · 判定 {classifier} · 图像生成 {image}",
|
|
181
|
+
panelTakeoverCount: "尚未接管的 models.json chat:{count}",
|
|
182
|
+
panelDescName: "设置界面显示名称;不会改变服务商 ID。",
|
|
183
|
+
panelDescId: "配置标识,只读。provider/model identity 使用此 ID。",
|
|
184
|
+
panelDescOwnership: "决定 classifier/image 是否由本插件注册;不要接管 Pi 内置服务商。",
|
|
185
|
+
panelDescBaseUrl: "模型实际调用地址。可使用默认地址或设置自定义地址。",
|
|
186
|
+
panelDescDiscoveryUrl: "仅用于获取远程模型列表,不会自动假设 /models。",
|
|
187
|
+
panelDescAuth: "选择 API 密钥、环境变量引用、Pi 已有认证、临时密钥或无需认证。",
|
|
188
|
+
panelDescDiscoveryAuth: "仅用于获取模型列表的认证头;none 不转发 Pi auth headers。",
|
|
189
|
+
panelDescProxy: "代理只作用于此服务商,不修改进程环境或全局 dispatcher。",
|
|
190
|
+
panelDescDefaultType: "远程列表缺少类型时使用;落盘值仍为 chat/classifier/image。",
|
|
191
|
+
panelDescChatApi: "聊天模型调用协议;API 名保持原文。",
|
|
192
|
+
panelDescClassifierApi: "判定模型调用协议;API 名保持原文。",
|
|
193
|
+
panelDescImageApi: "图像生成模型调用协议;API 名保持原文。",
|
|
194
|
+
panelDescContext: "远程模型缺少 context window 时使用的默认值。",
|
|
195
|
+
panelDescMaxTokens: "远程聊天模型缺少 max tokens 时使用的默认值。",
|
|
196
|
+
panelModelName: "模型名称:{value}",
|
|
197
|
+
panelModelId: "模型 ID:{value}",
|
|
198
|
+
panelModelType: "模型类型:{value}",
|
|
199
|
+
panelModelApi: "调用协议:{value}",
|
|
200
|
+
panelModelContext: "Context window:{value}",
|
|
201
|
+
panelModelMaxTokens: "Max tokens:{value}",
|
|
202
|
+
panelModelInput: "输入模态:{value}",
|
|
203
|
+
panelModelOutput: "输出模态:{value}",
|
|
204
|
+
panelModelCost: "费用:input {input} · output {output} · cache read {cacheRead} · cache write {cacheWrite}",
|
|
205
|
+
compatContextDisplay: "上下文:128K · Pi兼容默认值(未验证)",
|
|
206
|
+
compatMaxDisplay: "最大输出:16K · Pi兼容默认值(未验证)",
|
|
207
|
+
compatSaveRisk: "以下模型使用 Pi 兼容默认值(未验证),将显式写入 128000/16384:{items}",
|
|
208
|
+
probeBlocked: "仅已保存并 /reload、已启用、非dirty且使用持久认证的managed chat/classifier模型可探测;image不支持",
|
|
209
|
+
probeChooseTitle: "选择上下文探测上限",
|
|
210
|
+
probeCostWarning: "将发送近似大输入进行真实API调用,可能计费;tokenizer、网关截断或压缩会使结果不精确。",
|
|
211
|
+
contextProbeRun: "[Ctrl+K] 开始串行探测",
|
|
212
|
+
probeRun: "[Ctrl+L] 开始串行探测",
|
|
213
|
+
probeUse: "使用已验证安全下限",
|
|
214
|
+
probeKeep: "保持当前值",
|
|
215
|
+
probeBack: "返回",
|
|
216
|
+
probeNoSuccess: "没有成功下限,无法写入探测结果",
|
|
217
|
+
probeMaxConflict: "探测下限小于当前maxTokens;不会自动调整maxTokens,请等待逐模型参数编辑功能",
|
|
218
|
+
panelModelBaseUrl: "Base URL:{value}",
|
|
219
|
+
panelModelReadonly: "此模型只读,不能在这里修改或移除。",
|
|
220
|
+
panelModelReadonlyExternal: "此模型来自 models.json,但尚未由本插件管理;可先执行“接管已有模型”。",
|
|
221
|
+
panelModelReadonlyRuntime: "此模型来自 Pi 内置目录或其他运行时扩展;本插件不能把取消勾选伪装成删除。",
|
|
222
|
+
takeoverModelsTitle: "接管已有模型?",
|
|
223
|
+
takeoverModelsBody: "仅接管服务商 {provider} 在 models.json 中已有的 {count} 个 chat 模型。接管只修改内存草稿;取消、返回或放弃不会写盘,最终确认保存后才更新 managedChatIds。Pi 内置目录和其他运行时扩展模型不在接管范围。",
|
|
224
|
+
takeoverModelsNone: "当前服务商没有可接管的 models.json chat 模型",
|
|
225
|
+
previewConfirmSave: "[Ctrl+S] 确认保存",
|
|
226
|
+
previewBackEdit: "[Esc] 返回继续编辑",
|
|
227
|
+
previewDiscard: "[Ctrl+D] 放弃全部更改",
|
|
228
|
+
providerTitle: "服务商连接设置:{id}",
|
|
229
|
+
fieldName: "服务商名称:{value}",
|
|
230
|
+
fieldId: "服务商 ID:{value}(只读)",
|
|
231
|
+
fieldIdReadonly: "服务商 ID 用于配置标识,当前不能在这里修改",
|
|
232
|
+
fieldOwnership: "配置归属:{value}",
|
|
233
|
+
owned: "本插件管理的自定义服务商",
|
|
234
|
+
externalOwned: "内置或外部服务商(不接管目录)",
|
|
235
|
+
fieldBaseUrl: "模型调用地址(Base URL):{value}",
|
|
236
|
+
fieldDiscoveryUrl: "模型列表地址(Discovery URL):{value}",
|
|
237
|
+
fieldAuth: "认证方式:{value}",
|
|
238
|
+
fieldDiscoveryAuth: "获取模型列表时的认证头:{value}",
|
|
239
|
+
fieldProxy: "仅此服务商使用代理:{value}",
|
|
240
|
+
fieldDefaultType: "远程模型默认类型:{value}",
|
|
241
|
+
fieldChatApi: "调用协议(聊天):{value}",
|
|
242
|
+
fieldClassifierApi: "调用协议(判定):{value}",
|
|
243
|
+
fieldImageApi: "调用协议(图像生成):{value}",
|
|
244
|
+
fieldContext: "默认 context window:{value}",
|
|
245
|
+
fieldMaxTokens: "默认 max tokens:{value}",
|
|
246
|
+
providerDisplayName: "服务商名称",
|
|
247
|
+
takeoverTitle: "接管服务商目录",
|
|
248
|
+
takeoverBody: "插件自有服务商会由本插件注册完整目录;仅应对自定义服务商启用,不能用于接管 Pi 内置服务商。",
|
|
249
|
+
urlSet: "使用自定义地址",
|
|
250
|
+
urlRestore: "使用默认地址(移除自定义地址)",
|
|
251
|
+
discoveryUrlSet: "设置模型列表地址",
|
|
252
|
+
discoveryUrlClear: "清除模型列表地址",
|
|
253
|
+
proxySet: "设置此服务商专属代理",
|
|
254
|
+
proxyDisable: "关闭此服务商专属代理",
|
|
255
|
+
clear: "清空",
|
|
256
|
+
baseUrl: "Base URL",
|
|
257
|
+
discoveryUrl: "Discovery URL(独立于 Base URL)",
|
|
258
|
+
proxyUrl: "服务商专属代理 URL(例如 http://127.0.0.1:7890,仅示例)",
|
|
259
|
+
authTitle: "认证方式",
|
|
260
|
+
authLiteralOption: "API 密钥",
|
|
261
|
+
authEnvOption: "环境变量引用",
|
|
262
|
+
authReuseOption: "使用 Pi 已有认证",
|
|
263
|
+
authTemporaryOption: "临时密钥(仅本次获取列表)",
|
|
264
|
+
authAnonymousOption: "无需认证",
|
|
265
|
+
authModelsJson: "API 密钥或环境变量引用",
|
|
266
|
+
authReuse: "使用 Pi 已有认证",
|
|
267
|
+
authTemporary: "临时密钥(仅本次获取列表)",
|
|
268
|
+
authAnonymous: "无需认证",
|
|
269
|
+
authSourceAnonymous: "匿名",
|
|
270
|
+
authSourceStored: "已存储凭据",
|
|
271
|
+
secretApiKey: "输入 API key",
|
|
272
|
+
secretTemporary: "输入临时 API key(不会写入磁盘)",
|
|
273
|
+
envName: "环境变量名(不含 $)",
|
|
274
|
+
discoveryAuthTitle: "Discovery 认证头",
|
|
275
|
+
authHeaderAuto: "自动(按调用协议)",
|
|
276
|
+
authHeaderBearer: "Bearer Authorization",
|
|
277
|
+
authHeaderXApiKey: "x-api-key",
|
|
278
|
+
authHeaderApiKey: "api-key(Azure)",
|
|
279
|
+
authHeaderGoogle: "x-goog-api-key(Google)",
|
|
280
|
+
authHeaderAnthropic: "Anthropic x-api-key + version",
|
|
281
|
+
authHeaderNone: "不发送认证头",
|
|
282
|
+
defaultTypeTitle: "远程缺少类型时使用",
|
|
283
|
+
defaultContextTitle: "默认 context window",
|
|
284
|
+
defaultMaxTitle: "默认 max tokens",
|
|
285
|
+
fetching: "正在从 {url} 获取模型…",
|
|
286
|
+
fetchCancelled: "已取消远程获取",
|
|
287
|
+
temporaryMustPersist: "临时密钥只用于获取列表;保存前请选择持久认证方式",
|
|
288
|
+
emptyTitle: "确认清空插件管理集合",
|
|
289
|
+
emptyBody: "这会移除该服务商下所有由 pi-model-config 管理的条目,但不动内置目录和其他服务商。",
|
|
290
|
+
previewTitle: "预览更改",
|
|
291
|
+
previewProviderSettings: "服务商设置:{provider}",
|
|
292
|
+
previewModelConfig: "模型配置:{provider}",
|
|
293
|
+
previewSettingsChanged: "服务商设置有更改",
|
|
294
|
+
previewSettingsUnchanged: "服务商设置无更改",
|
|
295
|
+
previewKeyUpdated: "API 密钥:将更新,内容已隐藏",
|
|
296
|
+
previewModelsAdded: "添加模型:{items}",
|
|
297
|
+
previewModelsRemoved: "移除模型:{items}",
|
|
298
|
+
previewModelsModified: "修改模型:{items}",
|
|
299
|
+
previewModelsUnchanged: "未变化模型:{count}",
|
|
300
|
+
saveSuccess: "配置已原子写入;生成 {count} 个备份。运行 /reload 使服务商 transport/classifier/image 注册生效。",
|
|
301
|
+
selectorTitle: "配置模型:{provider}",
|
|
302
|
+
selectorSearchPrompt: "搜索模型:",
|
|
303
|
+
selectorSearchPlaceholder: "搜索模型名称或 ID",
|
|
304
|
+
selectorHelp: "Tab/Shift+Tab 切换焦点 · Enter 勾选 · Space 输入搜索 · Ctrl+T 切换远程类型 · Ctrl+A/D 批量 · Ctrl+S 预览",
|
|
305
|
+
selectorSemantics: "勾选 = 保存后保留或添加;取消勾选 = 保存后移除。获取和勾选不会立即保存。",
|
|
306
|
+
selectorNarrow: "终端过窄;请扩宽后重试,Esc 返回",
|
|
307
|
+
selectorNoMatches: " 没有匹配的模型",
|
|
308
|
+
selectorReadonlyStatus: "此模型不由本插件管理,当前不能在这里修改或移除",
|
|
309
|
+
selectorReadonlyExternal: "此模型来自 models.json,需先使用“接管已有模型”后才能修改或移除",
|
|
310
|
+
selectorReadonlyRuntime: "此模型来自 Pi 内置目录或其他运行时扩展,不能在这里移除",
|
|
311
|
+
selectorTypeConflict: "不能切换为{type}:{identity} 已存在",
|
|
312
|
+
selectorReadonlyTag: "只读",
|
|
313
|
+
selectorTypeChat: "聊天",
|
|
314
|
+
selectorTypeClassifier: "判定",
|
|
315
|
+
selectorTypeImage: "图像生成",
|
|
316
|
+
selectorFooter: "可见 {filtered} / 总计 {total} · 已选 {selected} · 只读 {readonly}",
|
|
317
|
+
maskedLabel: "密钥: ",
|
|
318
|
+
maskedHelp: "Enter 保存 · Esc 取消 · Ctrl+U 清空 · 输入始终掩码",
|
|
319
|
+
diffConnection: "连接: {before}\n→ {after}",
|
|
320
|
+
diffSafety: "密钥值未显示;未勾选只移除本插件管理集合,不删除其他服务商或内置目录。",
|
|
321
|
+
valueDefault: "默认",
|
|
322
|
+
valueInvalidUrl: "[无效 URL]",
|
|
323
|
+
valueAnonymous: "无需认证",
|
|
324
|
+
valueReuseAuth: "使用 Pi 已有认证",
|
|
325
|
+
valueUnset: "未配置",
|
|
326
|
+
valueProxyDisabled: "关闭",
|
|
327
|
+
valueEnv: "环境变量 {value}",
|
|
328
|
+
valueLiteralMasked: "literal(已掩码)",
|
|
329
|
+
valueRedacted: "[已隐藏]",
|
|
330
|
+
errorExternal: "[{code}] 外部或系统错误(以下仅为不可信诊断文本):{detail}",
|
|
331
|
+
errorUnknownDetail: "详情不可安全显示",
|
|
332
|
+
} as const;
|
|
333
|
+
|
|
334
|
+
export type TranslationKey = keyof typeof ZH;
|
|
335
|
+
|
|
336
|
+
const EN = {
|
|
337
|
+
commandDescription: "Models and providers / 模型与服务商配置",
|
|
338
|
+
mainTitle: "Models and Providers",
|
|
339
|
+
mainCancel: "Cancel",
|
|
340
|
+
back: "Back",
|
|
341
|
+
panelBack: "[B] Back",
|
|
342
|
+
languageTitle: "Language / 语言",
|
|
343
|
+
languageZh: "简体中文",
|
|
344
|
+
languageEn: "English",
|
|
345
|
+
languageSaved: "UI language saved: {language} (effective immediately)",
|
|
346
|
+
tuiRequired: "/model-config requires the interactive TUI; custom multi-select is unavailable in RPC/print/JSON modes",
|
|
347
|
+
startupIssues: "Some providers were not registered because of configuration errors. You can continue editing: {issues}{more}",
|
|
348
|
+
startupMore: "; {count} more",
|
|
349
|
+
newProviderTitle: "Choose a Built-in Provider (built-in does not mean authenticated or available)",
|
|
350
|
+
providerTagCustom: "Custom",
|
|
351
|
+
providerTagBuiltin: "Built-in",
|
|
352
|
+
providerLabelCustom: "★ {display} [{status}]",
|
|
353
|
+
providerLabelConfigured: "◆ {display} [{status}]",
|
|
354
|
+
providerLabelBuiltin: "{display} [{status}]",
|
|
355
|
+
newProviderId: "New Provider ID",
|
|
356
|
+
providerExists: "Provider {id} already exists. Opening the existing configuration instead of overwriting it as new.",
|
|
357
|
+
panelLanguage: "Interface language: {language}",
|
|
358
|
+
panelSearchProviderPrompt: "Search providers: ",
|
|
359
|
+
panelSearchProviderPlaceholder: "Name or ID",
|
|
360
|
+
panelNoConfiguredProviders: "No configured providers",
|
|
361
|
+
panelNoProviderSelected: "Select a provider to view details",
|
|
362
|
+
overviewEnter: "[Enter] Open provider",
|
|
363
|
+
overviewSearch: "[/] Search",
|
|
364
|
+
overviewDisable: "[Ctrl+X] Disable",
|
|
365
|
+
overviewEnable: "[Ctrl+X] Enable",
|
|
366
|
+
overviewDelete: "[Ctrl+Del] Delete provider",
|
|
367
|
+
overviewAdd: "[Ctrl+N] Add provider",
|
|
368
|
+
overviewClose: "[Esc] Close",
|
|
369
|
+
overviewLanguage: "[F2] Interface language",
|
|
370
|
+
detailConnectionTitle: "Connection Summary",
|
|
371
|
+
detailModelsTitle: "Model Overview",
|
|
372
|
+
detailFetchHint: "Open Configure models, then press Ctrl+R to fetch the remote model list",
|
|
373
|
+
detailEditConnection: "[Ctrl+E] Edit connection",
|
|
374
|
+
detailConfigureModels: "[Ctrl+M] Configure models",
|
|
375
|
+
detailTakeover: "[Ctrl+T] Manage existing models",
|
|
376
|
+
detailDisable: "[Ctrl+X] Disable provider",
|
|
377
|
+
detailEnable: "[Ctrl+X] Enable provider",
|
|
378
|
+
detailTestModels: "[Ctrl+P] Batch test models",
|
|
379
|
+
detailBack: "[Esc] Back to overview",
|
|
380
|
+
connectionModify: "[Enter] Modify field",
|
|
381
|
+
connectionPreview: "[Ctrl+S] Preview changes",
|
|
382
|
+
connectionBack: "[Esc] Back to provider",
|
|
383
|
+
editorTitle: "Edit model parameters: {model}",
|
|
384
|
+
editorName: "Name",
|
|
385
|
+
editorContext: "Context Window",
|
|
386
|
+
editorMaxTokens: "Max Tokens",
|
|
387
|
+
editorReasoning: "Reasoning",
|
|
388
|
+
editorThinkingMap: "Thinking level map",
|
|
389
|
+
editorInput: "Input modalities",
|
|
390
|
+
editorOutput: "Output modalities",
|
|
391
|
+
editorCost: "Cost",
|
|
392
|
+
editorReview: "Review and apply",
|
|
393
|
+
editorCancel: "Cancel",
|
|
394
|
+
editorModify: "[Enter] Modify field",
|
|
395
|
+
editorReturn: "[Esc] Back",
|
|
396
|
+
editorInlineHint: "Enter a value, then press Enter",
|
|
397
|
+
editorMapHint: "Thinking maps: blank restores default; - or null disables this level",
|
|
398
|
+
editorMapDefault: "protocol/catalog default",
|
|
399
|
+
editorMapDisabled: "disabled",
|
|
400
|
+
editorDone: "Done",
|
|
401
|
+
editorModalities: "Choose modalities",
|
|
402
|
+
valueYes: "Yes",
|
|
403
|
+
valueNo: "No",
|
|
404
|
+
editorKeep: "Keep",
|
|
405
|
+
editorEnable: "Enable and map",
|
|
406
|
+
editorDisable: "Disable",
|
|
407
|
+
editorPositiveError: "Enter a positive integer; K/M suffixes are supported",
|
|
408
|
+
editorMaxError: "maxTokens cannot exceed contextWindow",
|
|
409
|
+
editorCostError: "Cost must be a non-negative decimal number",
|
|
410
|
+
editorModalityEmpty: "Select at least one modality",
|
|
411
|
+
editorModalityRequired: "This field must include {modality}",
|
|
412
|
+
editorThinkingSummary: "Default thinking level: {level} (Pi baseline; session/settings may override) · map source: {source}",
|
|
413
|
+
thinkingSourceExisting: "existing config",
|
|
414
|
+
thinkingSourceRemote: "remote catalog",
|
|
415
|
+
thinkingSourceCatalog: "runtime catalog",
|
|
416
|
+
thinkingSourceProtocol: "protocol default",
|
|
417
|
+
thinkingSourceUser: "user edit",
|
|
418
|
+
thinkingSourceProbed: "probe",
|
|
419
|
+
thinkingSourceDefault: "provider default / unspecified",
|
|
420
|
+
editorConfirmTitle: "Apply model parameters to the draft?",
|
|
421
|
+
editorApply: "[Ctrl+S] Apply to draft",
|
|
422
|
+
thinkingProbeBlocked: "Only saved/reloaded, enabled, clean managed chat models with persistent auth can probe thinking levels",
|
|
423
|
+
thinkingProbeTitle: "Probe thinking levels",
|
|
424
|
+
thinkingProbeWarning: "This serially sends real minimal requests per level and may incur cost; server acceptance does not prove stronger reasoning ability.",
|
|
425
|
+
modelSearch: "[/] Search",
|
|
426
|
+
modelSelectFiltered: "[Ctrl+A] Check search results",
|
|
427
|
+
modelDeselectFiltered: "[Ctrl+D] Uncheck search results",
|
|
428
|
+
modelCycleType: "[Ctrl+T] Change model type",
|
|
429
|
+
modelEditParameters: "[Ctrl+E] Edit model parameters",
|
|
430
|
+
editorExternalTakeover: "This model comes from models.json. Use Manage existing models on the connection page before editing it.",
|
|
431
|
+
editorManagedOnly: "Only checked models managed by this plugin in the current draft can be edited",
|
|
432
|
+
modelProbeContext: "[Ctrl+K] Probe context capacity",
|
|
433
|
+
modelProbeThinking: "[Ctrl+L] Probe thinking levels",
|
|
434
|
+
modelFetch: "[Ctrl+R] Fetch remote model list",
|
|
435
|
+
modelPreview: "[Ctrl+S] Preview changes",
|
|
436
|
+
modelBack: "[Esc] Back to provider",
|
|
437
|
+
addProviderTitle: "Add Provider",
|
|
438
|
+
addBuiltinChoice: "Choose built-in provider",
|
|
439
|
+
addCustomChoice: "Add custom provider",
|
|
440
|
+
discoveryRequired: "No model catalog address (Discovery URL) is configured",
|
|
441
|
+
discoveryDerivedTitle: "A candidate can be derived from the model API address (not guaranteed to be the provider official Discovery endpoint)",
|
|
442
|
+
discoveryUseDerived: "Use derived address (add to this draft; persisted only after final save): {url}",
|
|
443
|
+
discoveryUseCandidates: "Use preferred address: {url} ({count} same-origin fallback(s); successful URL enters the draft and persists only after final save)",
|
|
444
|
+
discoveryRequiredEdit: "Open connection settings",
|
|
445
|
+
discoveryRequiredCancel: "Cancel fetch",
|
|
446
|
+
dirtyLeaveTitle: "Discard unsaved changes?",
|
|
447
|
+
dirtyLeaveBody: "Returning to the overview discards this provider's connection draft, model selection, and takeover state. Nothing will be written.",
|
|
448
|
+
confirmCancel: "[Esc] Cancel / Back",
|
|
449
|
+
confirmSave: "[Ctrl+S] Confirm Save",
|
|
450
|
+
confirmEmpty: "[Ctrl+S] Confirm Empty",
|
|
451
|
+
confirmTakeover: "[Ctrl+T] Confirm Takeover",
|
|
452
|
+
dirtyReturnEdit: "[Esc] Return to Editing",
|
|
453
|
+
dirtyPreviewSave: "[Ctrl+S] Preview and Save",
|
|
454
|
+
dirtyDiscard: "[Ctrl+D] Discard Changes",
|
|
455
|
+
enableConfirmTitle: "Enable provider {provider}?",
|
|
456
|
+
disableConfirmTitle: "Disable provider {provider}?",
|
|
457
|
+
enableConfirmSummary: "After save and /reload, restore the provider underlying model catalog.",
|
|
458
|
+
disableConfirmSummary: "After save and /reload, hide this provider chat/classifier/image catalog while retaining models and auth.",
|
|
459
|
+
confirmEnable: "[Ctrl+X] Confirm Enable",
|
|
460
|
+
confirmDisable: "[Ctrl+X] Confirm Disable",
|
|
461
|
+
disabledRetainAuth: "models.json, model-config.json models, and auth are retained.",
|
|
462
|
+
toggleProviderSuccess: "Provider {provider} was updated directly; {count} backup(s) created. Run /reload to apply the enablement state.",
|
|
463
|
+
deleteProviderTitle: "Delete provider {provider}?",
|
|
464
|
+
deleteProviderSummary: "This is dangerous: the provider {provider} configuration and model catalog overlay will be deleted.",
|
|
465
|
+
deleteProviderModels: "The models.json provider entry will be removed, including its chat models and API-key reference.",
|
|
466
|
+
deleteProviderOverlay: "The model-config.json provider configuration, managed overlay, and classifier/image catalog will be removed.",
|
|
467
|
+
deleteProviderAuthRetained: "Pi credentials in auth.json are not deleted.",
|
|
468
|
+
deleteProviderOtherExtension: "If another extension manages this provider, its runtime catalog may still be determined by that extension after the local entry is removed.",
|
|
469
|
+
transactionRecovered: "Recovered both configuration files to their old generation from interrupted transaction {txId}; review them before retrying.",
|
|
470
|
+
deleteProviderBuiltinRestore: "This is a Pi built-in provider; save and /reload restores Pi's built-in catalog.",
|
|
471
|
+
confirmDeleteProvider: "[Ctrl+S] Confirm Delete Provider",
|
|
472
|
+
deleteProviderSuccess: "Provider {provider} was deleted through the two-file transaction; {count} backup(s) created. Run /reload to restore/apply catalogs.",
|
|
473
|
+
deleteCurrentModelBlocked: "The current session is using a model from this provider. Switch models before deleting it.",
|
|
474
|
+
deleteThirdPartyBlocked: "An unowned third-party runtime extension provider cannot be deleted safely.",
|
|
475
|
+
discoveryCandidateSafety: "The candidate is not guaranteed by the provider; only an actually successful URL enters the draft.",
|
|
476
|
+
discoveryUseAction: "[Ctrl+R] Use Preferred Candidate",
|
|
477
|
+
discoveryNoCandidate: "No candidate can be derived safely; open connection settings to configure one explicitly.",
|
|
478
|
+
finalSaveTitle: "Confirm Save",
|
|
479
|
+
finalSaveBody: "This writes models.json and model-config.json through the two-file transaction.",
|
|
480
|
+
discardConfirmTitle: "Discard all changes?",
|
|
481
|
+
discardConfirmBody: "This clears the current provider draft and model selection from memory without writing configuration.",
|
|
482
|
+
temporarySaveBlocked: "A temporary key is only for this catalog fetch. Choose an API key, environment reference, existing Pi authentication, or no authentication before previewing a save.",
|
|
483
|
+
providerEnabled: "Enabled",
|
|
484
|
+
providerDisabled: "Disabled",
|
|
485
|
+
providerEnabledStatus: "Runtime status: {value}",
|
|
486
|
+
disableCurrentModelBlocked: "The current session is using a model from this provider. Switch models before disabling it.",
|
|
487
|
+
disableThirdPartyBlocked: "An unowned third-party runtime extension provider cannot be safely disabled by same-name registration.",
|
|
488
|
+
disabledSaveHint: "The disabled state takes effect after save and /reload; models and authentication are not deleted.",
|
|
489
|
+
testProviderDisabled: "The provider is disabled. Enable it, save, and /reload first.",
|
|
490
|
+
testDraftDirty: "There are unsaved changes. Save and /reload before testing runtime models.",
|
|
491
|
+
testNoModels: "The current runtime has no testable models for this provider.",
|
|
492
|
+
testSelectionTitle: "Select models to test",
|
|
493
|
+
testSelectionHelp: "↑↓ select · Enter/Space toggle · Ctrl+S run · Esc cancel; images default off",
|
|
494
|
+
testImageConfirmTitle: "Test image models?",
|
|
495
|
+
testImageRun: "[Ctrl+I] Confirm Image Test",
|
|
496
|
+
testImageConfirmBody: "This makes real paid-capable calls to {count} image model(s) to generate a red dot, with a 120-second timeout.",
|
|
497
|
+
testRunConfirmTitle: "Make real model calls?",
|
|
498
|
+
testRunAction: "[Ctrl+S] Start Real Tests",
|
|
499
|
+
testRunConfirmBody: "This calls {count} currently loaded runtime model(s) and may incur cost; default concurrency is 2.",
|
|
500
|
+
testRunning: "Testing; press Esc to cancel all remaining requests…",
|
|
501
|
+
testResultsTitle: "Model availability results",
|
|
502
|
+
testResultsHelp: "←/→ switch actions · Enter activate",
|
|
503
|
+
testResultsBack: "[Esc] Back",
|
|
504
|
+
testResultsRetry: "Retry failures",
|
|
505
|
+
panelSummaryName: "Provider name: {value}",
|
|
506
|
+
panelSummaryId: "Provider ID: {value}",
|
|
507
|
+
panelSummaryProtocol: "Protocols: {value}",
|
|
508
|
+
panelSummaryBaseUrl: "Model API address: {value}",
|
|
509
|
+
panelSummaryDiscoveryUrl: "Model catalog address: {value}",
|
|
510
|
+
panelSummaryAuth: "Authentication: {value}",
|
|
511
|
+
panelSummaryProxy: "Provider-only proxy: {value}",
|
|
512
|
+
panelSummaryModels: "Models: Chat {chat} · Classification {classifier} · Image generation {image}",
|
|
513
|
+
panelTakeoverCount: "Unmanaged models.json chat models: {count}",
|
|
514
|
+
panelDescName: "Set the display name without changing the provider ID.",
|
|
515
|
+
panelDescId: "Read-only configuration identity used by provider/model identities.",
|
|
516
|
+
panelDescOwnership: "Controls classifier/image registration. Never take ownership of a Pi built-in provider.",
|
|
517
|
+
panelDescBaseUrl: "Address used for model calls. Use the default or set a custom address.",
|
|
518
|
+
panelDescDiscoveryUrl: "Used only for remote model discovery; /models is never assumed.",
|
|
519
|
+
panelDescAuth: "Choose an API key, environment reference, existing Pi authentication, temporary key, or no authentication.",
|
|
520
|
+
panelDescDiscoveryAuth: "Auth header for catalog discovery only; none forwards no Pi auth headers.",
|
|
521
|
+
panelDescProxy: "Applies only to this provider; process environment and global dispatcher remain unchanged.",
|
|
522
|
+
panelDescDefaultType: "Used when remote entries omit type; stored values remain chat/classifier/image.",
|
|
523
|
+
panelDescChatApi: "Chat model protocol; API names remain unchanged.",
|
|
524
|
+
panelDescClassifierApi: "Classification model protocol; API names remain unchanged.",
|
|
525
|
+
panelDescImageApi: "Image generation protocol; API names remain unchanged.",
|
|
526
|
+
panelDescContext: "Default used when a remote model omits context window.",
|
|
527
|
+
panelDescMaxTokens: "Default used when a remote chat model omits max tokens.",
|
|
528
|
+
panelModelName: "Model name: {value}",
|
|
529
|
+
panelModelId: "Model ID: {value}",
|
|
530
|
+
panelModelType: "Model type: {value}",
|
|
531
|
+
panelModelApi: "Protocol: {value}",
|
|
532
|
+
panelModelContext: "Context window: {value}",
|
|
533
|
+
panelModelMaxTokens: "Max tokens: {value}",
|
|
534
|
+
panelModelInput: "Input modalities: {value}",
|
|
535
|
+
panelModelOutput: "Output modalities: {value}",
|
|
536
|
+
panelModelCost: "Cost: input {input} · output {output} · cache read {cacheRead} · cache write {cacheWrite}",
|
|
537
|
+
compatContextDisplay: "Context: 128K · Pi compatibility default (unverified)",
|
|
538
|
+
compatMaxDisplay: "Max output: 16K · Pi compatibility default (unverified)",
|
|
539
|
+
compatSaveRisk: "These models use unverified Pi compatibility defaults and will explicitly persist 128000/16384: {items}",
|
|
540
|
+
probeBlocked: "Only saved/reloaded, enabled, clean managed chat/classifier models with persistent auth can be probed; images are unsupported.",
|
|
541
|
+
probeChooseTitle: "Choose context probe ceiling",
|
|
542
|
+
probeCostWarning: "This sends approximate large real API inputs and may incur cost; tokenizer behavior, gateway truncation, or compression can make results unreliable.",
|
|
543
|
+
contextProbeRun: "[Ctrl+K] Start Serial Probe",
|
|
544
|
+
probeRun: "[Ctrl+L] Start Serial Probe",
|
|
545
|
+
probeUse: "Use verified safe lower bound",
|
|
546
|
+
probeKeep: "Keep current value",
|
|
547
|
+
probeBack: "Back",
|
|
548
|
+
probeNoSuccess: "No successful lower bound is available to write",
|
|
549
|
+
probeMaxConflict: "The probe lower bound is below current maxTokens. maxTokens will not be changed automatically; wait for per-model parameter editing.",
|
|
550
|
+
panelModelBaseUrl: "Base URL: {value}",
|
|
551
|
+
panelModelReadonly: "This model is read-only and cannot be changed or removed here.",
|
|
552
|
+
panelModelReadonlyExternal: "This model is in models.json but is not managed by this plugin yet; use Manage existing models first.",
|
|
553
|
+
panelModelReadonlyRuntime: "This model comes from Pi's built-in catalog or another runtime extension; unchecking it cannot be presented as deletion.",
|
|
554
|
+
takeoverModelsTitle: "Manage existing models?",
|
|
555
|
+
takeoverModelsBody: "This takes ownership only of the {count} existing chat model(s) for provider {provider} in models.json. It changes the in-memory draft only; cancel, back, or discard writes nothing. managedChatIds changes only after final save. Pi built-in catalogs and other runtime-extension models are outside this scope.",
|
|
556
|
+
takeoverModelsNone: "This provider has no models.json chat models available for takeover",
|
|
557
|
+
previewConfirmSave: "[Ctrl+S] Confirm Save",
|
|
558
|
+
previewBackEdit: "[Esc] Back to Editing",
|
|
559
|
+
previewDiscard: "[Ctrl+D] Discard All Changes",
|
|
560
|
+
providerTitle: "Provider connection settings: {id}",
|
|
561
|
+
fieldName: "Provider name: {value}",
|
|
562
|
+
fieldId: "Provider ID: {value} (read-only)",
|
|
563
|
+
fieldIdReadonly: "Provider ID is the configuration identifier and cannot be changed here",
|
|
564
|
+
fieldOwnership: "Configuration ownership: {value}",
|
|
565
|
+
owned: "Custom provider managed by this plugin",
|
|
566
|
+
externalOwned: "Built-in or external provider (catalog not owned)",
|
|
567
|
+
fieldBaseUrl: "Model API address (Base URL): {value}",
|
|
568
|
+
fieldDiscoveryUrl: "Model catalog address (Discovery URL): {value}",
|
|
569
|
+
fieldAuth: "Authentication: {value}",
|
|
570
|
+
fieldDiscoveryAuth: "Auth header used to fetch the model catalog: {value}",
|
|
571
|
+
fieldProxy: "Proxy used only by this provider: {value}",
|
|
572
|
+
fieldDefaultType: "Default type for remote models: {value}",
|
|
573
|
+
fieldChatApi: "Protocol (chat): {value}",
|
|
574
|
+
fieldClassifierApi: "Protocol (classification): {value}",
|
|
575
|
+
fieldImageApi: "Protocol (image generation): {value}",
|
|
576
|
+
fieldContext: "Default context window: {value}",
|
|
577
|
+
fieldMaxTokens: "Default max tokens: {value}",
|
|
578
|
+
providerDisplayName: "Provider name",
|
|
579
|
+
takeoverTitle: "Take ownership of provider catalog",
|
|
580
|
+
takeoverBody: "A plugin-owned provider is registered with its complete catalog. Enable this only for custom providers; never take ownership of a Pi built-in provider.",
|
|
581
|
+
urlSet: "Use custom address",
|
|
582
|
+
urlRestore: "Use default address (remove custom address)",
|
|
583
|
+
discoveryUrlSet: "Set model catalog address",
|
|
584
|
+
discoveryUrlClear: "Clear model catalog address",
|
|
585
|
+
proxySet: "Set provider-only proxy",
|
|
586
|
+
proxyDisable: "Disable provider-only proxy",
|
|
587
|
+
clear: "Clear",
|
|
588
|
+
baseUrl: "Base URL",
|
|
589
|
+
discoveryUrl: "Discovery URL (independent from Base URL)",
|
|
590
|
+
proxyUrl: "Provider proxy URL (for example http://127.0.0.1:7890; example only)",
|
|
591
|
+
authTitle: "Authentication method",
|
|
592
|
+
authLiteralOption: "API key",
|
|
593
|
+
authEnvOption: "Environment variable reference",
|
|
594
|
+
authReuseOption: "Use existing Pi authentication",
|
|
595
|
+
authTemporaryOption: "Temporary key (this catalog fetch only)",
|
|
596
|
+
authAnonymousOption: "No authentication required",
|
|
597
|
+
authModelsJson: "API key or environment variable reference",
|
|
598
|
+
authReuse: "Use existing Pi authentication",
|
|
599
|
+
authTemporary: "Temporary key (this catalog fetch only)",
|
|
600
|
+
authAnonymous: "No authentication required",
|
|
601
|
+
authSourceAnonymous: "anonymous",
|
|
602
|
+
authSourceStored: "stored credential",
|
|
603
|
+
secretApiKey: "Enter API key",
|
|
604
|
+
secretTemporary: "Enter temporary API key (not written to disk)",
|
|
605
|
+
envName: "Environment variable name (without $)",
|
|
606
|
+
discoveryAuthTitle: "Discovery auth header",
|
|
607
|
+
authHeaderAuto: "Auto (from protocol)",
|
|
608
|
+
authHeaderBearer: "Bearer Authorization",
|
|
609
|
+
authHeaderXApiKey: "x-api-key",
|
|
610
|
+
authHeaderApiKey: "api-key (Azure)",
|
|
611
|
+
authHeaderGoogle: "x-goog-api-key (Google)",
|
|
612
|
+
authHeaderAnthropic: "Anthropic x-api-key + version",
|
|
613
|
+
authHeaderNone: "Send no authentication header",
|
|
614
|
+
defaultTypeTitle: "Type to use when remote entries omit it",
|
|
615
|
+
defaultContextTitle: "Default context window",
|
|
616
|
+
defaultMaxTitle: "Default max tokens",
|
|
617
|
+
fetching: "Fetching models from {url}…",
|
|
618
|
+
fetchCancelled: "Remote fetch cancelled",
|
|
619
|
+
temporaryMustPersist: "The temporary key is only for catalog discovery. Choose persistent runtime authentication before saving.",
|
|
620
|
+
emptyTitle: "Confirm empty managed collection",
|
|
621
|
+
emptyBody: "This removes every entry managed by pi-model-config for this provider. Built-in catalogs and other providers are untouched.",
|
|
622
|
+
previewTitle: "Preview Changes",
|
|
623
|
+
previewProviderSettings: "Provider Settings: {provider}",
|
|
624
|
+
previewModelConfig: "Model Configuration: {provider}",
|
|
625
|
+
previewSettingsChanged: "Provider settings changed",
|
|
626
|
+
previewSettingsUnchanged: "Provider settings unchanged",
|
|
627
|
+
previewKeyUpdated: "API key: will be updated; content is hidden",
|
|
628
|
+
previewModelsAdded: "Add models: {items}",
|
|
629
|
+
previewModelsRemoved: "Remove models: {items}",
|
|
630
|
+
previewModelsModified: "Modify models: {items}",
|
|
631
|
+
previewModelsUnchanged: "Unchanged models: {count}",
|
|
632
|
+
saveSuccess: "Configuration written transactionally; {count} backup(s) created. Run /reload to apply provider transport and classifier/image registration.",
|
|
633
|
+
selectorTitle: "Configure Models: {provider}",
|
|
634
|
+
selectorSearchPrompt: "Search models: ",
|
|
635
|
+
selectorSearchPlaceholder: "Search model name or ID",
|
|
636
|
+
selectorHelp: "Tab/Shift+Tab focus · Enter check · Space types in search · Ctrl+T remote type · Ctrl+A/D bulk · Ctrl+S preview",
|
|
637
|
+
selectorSemantics: "Checked = keep or add after saving; unchecked = remove after saving. Fetching and checking do not save immediately.",
|
|
638
|
+
selectorNarrow: "Terminal too narrow; resize and retry, or press Esc to go back",
|
|
639
|
+
selectorNoMatches: " No matching models",
|
|
640
|
+
selectorReadonlyStatus: "This model is not managed by this plugin and cannot be changed or removed here",
|
|
641
|
+
selectorReadonlyExternal: "This model comes from models.json; use Manage existing models before changing or removing it",
|
|
642
|
+
selectorReadonlyRuntime: "This model comes from Pi's built-in catalog or another runtime extension and cannot be removed here",
|
|
643
|
+
selectorTypeConflict: "Cannot change to {type}: {identity} already exists",
|
|
644
|
+
selectorReadonlyTag: "read-only",
|
|
645
|
+
selectorTypeChat: "Chat",
|
|
646
|
+
selectorTypeClassifier: "Classification",
|
|
647
|
+
selectorTypeImage: "Image generation",
|
|
648
|
+
selectorFooter: "Visible {filtered} / Total {total} · Selected {selected} · Read-only {readonly}",
|
|
649
|
+
maskedLabel: "Key: ",
|
|
650
|
+
maskedHelp: "Enter save · Esc cancel · Ctrl+U clear · input is always masked",
|
|
651
|
+
diffConnection: "Connection: {before}\n→ {after}",
|
|
652
|
+
diffSafety: "Key values are hidden. Unchecked items remove only this plugin's managed collection; other providers and built-in catalogs are untouched.",
|
|
653
|
+
valueDefault: "default",
|
|
654
|
+
valueInvalidUrl: "[invalid URL]",
|
|
655
|
+
valueAnonymous: "no authentication required",
|
|
656
|
+
valueReuseAuth: "use existing Pi authentication",
|
|
657
|
+
valueUnset: "not configured",
|
|
658
|
+
valueProxyDisabled: "off",
|
|
659
|
+
valueEnv: "environment variable {value}",
|
|
660
|
+
valueLiteralMasked: "literal (masked)",
|
|
661
|
+
valueRedacted: "[redacted]",
|
|
662
|
+
errorExternal: "[{code}] External or system error (untrusted diagnostic text, display only): {detail}",
|
|
663
|
+
errorUnknownDetail: "details cannot be shown safely",
|
|
664
|
+
} satisfies Record<TranslationKey, string>;
|
|
665
|
+
|
|
666
|
+
const STRINGS: Record<UiLanguage, Record<TranslationKey, string>> = { "zh-CN": ZH, en: EN };
|
|
667
|
+
|
|
668
|
+
export function normalizeUiLanguage(value: unknown): UiLanguage {
|
|
669
|
+
return value === "en" ? "en" : "zh-CN";
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
export function t(language: UiLanguage, key: TranslationKey, params?: TranslationParams): string {
|
|
673
|
+
let message: string = STRINGS[language][key];
|
|
674
|
+
for (const [name, value] of Object.entries(params ?? {})) message = message.split(`{${name}}`).join(String(value));
|
|
675
|
+
return message;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
export type ErrorCode =
|
|
679
|
+
| "MCFG_E_CONFIG_SHAPE" | "MCFG_E_PROVIDER_SHAPE" | "MCFG_E_MODELS_ARRAY" | "MCFG_E_MODEL_SHAPE"
|
|
680
|
+
| "MCFG_E_AUTH_MODE" | "MCFG_E_PROVIDER_ID" | "MCFG_E_URL_INVALID" | "MCFG_E_URL_PROTOCOL"
|
|
681
|
+
| "MCFG_E_URL_CREDENTIALS" | "MCFG_E_URL_SECRET_QUERY" | "MCFG_E_POSITIVE_INTEGER"
|
|
682
|
+
| "MCFG_E_KEY_EMPTY" | "MCFG_E_KEY_MASKED" | "MCFG_E_ENV_NAME" | "MCFG_E_DISCOVERY_URL"
|
|
683
|
+
| "MCFG_E_ENV_MISSING" | "MCFG_E_PI_AUTH" | "MCFG_E_DISCOVERY_AUTH_HEADER" | "MCFG_E_DISCOVERY_AUTH_ORIGIN" | "MCFG_E_REDIRECT_LOCATION" | "MCFG_E_REDIRECT_ORIGIN"
|
|
684
|
+
| "MCFG_E_REDIRECT_LIMIT" | "MCFG_E_PAGINATION" | "MCFG_E_DISCOVERY_HTTP" | "MCFG_E_DISCOVERY_BODY_LIMIT" | "MCFG_E_DISCOVERY_JSON" | "MCFG_E_DISCOVERY_SHAPE"
|
|
685
|
+
| "MCFG_E_UNSUPPORTED_API" | "MCFG_E_TYPE_API" | "MCFG_E_NOT_OWNED" | "MCFG_E_BASE_URL_REQUIRED"
|
|
686
|
+
| "MCFG_E_EXTERNAL_TRANSPORT" | "MCFG_E_PROXY_UNSUPPORTED" | "MCFG_E_ANONYMOUS_UNSUPPORTED"
|
|
687
|
+
| "MCFG_E_AUTH_REQUIRED" | "MCFG_E_LOCK_ACTIVE" | "MCFG_E_LOCK_RECOVERY" | "MCFG_E_LOCK_CHANGED"
|
|
688
|
+
| "MCFG_E_LOCK_CLEANUP" | "MCFG_E_LOCK_RACE" | "MCFG_E_CONCURRENT_CHANGE" | "MCFG_E_STAGING_CHANGE"
|
|
689
|
+
| "MCFG_E_ROLLBACK_INCOMPLETE" | "MCFG_E_TRANSACTION_ROLLED_BACK" | "MCFG_E_TX_RECOVERED" | "MCFG_E_TX_RECOVERY_ACTIVE" | "MCFG_E_TX_RECOVERY_FAILED" | "MCFG_E_INTERNAL_CHAT_ONLY"
|
|
690
|
+
| "MCFG_E_OWNED_PROVIDER_MISSING" | "MCFG_E_NOT_BUILTIN" | "MCFG_E_DISABLE_THIRD_PARTY" | "MCFG_E_EXTERNAL";
|
|
691
|
+
|
|
692
|
+
const ERROR_ZH: Record<ErrorCode, string> = {
|
|
693
|
+
MCFG_E_CONFIG_SHAPE: "{file} 的版本或根结构无效",
|
|
694
|
+
MCFG_E_PROVIDER_SHAPE: "{file} 中服务商 {provider} 必须是对象",
|
|
695
|
+
MCFG_E_MODELS_ARRAY: "{file} 中服务商 {provider}.models 必须是对象数组",
|
|
696
|
+
MCFG_E_MODEL_SHAPE: "{file} 中服务商 {provider} 包含无效模型定义",
|
|
697
|
+
MCFG_E_AUTH_MODE: "{file} 中服务商 {provider}.authMode 无效",
|
|
698
|
+
MCFG_E_PROVIDER_ID: "服务商 ID 仅允许小写字母、数字、点、下划线和连字符",
|
|
699
|
+
MCFG_E_URL_INVALID: "{label} 不是有效 URL",
|
|
700
|
+
MCFG_E_URL_PROTOCOL: "{label} 仅支持 HTTP(S)",
|
|
701
|
+
MCFG_E_URL_CREDENTIALS: "{label} 不允许在 URL 中嵌入凭据",
|
|
702
|
+
MCFG_E_URL_SECRET_QUERY: "{label} 不允许在查询参数中嵌入密钥;请使用认证选项",
|
|
703
|
+
MCFG_E_POSITIVE_INTEGER: "请输入正整数",
|
|
704
|
+
MCFG_E_KEY_EMPTY: "API key 不能为空",
|
|
705
|
+
MCFG_E_KEY_MASKED: "掩码占位符不能作为 API key;请重新输入真实密钥或改用 $ENV",
|
|
706
|
+
MCFG_E_ENV_NAME: "环境变量名无效",
|
|
707
|
+
MCFG_E_DISCOVERY_URL: "请显式设置 Discovery URL;本插件不会假设 /models",
|
|
708
|
+
MCFG_E_ENV_MISSING: "环境变量 {name} 未设置",
|
|
709
|
+
MCFG_E_PI_AUTH: "该服务商没有可复用的 Pi auth",
|
|
710
|
+
MCFG_E_DISCOVERY_AUTH_HEADER: "{file} 中服务商 {provider}.discoveryAuthHeader 无效",
|
|
711
|
+
MCFG_E_DISCOVERY_AUTH_ORIGIN: "Discovery 与模型调用地址不同源;已拒绝转发 Pi 已有认证。请改用临时/显式密钥,或选择不发送认证头",
|
|
712
|
+
MCFG_E_REDIRECT_LOCATION: "Discovery 重定向 {status} 缺少 Location",
|
|
713
|
+
MCFG_E_REDIRECT_ORIGIN: "Discovery 拒绝跨域重定向,凭据未发送到目标域",
|
|
714
|
+
MCFG_E_REDIRECT_LIMIT: "Discovery 重定向次数超过 5 次",
|
|
715
|
+
MCFG_E_PAGINATION: "Discovery 分页暂不支持:{reason};未把部分结果当作完整目录",
|
|
716
|
+
MCFG_E_DISCOVERY_HTTP: "Discovery 请求失败:HTTP {status}{detail}",
|
|
717
|
+
MCFG_E_DISCOVERY_BODY_LIMIT: "Discovery 响应超过 {limit} 字节上限",
|
|
718
|
+
MCFG_E_DISCOVERY_JSON: "Discovery 响应不是有效 JSON",
|
|
719
|
+
MCFG_E_DISCOVERY_SHAPE: "Discovery 响应必须是数组或 { data: [] }",
|
|
720
|
+
MCFG_E_UNSUPPORTED_API: "{identity} 使用不支持的 API {api}",
|
|
721
|
+
MCFG_E_TYPE_API: "{identity} 的 API {api} 与模型类型不匹配",
|
|
722
|
+
MCFG_E_NOT_OWNED: "服务商 {provider} 不由本插件管理,不能替换其扩展模型目录",
|
|
723
|
+
MCFG_E_BASE_URL_REQUIRED: "插件自有服务商的每个模型必须有 model Base URL 或服务商 Base URL",
|
|
724
|
+
MCFG_E_EXTERNAL_TRANSPORT: "未接管的第三方 extension 服务商无法保证 stream 与 streamSimple 都经过请求级 transport;请移除代理/匿名设置,或仅对自定义服务商明确启用插件自有",
|
|
725
|
+
MCFG_E_PROXY_UNSUPPORTED: "{identity} 不支持 request-local 代理;已阻止启用代理",
|
|
726
|
+
MCFG_E_ANONYMOUS_UNSUPPORTED: "{identity} 无法安全匿名调用",
|
|
727
|
+
MCFG_E_AUTH_REQUIRED: "models.json 认证缺少密钥或 $ENV 引用",
|
|
728
|
+
MCFG_E_LOCK_ACTIVE: "检测到活动或尚未确认失效的并发配置锁({path})",
|
|
729
|
+
MCFG_E_LOCK_RECOVERY: "另一个进程正在复核失效配置锁;本次未写入",
|
|
730
|
+
MCFG_E_LOCK_CHANGED: "配置锁在复核期间已更新或其 owner 仍存活;本次未写入",
|
|
731
|
+
MCFG_E_LOCK_CLEANUP: "检测到失效锁但无法安全清理:{reason}",
|
|
732
|
+
MCFG_E_LOCK_RACE: "清理失效锁后有其他进程先取得锁;本次未写入",
|
|
733
|
+
MCFG_E_CONCURRENT_CHANGE: "配置文件已被其他进程修改;未写入任何内容,请重新打开 /model-config",
|
|
734
|
+
MCFG_E_STAGING_CHANGE: "配置文件在 staging 期间被其他进程修改;未提交临时文件",
|
|
735
|
+
MCFG_E_ROLLBACK_INCOMPLETE: "事务写入失败,且回滚不完整:{errors}",
|
|
736
|
+
MCFG_E_TRANSACTION_ROLLED_BACK: "事务写入失败,已回滚两个配置文件",
|
|
737
|
+
MCFG_E_TX_RECOVERED: "检测到中断事务 {txId},已恢复两份配置的旧版本",
|
|
738
|
+
MCFG_E_TX_RECOVERY_ACTIVE: "检测到中断事务 journal({path}),但配置锁仍活动或未确认失效;未恢复,请稍后重试",
|
|
739
|
+
MCFG_E_TX_RECOVERY_FAILED: "检测到中断事务 journal({path})但恢复失败:{reason}。journal 已保留;请按其中记录的备份路径手动恢复两份配置到同一旧版本",
|
|
740
|
+
MCFG_E_INTERNAL_CHAT_ONLY: "内部错误:非 chat 模型不能写入 models.json",
|
|
741
|
+
MCFG_E_OWNED_PROVIDER_MISSING: "自有服务商 {provider} 缺少 models.json 配置",
|
|
742
|
+
MCFG_E_NOT_BUILTIN: "{provider} 不是内置服务商;未接管的外部服务商无法保证完整请求都经过代理",
|
|
743
|
+
MCFG_E_DISABLE_THIRD_PARTY: "{provider} 是未接管的第三方 runtime extension 服务商,不能通过同名注册安全禁用",
|
|
744
|
+
MCFG_E_EXTERNAL: "外部或系统错误",
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
const ERROR_EN: Record<ErrorCode, string> = {
|
|
748
|
+
MCFG_E_CONFIG_SHAPE: "{file} has an invalid version or root structure",
|
|
749
|
+
MCFG_E_PROVIDER_SHAPE: "provider {provider} in {file} must be an object",
|
|
750
|
+
MCFG_E_MODELS_ARRAY: "provider {provider}.models in {file} must be an array of objects",
|
|
751
|
+
MCFG_E_MODEL_SHAPE: "provider {provider} in {file} contains an invalid model definition",
|
|
752
|
+
MCFG_E_AUTH_MODE: "provider {provider}.authMode in {file} is invalid",
|
|
753
|
+
MCFG_E_PROVIDER_ID: "Provider ID may contain only lowercase letters, digits, dots, underscores, and hyphens",
|
|
754
|
+
MCFG_E_URL_INVALID: "{label} is not a valid URL",
|
|
755
|
+
MCFG_E_URL_PROTOCOL: "{label} supports HTTP(S) only",
|
|
756
|
+
MCFG_E_URL_CREDENTIALS: "{label} must not embed credentials in the URL",
|
|
757
|
+
MCFG_E_URL_SECRET_QUERY: "{label} must not embed a key in query parameters; use an authentication option",
|
|
758
|
+
MCFG_E_POSITIVE_INTEGER: "Enter a positive integer",
|
|
759
|
+
MCFG_E_KEY_EMPTY: "API key must not be empty",
|
|
760
|
+
MCFG_E_KEY_MASKED: "A masked placeholder cannot be saved as an API key; enter the real key or use $ENV",
|
|
761
|
+
MCFG_E_ENV_NAME: "Invalid environment variable name",
|
|
762
|
+
MCFG_E_DISCOVERY_URL: "Set an explicit Discovery URL; the plugin does not assume /models",
|
|
763
|
+
MCFG_E_ENV_MISSING: "Environment variable {name} is not set",
|
|
764
|
+
MCFG_E_PI_AUTH: "This provider has no reusable Pi auth",
|
|
765
|
+
MCFG_E_DISCOVERY_AUTH_HEADER: "provider {provider}.discoveryAuthHeader in {file} is invalid",
|
|
766
|
+
MCFG_E_DISCOVERY_AUTH_ORIGIN: "Discovery and model API origins differ; existing Pi authentication was not forwarded. Use a temporary/explicit key or choose no authentication header",
|
|
767
|
+
MCFG_E_REDIRECT_LOCATION: "Discovery redirect {status} has no Location header",
|
|
768
|
+
MCFG_E_REDIRECT_ORIGIN: "Discovery rejected a cross-origin redirect; credentials were not sent to the target origin",
|
|
769
|
+
MCFG_E_REDIRECT_LIMIT: "Discovery exceeded 5 redirects",
|
|
770
|
+
MCFG_E_PAGINATION: "Discovery pagination is unsupported: {reason}; a partial catalog was not accepted as complete",
|
|
771
|
+
MCFG_E_DISCOVERY_HTTP: "Discovery request failed with HTTP {status}{detail}",
|
|
772
|
+
MCFG_E_DISCOVERY_BODY_LIMIT: "Discovery response exceeds the {limit}-byte limit",
|
|
773
|
+
MCFG_E_DISCOVERY_JSON: "Discovery response is not valid JSON",
|
|
774
|
+
MCFG_E_DISCOVERY_SHAPE: "Discovery response must be an array or { data: [] }",
|
|
775
|
+
MCFG_E_UNSUPPORTED_API: "{identity} uses unsupported API {api}",
|
|
776
|
+
MCFG_E_TYPE_API: "{identity} uses API {api}, which does not match the model type",
|
|
777
|
+
MCFG_E_NOT_OWNED: "Provider {provider} is not plugin-owned, so its extension model catalog cannot be replaced",
|
|
778
|
+
MCFG_E_BASE_URL_REQUIRED: "Every model in a plugin-owned provider needs a model Base URL or provider Base URL",
|
|
779
|
+
MCFG_E_EXTERNAL_TRANSPORT: "A third-party extension provider that is not owned by this plugin cannot guarantee request-local transport for both stream and streamSimple; remove proxy/anonymous settings or explicitly own a custom provider",
|
|
780
|
+
MCFG_E_PROXY_UNSUPPORTED: "{identity} does not support request-local proxying; proxy activation was blocked",
|
|
781
|
+
MCFG_E_ANONYMOUS_UNSUPPORTED: "{identity} cannot be called anonymously without leaking synthetic authentication",
|
|
782
|
+
MCFG_E_AUTH_REQUIRED: "models.json authentication needs a key or $ENV reference",
|
|
783
|
+
MCFG_E_LOCK_ACTIVE: "An active or not-yet-proven-stale configuration lock exists ({path})",
|
|
784
|
+
MCFG_E_LOCK_RECOVERY: "Another process is checking a stale configuration lock; nothing was written",
|
|
785
|
+
MCFG_E_LOCK_CHANGED: "The configuration lock changed during recheck or its owner is still alive; nothing was written",
|
|
786
|
+
MCFG_E_LOCK_CLEANUP: "A stale lock was detected but could not be cleaned safely: {reason}",
|
|
787
|
+
MCFG_E_LOCK_RACE: "Another process acquired the lock after stale-lock cleanup; nothing was written",
|
|
788
|
+
MCFG_E_CONCURRENT_CHANGE: "Configuration files changed in another process; nothing was written. Reopen /model-config",
|
|
789
|
+
MCFG_E_STAGING_CHANGE: "Configuration files changed while staging; temporary files were not committed",
|
|
790
|
+
MCFG_E_ROLLBACK_INCOMPLETE: "Transaction failed and rollback was incomplete: {errors}",
|
|
791
|
+
MCFG_E_TRANSACTION_ROLLED_BACK: "Transaction failed; both configuration files were rolled back",
|
|
792
|
+
MCFG_E_TX_RECOVERED: "An interrupted transaction {txId} was detected; both configuration files were restored to their old generation",
|
|
793
|
+
MCFG_E_TX_RECOVERY_ACTIVE: "An interrupted transaction journal exists at {path}, but its configuration lock is active or not proven stale; recovery did not run. Retry later",
|
|
794
|
+
MCFG_E_TX_RECOVERY_FAILED: "An interrupted transaction journal at {path} could not be recovered: {reason}. The journal was kept; restore both files to the same old generation using its recorded backup paths",
|
|
795
|
+
MCFG_E_INTERNAL_CHAT_ONLY: "Internal error: a non-chat model cannot be written to models.json",
|
|
796
|
+
MCFG_E_OWNED_PROVIDER_MISSING: "Plugin-owned provider {provider} has no models.json configuration",
|
|
797
|
+
MCFG_E_NOT_BUILTIN: "{provider} is not built in; a complete proxy wrapper cannot be guaranteed for an unowned external provider",
|
|
798
|
+
MCFG_E_DISABLE_THIRD_PARTY: "{provider} is an unowned third-party runtime extension provider and cannot be safely disabled by same-name registration",
|
|
799
|
+
MCFG_E_EXTERNAL: "External or system error",
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
export class ModelConfigError extends Error {
|
|
803
|
+
constructor(public readonly code: ErrorCode, public readonly params: TranslationParams = {}, options?: ErrorOptions) {
|
|
804
|
+
super(code, options);
|
|
805
|
+
this.name = "ModelConfigError";
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export function mcError(code: ErrorCode, params: TranslationParams = {}, cause?: unknown): ModelConfigError {
|
|
810
|
+
return new ModelConfigError(code, params, cause === undefined ? undefined : { cause });
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
export function formatError(language: UiLanguage, error: unknown, safeExternalDetail?: string): string {
|
|
814
|
+
if (error instanceof ModelConfigError) {
|
|
815
|
+
const template = language === "zh-CN" ? ERROR_ZH[error.code] : ERROR_EN[error.code];
|
|
816
|
+
let message = template;
|
|
817
|
+
for (const [name, value] of Object.entries(error.params)) message = message.split(`{${name}}`).join(String(value));
|
|
818
|
+
return `[${error.code}] ${message}`;
|
|
819
|
+
}
|
|
820
|
+
return t(language, "errorExternal", {
|
|
821
|
+
code: "MCFG_E_EXTERNAL",
|
|
822
|
+
detail: safeExternalDetail?.trim() || t(language, "errorUnknownDetail"),
|
|
823
|
+
});
|
|
824
|
+
}
|