dsh-strata-sync 0.0.0-stage → 0.9.0
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 +185 -2
- package/cordis.patch.yml +3 -0
- package/dist/client.js +579 -0
- package/dist/core.mjs +285 -0
- package/dist/index.js +550 -0
- package/dist/summary.mjs +110 -0
- package/docs/CAPABILITIES.md +47 -0
- package/docs/README.zh-CN.md +156 -0
- package/docs/README.zh-TW.md +156 -0
- package/docs/VALIDATION.md +129 -0
- package/package.json +73 -4
package/dist/client.js
ADDED
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
/* Browser entry: DSH's own module loader provides React and services. */
|
|
2
|
+
window.__ModuleLoader__.load({
|
|
3
|
+
id: 'dsh-strata-sync',
|
|
4
|
+
factory: (require) => {
|
|
5
|
+
const React = require('react');
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
const translations = {
|
|
8
|
+
"Output limit source: ": {"zh-Hant":"輸出上限來源:","zh-Hans":"输出上限来源:"},
|
|
9
|
+
"Strata Server default": {"zh-Hant":"Strata Server 預設值","zh-Hans":"Strata Server 默认值"},
|
|
10
|
+
"Manual override": {"zh-Hant":"手動覆寫","zh-Hans":"手动覆盖"},
|
|
11
|
+
"Automatic calculation": {"zh-Hant":"自動計算","zh-Hans":"自动计算"},
|
|
12
|
+
"Server has no fixed output limit.": {"zh-Hant":"伺服器未設定固定輸出上限。","zh-Hans":"服务器未设置固定输出上限。"},
|
|
13
|
+
"Server output limit could not be verified.": {"zh-Hant":"無法確認伺服器的輸出上限。","zh-Hans":"无法确认服务器的输出上限。"},
|
|
14
|
+
"Leave blank to use the server output default; if absent or unlimited, use 30% of context, up to 16,384. An explicit override takes priority.": {"zh-Hant":"留空會採用伺服器輸出預設值;未提供或無固定上限時,使用上下文的 30%,最多 16,384。明確填寫的覆寫值優先。","zh-Hans":"留空采用服务器输出默认值;未提供或无固定上限时,使用上下文的 30%,最多 16,384。明确填写的覆盖值优先。"},
|
|
15
|
+
"Strata returned an invalid output limit. Review the server setting before syncing.": {"zh-Hant":"Strata 回傳無效的輸出上限,請先檢查伺服器設定。","zh-Hans":"Strata 返回无效的输出上限,请先检查服务器设置。"},
|
|
16
|
+
"Runs once on the next DSH start. Waits for running tasks to finish; no periodic server checks.": {
|
|
17
|
+
"zh-Hant": "下次啟動 DSH 時同步一次;若有工作執行中,會等到閒置,不再定期檢查伺服器。",
|
|
18
|
+
"zh-Hans": "下次启动 DSH 时同步一次;如有任务运行中,会等待空闲,不再定期检查服务器。"
|
|
19
|
+
},
|
|
20
|
+
"Image": {"zh-Hant":"圖片","zh-Hans":"图片"},
|
|
21
|
+
"View mapping": {"zh-Hant":"查看參數對應","zh-Hans":"查看参数映射"},
|
|
22
|
+
|
|
23
|
+
"Existing request mapping": { "zh-Hant": "既有請求對應", "zh-Hans": "已有请求映射" },
|
|
24
|
+
|
|
25
|
+
"Input types": {
|
|
26
|
+
"zh-Hant": "輸入類型",
|
|
27
|
+
"zh-Hans": "输入类型"
|
|
28
|
+
},
|
|
29
|
+
"Reasoning levels": {
|
|
30
|
+
"zh-Hant": "推理等級",
|
|
31
|
+
"zh-Hans": "推理等级"
|
|
32
|
+
},
|
|
33
|
+
"Reasoning request mapping": {
|
|
34
|
+
"zh-Hant": "推理請求對應",
|
|
35
|
+
"zh-Hans": "推理请求映射"
|
|
36
|
+
},
|
|
37
|
+
"Unconfirmed — keep current setting": {
|
|
38
|
+
"zh-Hant": "無法確認 — 保留原設定",
|
|
39
|
+
"zh-Hans": "无法确认 — 保留原设置"
|
|
40
|
+
},
|
|
41
|
+
"Text": {
|
|
42
|
+
"zh-Hant": "文字",
|
|
43
|
+
"zh-Hans": "文本"
|
|
44
|
+
},
|
|
45
|
+
"Text + image": {
|
|
46
|
+
"zh-Hant": "文字+圖片",
|
|
47
|
+
"zh-Hans": "文本+图片"
|
|
48
|
+
},
|
|
49
|
+
"Input types are missing or unsupported in server metadata. Existing input settings are preserved.": {
|
|
50
|
+
"zh-Hant": "伺服器未提供可確認的輸入類型,保留原輸入設定。",
|
|
51
|
+
"zh-Hans": "服务器未提供可确认的输入类型,保留原输入设置。"
|
|
52
|
+
},
|
|
53
|
+
"Reasoning support could not be verified against a reviewed Strata chat template. Existing reasoning settings are preserved.": {
|
|
54
|
+
"zh-Hant": "無法以已核對的 Strata 對話範本確認推理選項,保留原推理設定。",
|
|
55
|
+
"zh-Hans": "无法用已核对的 Strata 对话模板确认推理选项,保留原推理设置。"
|
|
56
|
+
},
|
|
57
|
+
"The provider default reasoning level is not supported by the verified template. Reasoning settings are preserved; resolve the default in Models first.": {
|
|
58
|
+
"zh-Hant": "供應者預設推理等級不在已驗證範本支援範圍,保留原推理設定;請先至模型設定調整預設值。",
|
|
59
|
+
"zh-Hans": "提供方默认推理等级不在已验证模板支持范围,保留原推理设置;请先到模型设置调整默认值。"
|
|
60
|
+
},
|
|
61
|
+
"Verified template: ": {
|
|
62
|
+
"zh-Hant": "已驗證範本:",
|
|
63
|
+
"zh-Hans": "已验证模板:"
|
|
64
|
+
},
|
|
65
|
+
"Only verified capabilities are synchronized. Unknown fields keep their existing values. Reasoning choices do not change the selected effort.": {
|
|
66
|
+
"zh-Hant": "只同步已確認的能力;未知欄位保留原值。更新推理選項不會改動目前選擇的等級。",
|
|
67
|
+
"zh-Hans": "只同步已确认的能力;未知字段保留原值。更新推理选项不会改动当前选择的等级。"
|
|
68
|
+
}
|
|
69
|
+
,
|
|
70
|
+
"Context window": {
|
|
71
|
+
"zh-Hant": "上下文視窗",
|
|
72
|
+
"zh-Hans": "上下文窗口"
|
|
73
|
+
},
|
|
74
|
+
"Response output limit": {
|
|
75
|
+
"zh-Hant": "回覆輸出上限",
|
|
76
|
+
"zh-Hans": "回复输出上限"
|
|
77
|
+
},
|
|
78
|
+
"Compaction headroom": {
|
|
79
|
+
"zh-Hant": "壓縮餘裕",
|
|
80
|
+
"zh-Hans": "压缩余量"
|
|
81
|
+
},
|
|
82
|
+
"Summary output limit": {
|
|
83
|
+
"zh-Hant": "摘要輸出上限",
|
|
84
|
+
"zh-Hans": "摘要输出上限"
|
|
85
|
+
},
|
|
86
|
+
"Dynamic summary target": {
|
|
87
|
+
"zh-Hant": "動態摘要目標",
|
|
88
|
+
"zh-Hans": "动态摘要目标"
|
|
89
|
+
},
|
|
90
|
+
"How the target is calculated": {
|
|
91
|
+
"zh-Hant": "目標如何計算",
|
|
92
|
+
"zh-Hans": "目标如何计算"
|
|
93
|
+
},
|
|
94
|
+
"When enabled, before each supported Strata summary, calculates a length target and appends one sentence to DSH's native summary prompt.": {
|
|
95
|
+
"zh-Hant": "啟用後,在每次支援的 Strata 摘要請求前,計算長度目標,並在 DSH 原生摘要提示詞後補充一句。",
|
|
96
|
+
"zh-Hans": "启用后,在每次支持的 Strata 摘要请求前,计算长度目标,并在 DSH 原生摘要提示词后补充一句。"
|
|
97
|
+
},
|
|
98
|
+
"Target = floor(min(context / N, summary output limit / 2, selected content estimate / 2)).": {
|
|
99
|
+
"zh-Hant": "目標 = floor(min(上下文 / N, 摘要輸出上限 / 2, 待壓縮內容估算 / 2))。",
|
|
100
|
+
"zh-Hans": "目标 = floor(min(上下文 / N, 摘要输出上限 / 2, 待压缩内容估算 / 2))。"
|
|
101
|
+
},
|
|
102
|
+
"Enable summary guidance": {
|
|
103
|
+
"zh-Hant": "啟用摘要提示補充",
|
|
104
|
+
"zh-Hans": "启用摘要提示补充"
|
|
105
|
+
},
|
|
106
|
+
"Enabled by default. Turning this off keeps DSH's native summary and automatic compaction behavior.": {
|
|
107
|
+
"zh-Hant": "預設開啟。關閉後保留 DSH 原生摘要與自動壓縮行為。",
|
|
108
|
+
"zh-Hans": "默认开启。关闭后保留 DSH 原生摘要与自动压缩行为。"
|
|
109
|
+
},
|
|
110
|
+
"Context divisor (context ÷ N)": {
|
|
111
|
+
"zh-Hant": "上下文除數(上下文 ÷ N)",
|
|
112
|
+
"zh-Hans": "上下文除数(上下文 ÷ N)"
|
|
113
|
+
},
|
|
114
|
+
"Default: 16. A smaller divisor allows a larger target, still bounded by half the summary output limit and half the selected content estimate.": {
|
|
115
|
+
"zh-Hant": "預設為 16。除數越小,允許的目標越大,但仍受摘要輸出上限的一半與待壓縮內容估算的一半限制。",
|
|
116
|
+
"zh-Hans": "默认为 16。除数越小,允许的目标越大,但仍受摘要输出上限的一半与待压缩内容估算的一半限制。"
|
|
117
|
+
},
|
|
118
|
+
"Enter a positive whole-number divisor.": {
|
|
119
|
+
"zh-Hant": "請輸入正整數除數。",
|
|
120
|
+
"zh-Hans": "请输入正整数除数。"
|
|
121
|
+
},
|
|
122
|
+
"This preview is below the 256-token minimum. The native summary prompt remains unchanged.": {
|
|
123
|
+
"zh-Hant": "本次預覽低於 256 tokens 的最低目標,將保留原生摘要提示詞。",
|
|
124
|
+
"zh-Hans": "本次预览低于 256 tokens 的最低目标,将保留原生摘要提示词。"
|
|
125
|
+
},
|
|
126
|
+
"Summary guidance is off. No extra sentence is added to the native prompt.": {
|
|
127
|
+
"zh-Hant": "摘要提示補充已關閉,不會在原生提示詞中加上額外句子。",
|
|
128
|
+
"zh-Hans": "摘要提示补充已关闭,不会在原生提示词中添加额外句子。"
|
|
129
|
+
},
|
|
130
|
+
"Save preferences to apply these controls to subsequent summary requests; other model and compaction settings stay unchanged.": {
|
|
131
|
+
"zh-Hant": "儲存偏好後,這些控制項會套用至後續摘要請求;其他模型與壓縮設定保持不變。",
|
|
132
|
+
"zh-Hans": "保存偏好后,这些控制项将应用于后续摘要请求;其他模型与压缩设置保持不变。"
|
|
133
|
+
},
|
|
134
|
+
"Context uses the smaller of the verified Strata window and the existing DSH model window. The preview ceiling assumes the proposed budgets; existing conversation settings can make the actual target lower.": {
|
|
135
|
+
"zh-Hant": "上下文採用已確認的 Strata 視窗與 DSH 既有模型視窗的較小值。預覽上限依提議預算計算,既有對話設定可能讓實際目標更低。",
|
|
136
|
+
"zh-Hans": "上下文采用已确认的 Strata 窗口与 DSH 已有模型窗口的较小值。预览上限按提议预算计算,已有对话设置可能让实际目标更低。"
|
|
137
|
+
},
|
|
138
|
+
"The original prompt, output cap and other settings stay unchanged. This is a writing target, not a guaranteed length or an optimal value.": {
|
|
139
|
+
"zh-Hant": "保留原生提示詞、輸出硬上限與其他設定。這是寫作目標,不保證實際長度,也不代表最佳值。",
|
|
140
|
+
"zh-Hans": "保留原生提示词、输出硬上限与其他设置。这是写作目标,不保证实际长度,也不代表最佳值。"
|
|
141
|
+
},
|
|
142
|
+
"Maximum target for this preview: ": {
|
|
143
|
+
"zh-Hant": "本次預覽的目標上限:",
|
|
144
|
+
"zh-Hans": "本次预览的目标上限:"
|
|
145
|
+
},
|
|
146
|
+
"The actual target may be lower for shorter content. System prompts and tool schemas are excluded from the content estimate.": {
|
|
147
|
+
"zh-Hant": "內容較短時,實際目標會更低;內容估算不包含系統提示詞與工具定義。",
|
|
148
|
+
"zh-Hans": "内容较短时,实际目标会更低;内容估算不包含系统提示词与工具定义。"
|
|
149
|
+
},
|
|
150
|
+
"If the route or native summary format cannot be verified, DSH uses its original prompt.": {
|
|
151
|
+
"zh-Hant": "無法確認路由或原生摘要格式時,DSH 沿用原本提示詞。",
|
|
152
|
+
"zh-Hans": "无法确认路由或原生摘要格式时,DSH 沿用原本提示词。"
|
|
153
|
+
},
|
|
154
|
+
"Unpriced image/file attachments, cancellation or a disabled plugin preserve native request handling.": {
|
|
155
|
+
"zh-Hant": "無法估算圖片/檔案附件用量、請求取消或外掛停用時,保留原生請求處理。",
|
|
156
|
+
"zh-Hans": "无法估算图片/文件附件用量、请求取消或插件禁用时,保留原生请求处理。"
|
|
157
|
+
},
|
|
158
|
+
"Targets below 256 tokens leave the original prompt unchanged; the native summary still runs.": {
|
|
159
|
+
"zh-Hant": "目標小於 256 tokens 時保留原本提示詞,原生摘要仍會執行。",
|
|
160
|
+
"zh-Hans": "目标小于 256 tokens 时保留原本提示词,原生摘要仍会执行。"
|
|
161
|
+
},
|
|
162
|
+
"Compaction threshold": {
|
|
163
|
+
"zh-Hant": "壓縮門檻",
|
|
164
|
+
"zh-Hans": "压缩阈值"
|
|
165
|
+
},
|
|
166
|
+
"Recent conversation retention": {
|
|
167
|
+
"zh-Hant": "近期對話保留量",
|
|
168
|
+
"zh-Hans": "近期对话保留量"
|
|
169
|
+
},
|
|
170
|
+
"Model": {
|
|
171
|
+
"zh-Hant": "模型",
|
|
172
|
+
"zh-Hans": "模型"
|
|
173
|
+
},
|
|
174
|
+
"Default model": {
|
|
175
|
+
"zh-Hant": "預設模型",
|
|
176
|
+
"zh-Hans": "默认模型"
|
|
177
|
+
},
|
|
178
|
+
"Compaction ratio ceiling": {
|
|
179
|
+
"zh-Hant": "壓縮比例上限",
|
|
180
|
+
"zh-Hans": "压缩比例上限"
|
|
181
|
+
},
|
|
182
|
+
"Recent conversation retention ratio": {
|
|
183
|
+
"zh-Hant": "近期對話保留比例",
|
|
184
|
+
"zh-Hans": "近期对话保留比例"
|
|
185
|
+
},
|
|
186
|
+
"Not set": {
|
|
187
|
+
"zh-Hant": "未設定",
|
|
188
|
+
"zh-Hans": "未设置"
|
|
189
|
+
},
|
|
190
|
+
"Automatic compaction is disabled in an affected Agent preset. Sync preserves that choice.": {
|
|
191
|
+
"zh-Hant": "受影響的 Agent 範本已停用自動壓縮,同步會保留此選擇。",
|
|
192
|
+
"zh-Hans": "受影响的 Agent 预设已停用自动压缩,同步会保留此选择。"
|
|
193
|
+
},
|
|
194
|
+
"Live slots do not report a context size. Using the model context reported by Strata.": {
|
|
195
|
+
"zh-Hant": "執行中的 slot 未提供上下文大小,將使用 Strata 模型資訊。",
|
|
196
|
+
"zh-Hans": "运行中的 slot 未提供上下文大小,将使用 Strata 模型信息。"
|
|
197
|
+
},
|
|
198
|
+
"The active engine context differs from the model metadata. Using the active engine context.": {
|
|
199
|
+
"zh-Hant": "引擎的實際上下文與模型資訊不同,將採用引擎的實際值。",
|
|
200
|
+
"zh-Hans": "引擎的实际上下文与模型信息不同,将采用引擎的实际值。"
|
|
201
|
+
},
|
|
202
|
+
"Live slot information is unavailable. Using the model context reported by Strata.": {
|
|
203
|
+
"zh-Hant": "無法取得執行中的 slot 資訊,將使用 Strata 模型資訊。",
|
|
204
|
+
"zh-Hans": "无法获取运行中的 slot 信息,将使用 Strata 模型信息。"
|
|
205
|
+
},
|
|
206
|
+
"The operation failed.": {
|
|
207
|
+
"zh-Hant": "操作失敗。",
|
|
208
|
+
"zh-Hans": "操作失败。"
|
|
209
|
+
},
|
|
210
|
+
"Align DSH with the model loaded by Strata Server. The server supplies model, context and any fixed output default; this plugin calculates fallback output and compaction budgets.": {
|
|
211
|
+
"zh-Hant": "讓 DSH 對齊 Strata Server 已載入的模型。模型、上下文及固定輸出預設值由伺服器提供;此外掛計算備用輸出與壓縮預算。",
|
|
212
|
+
"zh-Hans": "让 DSH 对齐 Strata Server 已加载的模型。模型、上下文及固定输出默认值由服务器提供;此插件计算备用输出与压缩预算。"
|
|
213
|
+
},
|
|
214
|
+
"Loading sync settings...": {
|
|
215
|
+
"zh-Hant": "正在載入同步設定…",
|
|
216
|
+
"zh-Hans": "正在加载同步设置…"
|
|
217
|
+
},
|
|
218
|
+
"DSH is running a task. Synchronization will wait until all tasks are idle.": {
|
|
219
|
+
"zh-Hant": "DSH 正在執行工作,同步會等到所有工作閒置後進行。",
|
|
220
|
+
"zh-Hans": "DSH 正在执行任务,同步会等到所有任务空闲后进行。"
|
|
221
|
+
},
|
|
222
|
+
"Strata provider": {
|
|
223
|
+
"zh-Hant": "Strata 供應者",
|
|
224
|
+
"zh-Hans": "Strata 提供方"
|
|
225
|
+
},
|
|
226
|
+
"Choose an existing provider": {
|
|
227
|
+
"zh-Hant": "選擇已設定的供應者",
|
|
228
|
+
"zh-Hans": "选择已设置的提供方"
|
|
229
|
+
},
|
|
230
|
+
"Output limit override (blank to follow Strata)": {
|
|
231
|
+
"zh-Hant": "輸出上限覆寫(留空跟隨 Strata)",
|
|
232
|
+
"zh-Hans": "输出上限覆盖(留空跟随 Strata)"
|
|
233
|
+
},
|
|
234
|
+
"Strata default, otherwise automatic": {
|
|
235
|
+
"zh-Hant": "Strata 預設值,否則自動計算",
|
|
236
|
+
"zh-Hans": "Strata 默认值,否则自动计算"
|
|
237
|
+
},
|
|
238
|
+
"Uses the URL and credentials already configured in DSH. API keys are never displayed or copied here.": {
|
|
239
|
+
"zh-Hant": "使用 DSH 現有的網址與認證,不在此顯示或複製 API key。",
|
|
240
|
+
"zh-Hans": "使用 DSH 现有的网址与认证,不在此显示或复制 API key。"
|
|
241
|
+
},
|
|
242
|
+
"Follow the loaded Strata model as the default": {
|
|
243
|
+
"zh-Hant": "預設模型跟隨 Strata 已載入的模型",
|
|
244
|
+
"zh-Hans": "默认模型跟随 Strata 已加载的模型"
|
|
245
|
+
},
|
|
246
|
+
"Automatically sync when DSH starts": {
|
|
247
|
+
"zh-Hant": "啟動 DSH 時自動同步",
|
|
248
|
+
"zh-Hans": "启动 DSH 时自动同步"
|
|
249
|
+
},
|
|
250
|
+
"Sync preferences saved.": {
|
|
251
|
+
"zh-Hant": "同步偏好已儲存。",
|
|
252
|
+
"zh-Hans": "同步偏好已保存。"
|
|
253
|
+
},
|
|
254
|
+
"Save preferences": {
|
|
255
|
+
"zh-Hant": "儲存偏好",
|
|
256
|
+
"zh-Hans": "保存偏好"
|
|
257
|
+
},
|
|
258
|
+
"Working...": {
|
|
259
|
+
"zh-Hant": "處理中…",
|
|
260
|
+
"zh-Hans": "处理中…"
|
|
261
|
+
},
|
|
262
|
+
"Check differences": {
|
|
263
|
+
"zh-Hant": "檢查差異",
|
|
264
|
+
"zh-Hans": "检查差异"
|
|
265
|
+
},
|
|
266
|
+
"Synced and backed up the original settings. Start a new conversation to use the updated compaction policy.": {
|
|
267
|
+
"zh-Hant": "已同步並備份原設定。請開新對話使用更新後的壓縮政策。",
|
|
268
|
+
"zh-Hans": "已同步并备份原设置。请开启新对话使用更新后的压缩策略。"
|
|
269
|
+
},
|
|
270
|
+
"Settings already match. No changes needed.": {
|
|
271
|
+
"zh-Hant": "設定已一致,無須變更。",
|
|
272
|
+
"zh-Hans": "设置已一致,无需更改。"
|
|
273
|
+
},
|
|
274
|
+
"Back up and sync": {
|
|
275
|
+
"zh-Hant": "備份並同步",
|
|
276
|
+
"zh-Hans": "备份并同步"
|
|
277
|
+
},
|
|
278
|
+
"Save preferences before checking for differences.": {
|
|
279
|
+
"zh-Hant": "請先儲存偏好,再檢查差異。",
|
|
280
|
+
"zh-Hans": "请先保存偏好,再检查差异。"
|
|
281
|
+
},
|
|
282
|
+
"Context tokens": {
|
|
283
|
+
"zh-Hant": "上下文 tokens",
|
|
284
|
+
"zh-Hans": "上下文 tokens"
|
|
285
|
+
},
|
|
286
|
+
"Context source: ": {
|
|
287
|
+
"zh-Hant": "上下文來源:",
|
|
288
|
+
"zh-Hans": "上下文来源:"
|
|
289
|
+
},
|
|
290
|
+
"Server model metadata": {
|
|
291
|
+
"zh-Hant": "伺服器模型資訊",
|
|
292
|
+
"zh-Hans": "服务器模型信息"
|
|
293
|
+
},
|
|
294
|
+
"Affected presets: ": {
|
|
295
|
+
"zh-Hant": "受影響的範本:",
|
|
296
|
+
"zh-Hans": "受影响的预设:"
|
|
297
|
+
},
|
|
298
|
+
". Existing conversations retain their preset revision. Start a new conversation to use the updated compaction budget.": {
|
|
299
|
+
"zh-Hant": "。既有對話保留原範本版本,請開新對話套用更新的壓縮預算。",
|
|
300
|
+
"zh-Hans": "。已有对话保留原预设版本,请开启新对话应用更新的压缩预算。"
|
|
301
|
+
},
|
|
302
|
+
"Setting": {
|
|
303
|
+
"zh-Hant": "設定",
|
|
304
|
+
"zh-Hans": "设置"
|
|
305
|
+
},
|
|
306
|
+
"Current": {
|
|
307
|
+
"zh-Hant": "目前",
|
|
308
|
+
"zh-Hans": "当前"
|
|
309
|
+
},
|
|
310
|
+
"After sync": {
|
|
311
|
+
"zh-Hant": "同步後",
|
|
312
|
+
"zh-Hans": "同步后"
|
|
313
|
+
},
|
|
314
|
+
"Settings are aligned.": {
|
|
315
|
+
"zh-Hant": "設定已對齊。",
|
|
316
|
+
"zh-Hans": "设置已对齐。"
|
|
317
|
+
},
|
|
318
|
+
"Effective compaction threshold: ": {
|
|
319
|
+
"zh-Hant": "實際壓縮門檻:",
|
|
320
|
+
"zh-Hans": "实际压缩阈值:"
|
|
321
|
+
},
|
|
322
|
+
"Only DSH settings are updated. Sync does not switch server models, change Strata configuration, or send inference requests. Start a new conversation after syncing.": {
|
|
323
|
+
"zh-Hant": "同步只更新 DSH 設定,不切換伺服器模型、不修改 Strata 設定,也不發送推論。同步後請開新對話。",
|
|
324
|
+
"zh-Hans": "同步只更新 DSH 设置,不切换服务器模型、不修改 Strata 设置,也不发送推理请求。同步后请开启新对话。"
|
|
325
|
+
},
|
|
326
|
+
"Last sync: ": {
|
|
327
|
+
"zh-Hant": "最近同步:",
|
|
328
|
+
"zh-Hans": "最近同步:"
|
|
329
|
+
},
|
|
330
|
+
"Original settings backup: ": {
|
|
331
|
+
"zh-Hant": "原設定備份:",
|
|
332
|
+
"zh-Hans": "原设置备份:"
|
|
333
|
+
},
|
|
334
|
+
"dsh-strata-sync is not ready.": {
|
|
335
|
+
"zh-Hant": "dsh-strata-sync 尚未就緒。",
|
|
336
|
+
"zh-Hans": "dsh-strata-sync 尚未就绪。"
|
|
337
|
+
},
|
|
338
|
+
"Server diagnostic: ": {
|
|
339
|
+
"zh-Hant": "伺服器診斷:",
|
|
340
|
+
"zh-Hans": "服务器诊断:"
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
Object.assign(translations, {
|
|
344
|
+
'Follow Strata output limit': {'zh-Hant':'輸出上限跟隨 Strata','zh-Hans':'输出上限跟随 Strata'},
|
|
345
|
+
'Use the verified server default. If none is available, calculate 30% of context, up to 16,384 tokens.': {'zh-Hant':'採用已確認的伺服器預設值;無固定值時,以上下文的 30% 計算,最多 16,384 tokens。','zh-Hans':'采用已确认的服务器默认值;无固定值时,按上下文的 30% 计算,最多 16,384 tokens。'},
|
|
346
|
+
'Advanced settings': {'zh-Hant':'進階設定','zh-Hans':'高级设置'},
|
|
347
|
+
'Manual output limit': {'zh-Hant':'手動輸出上限','zh-Hans':'手动输出上限'},
|
|
348
|
+
'Turn off Follow Strata output limit to enter a manual override.': {'zh-Hant':'關閉「輸出上限跟隨 Strata」後,可輸入手動覆寫值。','zh-Hans':'关闭“输出上限跟随 Strata”后,可输入手动覆盖值。'},
|
|
349
|
+
'A manual limit takes priority over the server default and automatic calculation.': {'zh-Hant':'手動上限優先於伺服器預設值與自動計算。','zh-Hans':'手动上限优先于服务器默认值与自动计算。'},
|
|
350
|
+
'Preferences changed in another window. Reload the latest preferences before saving; this discards your unsaved edits.': {'zh-Hant':'另一個視窗已變更偏好。儲存前請重新載入最新偏好,這會捨棄本視窗未儲存的編輯。','zh-Hans':'另一个窗口已更改偏好。保存前请重新加载最新偏好,这会丢弃此窗口未保存的编辑。'},
|
|
351
|
+
'Reload latest preferences': {'zh-Hant':'重新載入最新偏好','zh-Hans':'重新加载最新偏好'},
|
|
352
|
+
'Latest synchronization result': {'zh-Hant':'最近同步結果','zh-Hans':'最近同步结果'},
|
|
353
|
+
'This records the last synchronization, not a live server check.': {'zh-Hant':'這是最近一次同步的紀錄,並非伺服器即時狀態。','zh-Hans':'这是最近一次同步的记录,并非服务器实时状态。'},
|
|
354
|
+
'Server-provided output limit': {'zh-Hant':'伺服器提供的輸出上限','zh-Hans':'服务器提供的输出上限'},
|
|
355
|
+
'Effective output limit': {'zh-Hant':'實際套用的輸出上限','zh-Hans':'实际应用的输出上限'},
|
|
356
|
+
'No fixed limit': {'zh-Hant':'無固定上限','zh-Hans':'无固定上限'},
|
|
357
|
+
'Not verified': {'zh-Hant':'尚未確認','zh-Hans':'尚未确认'},
|
|
358
|
+
'Check differences to read the server and effective limits.': {'zh-Hant':'點選「檢查差異」以讀取伺服器與實際套用的上限。','zh-Hans':'点击“检查差异”以读取服务器与实际应用的上限。'},
|
|
359
|
+
'Confirmed settings are aligned.': {'zh-Hant':'已確認的設定已對齊。','zh-Hans':'已确认的设置已对齐。'},
|
|
360
|
+
'All synchronized settings are confirmed and aligned.': {'zh-Hant':'所有同步項目均已確認並對齊。','zh-Hans':'所有同步项均已确认并对齐。'},
|
|
361
|
+
'Preserved without confirmation: ': {'zh-Hant':'未能確認,保留原值:','zh-Hans':'无法确认,保留原值:'},
|
|
362
|
+
'items': {'zh-Hant':'項','zh-Hans':'项'},
|
|
363
|
+
'item': {'zh-Hant':'項','zh-Hans':'项'},
|
|
364
|
+
'Backup created': {'zh-Hant':'已建立備份','zh-Hans':'已创建备份'},
|
|
365
|
+
'No settings changed; no new backup was needed.': {'zh-Hant':'設定未變更,無須建立新備份。','zh-Hans':'设置未更改,无需创建新备份。'},
|
|
366
|
+
'This provider uses an unsupported model catalog configuration. Configure an explicit custom model list in DSH before syncing.': {'zh-Hant':'此供應者使用目前不支援的模型目錄設定。請先在 DSH 設定明確的自訂模型清單,再同步。','zh-Hans':'此提供方使用目前不支持的模型目录设置。请先在 DSH 设置明确的自定义模型列表,再同步。'},
|
|
367
|
+
});
|
|
368
|
+
const labels = {
|
|
369
|
+
input: 'Input types', reasoningEfforts: 'Reasoning levels', compat: 'Reasoning request mapping', contextWindow: 'Context window', outputTokens: 'Response output limit', headroomTokens: 'Compaction headroom',
|
|
370
|
+
summaryMaxTokens: 'Summary output limit', thresholdTokens: 'Compaction threshold', retainTokens: 'Recent conversation retention',
|
|
371
|
+
model: 'Model', modelId: 'Model', defaultModel: 'Default model',
|
|
372
|
+
thresholdRatio: 'Compaction ratio ceiling', retainRatio: 'Recent conversation retention ratio',
|
|
373
|
+
};
|
|
374
|
+
const css = `.strata-sync{max-width:880px;color:inherit;font:inherit;line-height:1.6}.strata-sync *{box-sizing:border-box}.strata-sync header{border-bottom:1px solid #8884;padding-bottom:16px;margin-bottom:20px}.strata-sync h2{margin:0;font-size:22px}.strata-sync .ss-muted{opacity:.72;font-size:13px}.strata-sync .ss-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:14px}.strata-sync label{display:flex;flex-direction:column;gap:5px;font-size:14px}.strata-sync select,.strata-sync input[type=number]{padding:9px;border:1px solid #8886;border-radius:7px;background:transparent;color:inherit;width:100%}.strata-sync option{color:#17252b;background:#fff}.strata-sync button{padding:9px 15px;border:1px solid #8885;border-radius:7px;background:transparent;color:inherit;cursor:pointer}.strata-sync button.primary{background:#126c65;border-color:#126c65;color:#fff}.strata-sync button:disabled{opacity:.45;cursor:default}.strata-sync .ss-actions{display:flex;gap:9px;flex-wrap:wrap;margin:18px 0}.strata-sync .ss-check{display:flex;flex-direction:row;align-items:center;margin-top:13px}.strata-sync .ss-card{padding:16px;border:1px solid #8884;border-radius:9px;margin:16px 0}.strata-sync .ss-status{padding:10px 13px;border-left:3px solid #21867b;background:#21867b12;margin:13px 0;overflow-wrap:anywhere}.strata-sync .ss-error{border-color:#ce7140;background:#ce714012}.strata-sync table{width:100%;border-collapse:collapse;font-size:14px}.strata-sync td,.strata-sync th{padding:9px 8px;text-align:left;border-bottom:1px solid #8883;overflow-wrap:anywhere}.strata-sync code{font-size:12px;overflow-wrap:anywhere}.strata-sync .ss-scroll{overflow-x:auto;container-type:inline-size;border:1px solid #8883;border-radius:8px;margin-top:16px}.strata-sync table{table-layout:fixed}.strata-sync th,.strata-sync td{vertical-align:top;padding:12px;line-height:1.5}.strata-sync thead th{font-size:12px;font-weight:600;background:#8881}.strata-sync th:first-child{width:34%}.strata-sync thead th:not(:first-child){width:33%;text-align:right}.strata-sync tbody th{font-weight:500}.strata-sync tbody tr:last-child>*{border-bottom:0}.strata-sync .ss-before,.strata-sync .ss-after{text-align:right;font-variant-numeric:tabular-nums}.strata-sync .ss-before{color:inherit;opacity:.7}.strata-sync .ss-after{background:#21867b0b;font-weight:600}.strata-sync .ss-target{display:block;font-size:11px;font-weight:400;opacity:.65;margin-top:5px;overflow-wrap:anywhere}.strata-sync .ss-values{display:flex;gap:5px;flex-wrap:wrap;justify-content:flex-end}.strata-sync .ss-chip{display:inline-block;padding:2px 7px;border:1px solid #8883;border-radius:5px;font-size:12px;font-weight:400;max-width:100%;overflow-wrap:anywhere}.strata-sync .ss-details{text-align:left;font-weight:400;font-size:12px}.strata-sync .ss-details summary{cursor:pointer;overflow-wrap:anywhere}.strata-sync .ss-details pre{white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0 0;font-size:11px;line-height:1.5}.strata-sync .ss-empty{font-weight:400;font-size:12px;opacity:.65}@container(max-width:440px){.strata-sync tbody th:first-child{width:100%}.strata-sync table,.strata-sync tbody,.strata-sync tr,.strata-sync tbody th,.strata-sync td{display:block;width:100%}.strata-sync thead{display:none}.strata-sync tbody tr{padding:10px 12px;border-bottom:1px solid #8883}.strata-sync tbody th,.strata-sync td{border:0;padding:5px 0}.strata-sync td::before{content:attr(data-label);float:left;font-size:12px;font-weight:400;opacity:.7}.strata-sync td{display:flow-root;padding:7px}.strata-sync .ss-values{max-width:72%;margin-left:auto}.strata-sync .ss-details{margin-top:24px}}`;
|
|
375
|
+
const fmt = (v) => v == null ? 'Not set' : typeof v === 'number' ? v.toLocaleString('en-US') : typeof v === 'object' ? JSON.stringify(v) : String(v);
|
|
376
|
+
const warningText = {
|
|
377
|
+
INPUT_UNCONFIRMED: 'Input types are missing or unsupported in server metadata. Existing input settings are preserved.',
|
|
378
|
+
REASONING_UNCONFIRMED: 'Reasoning support could not be verified against a reviewed Strata chat template. Existing reasoning settings are preserved.',
|
|
379
|
+
REASONING_DEFAULT_CONFLICT: 'The provider default reasoning level is not supported by the verified template. Reasoning settings are preserved; resolve the default in Models first.',
|
|
380
|
+
DSH_AUTO_COMPACTION_DISABLED: 'Automatic compaction is disabled in an affected Agent preset. Sync preserves that choice.',
|
|
381
|
+
SLOTS_CONTEXT_UNAVAILABLE: 'Live slots do not report a context size. Using the model context reported by Strata.',
|
|
382
|
+
SLOTS_OVERRIDE_MODEL_CONTEXT: 'The active engine context differs from the model metadata. Using the active engine context.',
|
|
383
|
+
};
|
|
384
|
+
function normalizeLanguage(id) {
|
|
385
|
+
const tag = String(id || 'en').toLowerCase();
|
|
386
|
+
if (/^zh(?:-|$)/.test(tag)) return /(?:hant|tw|hk|mo)(?:-|$)/.test(tag) ? 'zh-Hant' : 'zh-Hans';
|
|
387
|
+
return 'en';
|
|
388
|
+
}
|
|
389
|
+
function SettingsPage({ rpc, localeStore }) {
|
|
390
|
+
const language = normalizeLanguage(React.useSyncExternalStore(localeStore.subscribe, localeStore.getSnapshot, localeStore.getSnapshot));
|
|
391
|
+
const t = (text) => translations[text]?.[language] ?? text;
|
|
392
|
+
const format = (value) => value == null ? t('Not set') : typeof value === 'string' ? t(value) : fmt(value);
|
|
393
|
+
const renderValue = (field,value) => {
|
|
394
|
+
if (value == null) return h('span',{className:'ss-empty'},t('Not set'));
|
|
395
|
+
if (Array.isArray(value)) return h('div',{className:'ss-values'},...value.map((v,i)=>h('span',{className:'ss-chip',key:i},v==='text'?t('Text'):v==='image'?t('Image'):String(v))));
|
|
396
|
+
if (field==='reasoningEfforts' && typeof value==='object') return h('div',{className:'ss-values'},...Object.entries(value).map(([key,wire])=>h('span',{className:'ss-chip',key},wire===key?key:key+' → '+format(wire))));
|
|
397
|
+
if (typeof value==='object') return h('details',{className:'ss-details'},h('summary',null,t('View mapping')),h('pre',null,JSON.stringify(value,null,2)));
|
|
398
|
+
return format(value);
|
|
399
|
+
};
|
|
400
|
+
const diagnostic = (message) => translations[message] ? t(message) : language === 'en' ? message : t('Server diagnostic: ') + message;
|
|
401
|
+
const [state,setState] = React.useState(null), [preview,setPreview] = React.useState(null), [result,setResult] = React.useState(null);
|
|
402
|
+
const [form,setForm] = React.useState({providerId:'',output:'',manual:false,auto:false,follow:true,summaryGuidance:true,summaryDivisor:'16'});
|
|
403
|
+
const [pending,setPending] = React.useState(false), [error,setError] = React.useState(''), [loadError,setLoadError] = React.useState('');
|
|
404
|
+
const [notice,setNotice] = React.useState(''), [conflict,setConflict] = React.useState(false);
|
|
405
|
+
const formRef=React.useRef(form), baseline=React.useRef(null), requestSeq=React.useRef(0), alive=React.useRef(true);
|
|
406
|
+
const values = (f) => ({providerId:f.providerId,outputTokens:f.manual ? (f.output.trim()==='' ? NaN : Number(f.output)) : null,autoSync:f.auto,followLoadedModel:f.follow,summaryGuidance:f.summaryGuidance,summaryContextDivisor:f.summaryDivisor.trim()===''?NaN:Number(f.summaryDivisor)});
|
|
407
|
+
const preferences = (s) => ({providerId:s.providerId || '',outputTokens:s.outputTokens ?? null,autoSync:!!s.autoSync,followLoadedModel:s.followLoadedModel !== false,summaryGuidance:s.summaryGuidance!==false,summaryContextDivisor:s.summaryContextDivisor??16});
|
|
408
|
+
const equal = (a,b) => Object.keys(a).every(key=>Object.is(a[key],b[key]));
|
|
409
|
+
const acceptStatus = React.useCallback((s,force=false) => {
|
|
410
|
+
if (!alive.current) return;
|
|
411
|
+
const next=preferences(s), old=baseline.current;
|
|
412
|
+
const untouched=!old || equal(values(formRef.current),old.values);
|
|
413
|
+
if (force || untouched) {
|
|
414
|
+
const nextForm={providerId:next.providerId,output:next.outputTokens==null?'':String(next.outputTokens),manual:next.outputTokens!=null,auto:next.autoSync,follow:next.followLoadedModel,summaryGuidance:next.summaryGuidance,summaryDivisor:String(next.summaryContextDivisor)};
|
|
415
|
+
formRef.current=nextForm; setForm(nextForm); setConflict(false);
|
|
416
|
+
baseline.current={values:next,revision:s.settingsRevision};
|
|
417
|
+
} else if (old.revision!==s.settingsRevision || !equal(old.values,next)) {
|
|
418
|
+
setConflict(true);
|
|
419
|
+
}
|
|
420
|
+
if (old && (old.revision!==s.settingsRevision || !equal(old.values,next))) setPreview(null);
|
|
421
|
+
setState(s); setLoadError('');
|
|
422
|
+
},[]);
|
|
423
|
+
const refresh=React.useCallback(async (force=false) => {
|
|
424
|
+
const seq=++requestSeq.current;
|
|
425
|
+
const s=await rpc('status',{});
|
|
426
|
+
if (seq===requestSeq.current) acceptStatus(s,force);
|
|
427
|
+
return s;
|
|
428
|
+
},[rpc,acceptStatus]);
|
|
429
|
+
React.useEffect(()=>{
|
|
430
|
+
alive.current=true;
|
|
431
|
+
const read=()=>refresh().catch(e=>{if(alive.current)setLoadError(e.message);});
|
|
432
|
+
read(); const timer=setInterval(read,15000);
|
|
433
|
+
return ()=>{alive.current=false;requestSeq.current++;clearInterval(timer);};
|
|
434
|
+
},[refresh]);
|
|
435
|
+
function edit(patch) {
|
|
436
|
+
const next={...formRef.current,...patch};formRef.current=next;setForm(next);setPreview(null);setNotice('');
|
|
437
|
+
}
|
|
438
|
+
async function action(fn) {
|
|
439
|
+
setPending(true);setError('');setNotice('');
|
|
440
|
+
try { await fn(); }
|
|
441
|
+
catch(e) {
|
|
442
|
+
if(e.code==='stale-settings') { setConflict(true); await refresh().catch(()=>{}); }
|
|
443
|
+
else setError(e.code==='unsupported-route'?'This provider uses an unsupported model catalog configuration. Configure an explicit custom model list in DSH before syncing.':e.message||'The operation failed.');
|
|
444
|
+
} finally { if(alive.current)setPending(false); }
|
|
445
|
+
}
|
|
446
|
+
const draft=values(form), validDivisor=Number.isSafeInteger(draft.summaryContextDivisor)&&draft.summaryContextDivisor>0;
|
|
447
|
+
const valid=validDivisor&&(draft.outputTokens===null || Number.isSafeInteger(draft.outputTokens)&&draft.outputTokens>0);
|
|
448
|
+
const dirty=!!baseline.current&&!equal(draft,baseline.current.values);
|
|
449
|
+
const displayedResult=!result || state?.lastApplied?.at>result.at ? state?.lastApplied : result;
|
|
450
|
+
const activeBudget=preview?.budget;
|
|
451
|
+
const summaryTargetCeiling=form.summaryGuidance&&validDivisor&&Number.isSafeInteger(activeBudget?.contextWindow)&&activeBudget.contextWindow>0&&Number.isSafeInteger(activeBudget?.summaryMaxTokens)&&activeBudget.summaryMaxTokens>0
|
|
452
|
+
? Math.floor(Math.min(activeBudget.contextWindow/draft.summaryContextDivisor,activeBudget.summaryMaxTokens/2)) : null;
|
|
453
|
+
const outputSource=(budget)=>t(({server:'Strata Server default',manual:'Manual override',calculated:'Automatic calculation'})[budget?.outputSource]||'Automatic calculation');
|
|
454
|
+
const preserved=(view)=>{
|
|
455
|
+
const status=view?.capabilityStatus;
|
|
456
|
+
return [(!status ? !view?.server?.model?.capabilities?.input : status.input!=='confirmed')?'Input types':null,
|
|
457
|
+
(!status ? !view?.capabilityEdits?.reasoningEfforts : status.reasoning!=='confirmed')?'Reasoning levels':null].filter(Boolean);
|
|
458
|
+
};
|
|
459
|
+
const alignment=(view)=>{
|
|
460
|
+
const kept=preserved(view);
|
|
461
|
+
return h('div',{className:'ss-status','data-testid':'alignment'},
|
|
462
|
+
h('p',null,t(kept.length?'Confirmed settings are aligned.':'All synchronized settings are confirmed and aligned.')),
|
|
463
|
+
kept.length>0&&h('p',null,t('Preserved without confirmation: '),kept.length,' ',t(kept.length===1?'item':'items'),' — ',kept.map(t).join(', ')));
|
|
464
|
+
};
|
|
465
|
+
const serverLimit=(model)=>model?.serverOutputTokens!=null?format(model.serverOutputTokens):t(['missing','unlimited'].includes(model?.outputLimitStatus)?'No fixed limit':'Not verified');
|
|
466
|
+
const outputValues=(budget,model)=>h('div',{className:'ss-grid'},
|
|
467
|
+
h('div',null,h('strong',null,serverLimit(model)),h('div',{className:'ss-muted'},t('Server-provided output limit'))),
|
|
468
|
+
h('div',null,h('strong',null,format(budget?.outputTokens)),h('div',{className:'ss-muted'},t('Effective output limit'))),
|
|
469
|
+
h('div',null,h('strong',null,outputSource(budget)),h('div',{className:'ss-muted'},t('Output limit source: '))));
|
|
470
|
+
const warnings=(items)=>(items||[]).map((w,i)=>h('div',{key:i,className:'ss-status ss-error'},typeof w==='string'?(t(warningText[w])||(w.startsWith('SLOTS_')?t('Live slot information is unavailable. Using the model context reported by Strata.'):w)):JSON.stringify(w)));
|
|
471
|
+
return h('section',{className:'strata-sync','aria-label':'dsh-strata-sync'},
|
|
472
|
+
h('style',null,css),
|
|
473
|
+
h('header',null,h('h2',null,'dsh-strata-sync'),h('p',{className:'ss-muted'},t('Align DSH with the model loaded by Strata Server. The server supplies model, context and any fixed output default; this plugin calculates fallback output and compaction budgets.'))),
|
|
474
|
+
!state&&!loadError&&h('div',{className:'ss-status',role:'status'},t('Loading sync settings...')),
|
|
475
|
+
state?.busy&&h('div',{className:'ss-status',role:'status'},t('DSH is running a task. Synchronization will wait until all tasks are idle.')),
|
|
476
|
+
(error||loadError)&&h('div',{className:'ss-status ss-error',role:'alert'},diagnostic(error||loadError)),
|
|
477
|
+
notice&&h('div',{className:'ss-status',role:'status'},t(notice)),
|
|
478
|
+
conflict&&h('div',{className:'ss-status ss-error'},
|
|
479
|
+
h('p',null,t('Preferences changed in another window. Reload the latest preferences before saving; this discards your unsaved edits.')),
|
|
480
|
+
h('button',{disabled:pending,onClick:()=>action(()=>refresh(true))},t('Reload latest preferences'))),
|
|
481
|
+
h('label',null,t('Strata provider'),h('select',{value:form.providerId,onChange:e=>edit({providerId:e.target.value}),disabled:pending||!state},
|
|
482
|
+
h('option',{value:''},t('Choose an existing provider')),...(state?.providers||[]).map(p=>h('option',{key:p.id,value:p.id},p.id)))),
|
|
483
|
+
h('p',{className:'ss-muted'},t('Uses the URL and credentials already configured in DSH. API keys are never displayed or copied here.')),
|
|
484
|
+
h('div',{className:'ss-card'},
|
|
485
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:!form.manual,'aria-label':t('Follow Strata output limit'),onChange:e=>edit({manual:!e.target.checked}),disabled:pending||!state}),t('Follow Strata output limit')),
|
|
486
|
+
h('p',{className:'ss-muted'},t('Use the verified server default. If none is available, calculate 30% of context, up to 16,384 tokens.')),
|
|
487
|
+
activeBudget?outputValues(activeBudget,preview.server?.model):h('p',{className:'ss-muted'},t('Check differences to read the server and effective limits.')),
|
|
488
|
+
h('details',{className:'ss-details',open:form.manual},h('summary',null,t('Advanced settings')),
|
|
489
|
+
h('label',null,t('Manual output limit'),h('input',{type:'number',min:1,step:1,value:form.output,onChange:e=>edit({output:e.target.value}),disabled:pending||!state||!form.manual})),
|
|
490
|
+
h('p',{className:'ss-muted'},t(form.manual?'A manual limit takes priority over the server default and automatic calculation.':'Turn off Follow Strata output limit to enter a manual override.')))),
|
|
491
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.follow,onChange:e=>edit({follow:e.target.checked}),disabled:pending||!state}),t('Follow the loaded Strata model as the default')),
|
|
492
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox',checked:form.auto,onChange:e=>edit({auto:e.target.checked}),disabled:pending||!state}),t('Automatically sync when DSH starts')),
|
|
493
|
+
h('p',{className:'ss-muted'},t('Runs once on the next DSH start. Waits for running tasks to finish; no periodic server checks.')),
|
|
494
|
+
h('div',{className:'ss-card','data-testid':'summary-target'},
|
|
495
|
+
h('strong',null,t('Dynamic summary target')),
|
|
496
|
+
h('label',{className:'ss-check'},h('input',{type:'checkbox','data-testid':'summary-guidance-toggle','aria-label':t('Enable summary guidance'),checked:form.summaryGuidance,onChange:e=>edit({summaryGuidance:e.target.checked}),disabled:pending||!state}),t('Enable summary guidance')),
|
|
497
|
+
h('p',{className:'ss-muted'},t("Enabled by default. Turning this off keeps DSH's native summary and automatic compaction behavior.")),
|
|
498
|
+
h('label',null,t('Context divisor (context ÷ N)'),h('input',{type:'number','data-testid':'summary-divisor','aria-label':t('Context divisor (context ÷ N)'),'aria-invalid':!validDivisor,min:1,max:Number.MAX_SAFE_INTEGER,step:1,value:form.summaryDivisor,onChange:e=>edit({summaryDivisor:e.target.value}),disabled:pending||!state})),
|
|
499
|
+
state&&!validDivisor&&h('p',{className:'ss-status ss-error',role:'alert'},t('Enter a positive whole-number divisor.')),
|
|
500
|
+
h('p',{className:'ss-muted'},t('Default: 16. A smaller divisor allows a larger target, still bounded by half the summary output limit and half the selected content estimate.')),
|
|
501
|
+
h('p',{className:'ss-muted'},t('Save preferences to apply these controls to subsequent summary requests; other model and compaction settings stay unchanged.')),
|
|
502
|
+
!form.summaryGuidance&&h('p',{className:'ss-status','data-testid':'summary-guidance-off'},t('Summary guidance is off. No extra sentence is added to the native prompt.')),
|
|
503
|
+
h('p',null,t("When enabled, before each supported Strata summary, calculates a length target and appends one sentence to DSH's native summary prompt.")),
|
|
504
|
+
summaryTargetCeiling!==null&&(summaryTargetCeiling>=256
|
|
505
|
+
? h('p',{'data-testid':'summary-target-ceiling'},t('Maximum target for this preview: '),format(summaryTargetCeiling),' tokens')
|
|
506
|
+
: h('p',{className:'ss-status','data-testid':'summary-target-native'},t('This preview is below the 256-token minimum. The native summary prompt remains unchanged.'))),
|
|
507
|
+
h('details',{className:'ss-details'},h('summary',null,t('How the target is calculated')),
|
|
508
|
+
h('p',{className:'ss-muted'},t('Target = floor(min(context / N, summary output limit / 2, selected content estimate / 2)).')),
|
|
509
|
+
h('p',{className:'ss-muted'},t('Context uses the smaller of the verified Strata window and the existing DSH model window. The preview ceiling assumes the proposed budgets; existing conversation settings can make the actual target lower.')),
|
|
510
|
+
h('p',{className:'ss-muted'},t('The actual target may be lower for shorter content. System prompts and tool schemas are excluded from the content estimate.')),
|
|
511
|
+
h('p',{className:'ss-muted'},t('The original prompt, output cap and other settings stay unchanged. This is a writing target, not a guaranteed length or an optimal value.')),
|
|
512
|
+
h('p',{className:'ss-muted'},t('Targets below 256 tokens leave the original prompt unchanged; the native summary still runs.')),
|
|
513
|
+
h('p',{className:'ss-muted'},t('If the route or native summary format cannot be verified, DSH uses its original prompt.')),
|
|
514
|
+
h('p',{className:'ss-muted'},t('Unpriced image/file attachments, cancellation or a disabled plugin preserve native request handling.')))),
|
|
515
|
+
h('div',{className:'ss-actions'},
|
|
516
|
+
h('button',{disabled:pending||!state||!form.providerId||!valid||!dirty||conflict,onClick:()=>action(async()=>{
|
|
517
|
+
const changes=Object.fromEntries(Object.entries(values(formRef.current)).filter(([key,value])=>!Object.is(value,baseline.current.values[key])));
|
|
518
|
+
const s=await rpc('configure',{...changes,expectedRevision:baseline.current.revision});
|
|
519
|
+
requestSeq.current++;acceptStatus(s,true);setPreview(null);setNotice('Sync preferences saved.');
|
|
520
|
+
})},t('Save preferences')),
|
|
521
|
+
h('button',{disabled:pending||!state||!form.providerId||dirty||conflict||!valid,onClick:()=>action(async()=>{setPreview(await rpc('preview',{providerId:form.providerId}));await refresh();})},pending?t('Working...'):t('Check differences')),
|
|
522
|
+
h('button',{className:'primary',disabled:pending||!state||dirty||conflict||!preview||state.busy||!preview.changes?.length,onClick:()=>action(async()=>{
|
|
523
|
+
const r=await rpc('apply',{fingerprint:preview.fingerprint,providerId:form.providerId});
|
|
524
|
+
const view=r.preview||preview;
|
|
525
|
+
setResult({at:new Date().toISOString(),modelId:view.server?.model?.id,providerId:view.providerId||form.providerId,budget:view.budget,capabilityStatus:view.capabilityStatus,serverOutputTokens:view.server?.model?.serverOutputTokens,outputLimitStatus:view.server?.model?.outputLimitStatus,backupPath:r.backupPath,applied:r.applied,warnings:view.server?.warnings});
|
|
526
|
+
setPreview(null);setNotice(r.applied?'Synced and backed up the original settings. Start a new conversation to use the updated compaction policy.':'Settings already match. No changes needed.');await refresh();
|
|
527
|
+
})},t('Back up and sync'))),
|
|
528
|
+
dirty&&!conflict&&h('p',{className:'ss-muted'},t('Save preferences before checking for differences.')),
|
|
529
|
+
preview&&h('div',{className:'ss-card'},
|
|
530
|
+
h('strong',null,'Strata Server'),h('p',null,h('code',null,preview.server?.model?.id)),
|
|
531
|
+
h('div',{className:'ss-grid'},
|
|
532
|
+
h('div',null,h('strong',null,format(preview.budget?.contextWindow)),h('div',{className:'ss-muted'},t('Context tokens'))),
|
|
533
|
+
h('div',null,h('strong',null,format(preview.budget?.outputTokens)),h('div',{className:'ss-muted'},t('Response output limit'))),
|
|
534
|
+
h('div',null,h('strong',null,format(preview.budget?.summaryMaxTokens)),h('div',{className:'ss-muted'},t('Summary output limit')))),
|
|
535
|
+
h('p',{className:'ss-muted'},t('Output limit source: '),outputSource(preview.budget)),
|
|
536
|
+
['missing','unlimited'].includes(preview.server?.model?.outputLimitStatus)&&h('p',{className:'ss-muted'},t('Server has no fixed output limit.')),
|
|
537
|
+
['unavailable','unverified'].includes(preview.server?.model?.outputLimitStatus)&&h('p',{className:'ss-muted'},t('Server output limit could not be verified.')),
|
|
538
|
+
h('p',null,t('Input types'),': ',preview.server?.model?.capabilities?.input?t(preview.server.model.capabilities.input.includes('image')?'Text + image':'Text'):t('Unconfirmed — keep current setting')),
|
|
539
|
+
h('p',null,t('Reasoning levels'),': ',preview.capabilityEdits?.reasoningEfforts?Object.keys(preview.capabilityEdits.reasoningEfforts).join(' / '):t('Unconfirmed — keep current setting')),
|
|
540
|
+
preview.capabilityEdits?.reasoningEfforts&&h('p',{className:'ss-muted'},t('Verified template: '),preview.server?.model?.capabilities?.reasoning?.family),
|
|
541
|
+
h('p',{className:'ss-muted'},t('Only verified capabilities are synchronized. Unknown fields keep their existing values. Reasoning choices do not change the selected effort.')),
|
|
542
|
+
h('p',{className:'ss-muted'},t('Context source: '),preview.server?.model?.contextSource||t('Server model metadata')),
|
|
543
|
+
preview.presetIds?.length>0&&h('p',{className:'ss-muted'},t('Affected presets: '),preview.presetIds.join(', '),t('. Existing conversations retain their preset revision. Start a new conversation to use the updated compaction budget.')),
|
|
544
|
+
...warnings(preview.server?.warnings),
|
|
545
|
+
preview.changes?.length>0&&h('div',{className:'ss-scroll'},h('table',null,h('thead',null,h('tr',null,h('th',{scope:'col'},t('Setting')),h('th',{scope:'col'},t('Current')),h('th',{scope:'col'},t('After sync')))),
|
|
546
|
+
h('tbody',null,...preview.changes.map(c=>h('tr',{key:(c.target||'')+c.field},h('th',{scope:'row'},t(labels[c.field]||c.field),c.target&&h('span',{className:'ss-target',title:c.target},c.target)),h('td',{className:'ss-before','data-label':t('Current')},renderValue(c.field,c.before)),h('td',{className:'ss-after','data-label':t('After sync')},renderValue(c.field,c.after))))))),
|
|
547
|
+
!preview.changes?.length&&alignment(preview),
|
|
548
|
+
h('p',null,t('Effective compaction threshold: '),format(preview.budget?.thresholdTokens),' tokens (',preview.budget?.thresholdPercent?.toFixed(2),'%)'),
|
|
549
|
+
h('p',{className:'ss-muted'},t('Only DSH settings are updated. Sync does not switch server models, change Strata configuration, or send inference requests. Start a new conversation after syncing.'))),
|
|
550
|
+
displayedResult?.budget&&h('div',{className:'ss-card','data-testid':'sync-result'},
|
|
551
|
+
h('strong',null,t('Latest synchronization result')),h('p',{className:'ss-muted'},t('This records the last synchronization, not a live server check.')),
|
|
552
|
+
h('p',null,t('Model'),': ',h('code',null,displayedResult.modelId)),h('p',null,t('Context window'),': ',format(displayedResult.budget.contextWindow)),
|
|
553
|
+
outputValues(displayedResult.budget,displayedResult),alignment(displayedResult),
|
|
554
|
+
h('p',null,t(displayedResult.backupPath?'Backup created':'No settings changed; no new backup was needed.')),
|
|
555
|
+
displayedResult.backupPath&&h('p',{className:'ss-muted'},t('Original settings backup: '),h('code',null,displayedResult.backupPath)),
|
|
556
|
+
h('p',{className:'ss-muted'},t('Last sync: '),format(displayedResult.at)),...warnings(displayedResult.warnings)),
|
|
557
|
+
state?.lastError&&h('div',{className:'ss-status ss-error'},diagnostic(typeof state.lastError==='string'?state.lastError:state.lastError.message)));
|
|
558
|
+
}
|
|
559
|
+
function apply(ctx) {
|
|
560
|
+
const localeStore = {
|
|
561
|
+
subscribe: listener => ctx.locale.subscribe(listener),
|
|
562
|
+
getSnapshot: () => ctx.locale.getLocale().active,
|
|
563
|
+
};
|
|
564
|
+
const rpc = async (endpoint,payload) => {
|
|
565
|
+
const result = await ctx.connection.rpc.call('/api','strata-sync/'+endpoint,payload);
|
|
566
|
+
if (!result?.ok) {
|
|
567
|
+
const error = new Error(result?.error?.message || 'dsh-strata-sync is not ready.');
|
|
568
|
+
error.code = result?.error?.code;
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
return result.value;
|
|
572
|
+
};
|
|
573
|
+
ctx.effect(() => ctx.slots.inject('settings.section', () => ctx.slots.register({
|
|
574
|
+
name:'settings.section',id:'strata-sync',order:25,label:()=> 'dsh-strata-sync',inject:()=>({rpc,localeStore}),
|
|
575
|
+
},SettingsPage)), 'strata-sync: settings page');
|
|
576
|
+
}
|
|
577
|
+
return {inject:['slots','connection','locale'],apply};
|
|
578
|
+
},
|
|
579
|
+
});
|