gpteam 0.1.8 → 0.1.9

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/README.md CHANGED
@@ -8,7 +8,7 @@ npx gpteam
8
8
 
9
9
  The CLI asks for an API key, validates it with `/v1/models`, detects available models, benchmarks all production ingress endpoints with real API requests, then backs up old files and writes the selected client configuration. The next steps are blocked until the key validation succeeds. Ingress endpoints are benchmarked in parallel, while rounds for the same endpoint remain sequential to keep real API request pressure bounded. Failed benchmark rows show their error reason in the result table.
10
10
 
11
- Recommendation order is deterministic: success rate first, then an experience score. The score weighs first SSE event time, total completion time, p90 completion tail latency, and health-check time, so a node with one very slow probe is not recommended just because its median result looks good. Model prompts use "configurable context" wording because the value is written to the client-side Codex `model_context_window`; it must not be confused with a public marketing total-window label.
11
+ Recommendation order is deterministic: success rate first, then an experience score. The score weighs first SSE event time, total completion time, p90 completion tail latency, and health-check time, so a node with one very slow probe is not recommended just because its median result looks good. The result table also marks the fastest full completion separately from the balanced recommendation, because deep or long-output model runs may care more about full completion time than first-token responsiveness. Model prompts use "configurable context" wording because the value is written to the client-side Codex `model_context_window`; it must not be confused with a public marketing total-window label.
12
12
 
13
13
  Codex config writing follows the same safety pattern as cc-switch: keep top-level fields separate from provider tables, preserve unrelated sections such as MCP servers, and stop before writing if the generated TOML would contain duplicate keys.
14
14
 
package/lib/cli.js CHANGED
@@ -38,7 +38,7 @@ export async function runCli(argv = []) {
38
38
 
39
39
  printStep(theme, 3, 5, '真实请求测速', 'GET /api/health + POST /v1/responses stream=true');
40
40
  printHint(theme, '入口之间并行测速,单入口多轮顺序执行,避免同时打出过多真实请求。');
41
- printHint(theme, '推荐规则:成功率优先,其次按首包、完成、尾延迟和健康检查计算体验分,分数越低越好。');
41
+ printHint(theme, '综合推荐规则:成功率优先,其次按首包、完成、尾延迟和健康检查计算体验分,分数越低越好;完成最快会单独标出。');
42
42
  printHint(theme, `模型:${model.id},测速输出上限:${maxOutputTokens}`);
43
43
  const results = await benchmarkNodes(INGRESS_NODES, {
44
44
  apiKey,
@@ -97,6 +97,7 @@ export function parseArgs(argv) {
97
97
 
98
98
  export function printResults(results, theme = createTheme()) {
99
99
  const recommended = results.find((item) => item.successRate > 0);
100
+ const fastestTotal = findFastestTotalResult(results);
100
101
  const rows = results.map((item) => [
101
102
  `${item.node.label}(${describeSplit(item.node)})`,
102
103
  formatMs(item.dnsMs),
@@ -108,10 +109,10 @@ export function printResults(results, theme = createTheme()) {
108
109
  `${Math.round(item.successRate * 100)}%`,
109
110
  formatScore(item.experienceScore),
110
111
  formatMs(item.healthMs),
111
- item === recommended ? theme.ok('推荐') : '-',
112
+ resultMarker(item, recommended, fastestTotal),
112
113
  item.error || '-'
113
114
  ]);
114
- const header = ['节点', 'DNS', 'TCP', 'TLS', '首包', '完成', '尾延迟', '成功率', '体验分', '健康检查', '推荐', '错误'];
115
+ const header = ['节点', 'DNS', 'TCP', 'TLS', '首包', '完成', '尾延迟', '成功率', '体验分', '健康检查', '标记', '错误'];
115
116
  const widths = header.map((name, i) => Math.max(displayWidth(name), ...rows.map((row) => displayWidth(row[i]))) + 2);
116
117
  console.log('');
117
118
  console.log(theme.title(formatRow(header, widths)));
@@ -119,6 +120,22 @@ export function printResults(results, theme = createTheme()) {
119
120
  const line = formatRow(row, widths);
120
121
  console.log(row[10] === '-' ? line : theme.ok(line));
121
122
  }
123
+ if (recommended && fastestTotal && recommended !== fastestTotal) {
124
+ printHint(theme, `综合推荐偏首包和稳定性;如果更看重整段完成速度,可选 ${formatNodeLabel(fastestTotal.node)}。`);
125
+ }
126
+ }
127
+
128
+ export function findFastestTotalResult(results) {
129
+ return results
130
+ .filter((item) => item.successRate > 0 && Number.isFinite(item.totalMs))
131
+ .sort((a, b) => a.totalMs - b.totalMs)[0] || null;
132
+ }
133
+
134
+ export function resultMarker(item, recommended, fastestTotal) {
135
+ const labels = [];
136
+ if (item === recommended) labels.push('综合推荐');
137
+ if (item === fastestTotal) labels.push('完成最快');
138
+ return labels.length ? labels.join('/') : '-';
122
139
  }
123
140
 
124
141
  async function chooseModel(rl, models, preferred, theme) {
@@ -145,7 +162,7 @@ export async function chooseNode(rl, results, preferred, recommendedID, theme =
145
162
  const preferredNode = nodes.find((node) => node.id === preferred);
146
163
  if (preferredNode) return preferredNode;
147
164
  if (recommendedID) {
148
- printStatus(theme, '推荐', recommendedID);
165
+ printStatus(theme, '综合推荐', recommendedID);
149
166
  }
150
167
  return (await choose(rl, '请选择最终写入的入口', nodes.map((node) => ({
151
168
  id: node.id,
package/lib/help.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PACKAGE_NAME = 'gpteam';
2
- export const PACKAGE_VERSION = '0.1.8';
2
+ export const PACKAGE_VERSION = '0.1.9';
3
3
 
4
4
  export function getHelpText() {
5
5
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gpteam",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "GPTeam API interactive client configurator and ingress benchmark CLI.",
5
5
  "type": "module",
6
6
  "bin": {