u1s1-cli 1.11.0 → 1.11.1

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/dist/bench.js CHANGED
@@ -82,6 +82,24 @@ function listSuites() {
82
82
  return suites;
83
83
  }
84
84
  // ─── 模型调用 ───
85
+ function asRecord(value) {
86
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
87
+ }
88
+ function tokenCount(value) {
89
+ return typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : 0;
90
+ }
91
+ /** 只认 OpenAI 兼容形状:choices[0].message.content 与 usage.prompt/completion_tokens,缺哪项哪项归零。 */
92
+ function parseChatCompletion(value) {
93
+ const data = asRecord(value);
94
+ const choices = Array.isArray(data?.choices) ? data.choices : [];
95
+ const message = asRecord(asRecord(choices[0])?.message);
96
+ const usage = asRecord(data?.usage);
97
+ return {
98
+ response: typeof message?.content === "string" ? message.content : "",
99
+ tokensIn: tokenCount(usage?.prompt_tokens),
100
+ tokensOut: tokenCount(usage?.completion_tokens),
101
+ };
102
+ }
85
103
  async function callModel(input) {
86
104
  const { baseUrl, apiKey, modelId, prompt, officialCfg } = input;
87
105
  const start = performance.now();
@@ -111,30 +129,7 @@ async function callModel(input) {
111
129
  const errBody = await readResponseTextCapped(res, 64 * 1024).catch(() => "未知错误");
112
130
  return { response: "", latencyMs, tokensIn: 0, tokensOut: 0, error: `HTTP ${res.status}: ${errBody.slice(0, 200)}` };
113
131
  }
114
- const value = await readJsonResponseCapped(res);
115
- const data = value && typeof value === "object" && !Array.isArray(value)
116
- ? value
117
- : null;
118
- const choices = Array.isArray(data?.choices) ? data.choices : [];
119
- const firstChoice = choices[0] && typeof choices[0] === "object" && !Array.isArray(choices[0])
120
- ? choices[0]
121
- : null;
122
- const message = firstChoice?.message && typeof firstChoice.message === "object" && !Array.isArray(firstChoice.message)
123
- ? firstChoice.message
124
- : null;
125
- const usage = data?.usage && typeof data.usage === "object" && !Array.isArray(data.usage)
126
- ? data.usage
127
- : null;
128
- return {
129
- response: typeof message?.content === "string" ? message.content : "",
130
- latencyMs,
131
- tokensIn: typeof usage?.prompt_tokens === "number" && Number.isFinite(usage.prompt_tokens)
132
- ? Math.max(0, usage.prompt_tokens)
133
- : 0,
134
- tokensOut: typeof usage?.completion_tokens === "number" && Number.isFinite(usage.completion_tokens)
135
- ? Math.max(0, usage.completion_tokens)
136
- : 0,
137
- };
132
+ return { ...parseChatCompletion(await readJsonResponseCapped(res)), latencyMs };
138
133
  }
139
134
  catch (e) {
140
135
  const latencyMs = Math.round(performance.now() - start);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.11.0",
3
+ "version": "1.11.1",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {
@@ -391,23 +391,31 @@ const TUI_TEXT_TARGETS = [
391
391
  [["modes", "interactive", "components", "settings-selector.js"], SETTINGS_REPLACEMENTS, "设置菜单"],
392
392
  ];
393
393
 
394
+ /**
395
+ * 逐条套文案替换规则:已替换过的算应用成功(幂等),出现次数与预期不符的跳过
396
+ * (pi 升级后上下文变了,宁可不改也不误改)。
397
+ */
398
+ function applyTextRules(text, rules) {
399
+ let applied = 0;
400
+ for (const rule of rules) {
401
+ const { from, to, expected } = Array.isArray(rule) ? { from: rule[0], to: rule[1], expected: 1 } : rule;
402
+ if (text.includes(to)) {
403
+ applied++;
404
+ continue;
405
+ }
406
+ if (text.split(from).length !== expected + 1) continue;
407
+ text = text.split(from).join(to);
408
+ applied++;
409
+ }
410
+ return { text, applied };
411
+ }
412
+
394
413
  function patchTuiText() {
395
414
  for (const piDir of findPackageDirs("@earendil-works/pi-coding-agent")) {
396
415
  for (const [parts, rules, label] of TUI_TEXT_TARGETS) {
397
416
  const target = join(piDir, "dist", ...parts);
398
417
  try {
399
- let text = readFileSync(target, "utf8");
400
- let applied = 0;
401
- for (const rule of rules) {
402
- const { from, to, expected } = Array.isArray(rule) ? { from: rule[0], to: rule[1], expected: 1 } : rule;
403
- if (text.includes(to)) {
404
- applied++;
405
- continue;
406
- }
407
- if (text.split(from).length !== expected + 1) continue;
408
- text = text.split(from).join(to);
409
- applied++;
410
- }
418
+ const { text, applied } = applyTextRules(readFileSync(target, "utf8"), rules);
411
419
  if (applied < rules.length) {
412
420
  console.log(
413
421
  `[u1s1] 提示: pi 版本可能已更新,${label}汉化补丁只应用了 ${applied}/${rules.length} 处(不影响使用)`,