gpteam 0.1.0 → 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/README.md +1 -1
- package/lib/bench.js +5 -5
- package/lib/cli.js +28 -8
- package/lib/help.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ Interactive GPTeam API client configurator.
|
|
|
6
6
|
npx gpteam
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The CLI asks for an API key, detects available models, benchmarks all production ingress endpoints with real API requests, then writes the selected client configuration
|
|
9
|
+
The CLI asks for an API key, detects available models, benchmarks all production ingress endpoints with real API requests, then backs up old files and writes the selected client configuration. Ingress endpoints are benchmarked in parallel, while rounds for the same endpoint remain sequential to keep real API request pressure bounded.
|
|
10
10
|
|
|
11
11
|
Supported clients:
|
|
12
12
|
|
package/lib/bench.js
CHANGED
|
@@ -5,14 +5,14 @@ import { inspectSSEBody } from './sse.js';
|
|
|
5
5
|
|
|
6
6
|
export async function benchmarkNodes(nodes, options) {
|
|
7
7
|
const rounds = options.rounds || 3;
|
|
8
|
-
const
|
|
9
|
-
|
|
8
|
+
const runBenchmark = options.benchmarkNode || benchmarkNode;
|
|
9
|
+
const results = await Promise.all(nodes.map(async (node) => {
|
|
10
10
|
const samples = [];
|
|
11
11
|
for (let index = 0; index < rounds; index += 1) {
|
|
12
|
-
samples.push(await
|
|
12
|
+
samples.push(await runBenchmark(node, options));
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
}
|
|
14
|
+
return summarizeNode(node, samples);
|
|
15
|
+
}));
|
|
16
16
|
return results.sort((a, b) => scoreResult(a) - scoreResult(b));
|
|
17
17
|
}
|
|
18
18
|
|
package/lib/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function runCli(argv = []) {
|
|
|
20
20
|
const rl = readline.createInterface({ input, output });
|
|
21
21
|
try {
|
|
22
22
|
console.log('GPTeam API 配置助手');
|
|
23
|
-
console.log('
|
|
23
|
+
console.log('接下来会用你的 key 跑几次真实请求测速,然后把选中的入口写进客户端配置。原来的配置会先备份。\n');
|
|
24
24
|
|
|
25
25
|
const apiKey = args.key || args.apiKey || await askRequired(rl, '请输入 API key:');
|
|
26
26
|
const client = await choose(rl, '请选择客户端类型', CLIENTS, args.client);
|
|
@@ -31,6 +31,7 @@ export async function runCli(argv = []) {
|
|
|
31
31
|
const maxOutputTokens = Number(args.maxOutputTokens || 648);
|
|
32
32
|
|
|
33
33
|
console.log('\n开始真实测速:GET /api/health + POST /v1/responses stream=true');
|
|
34
|
+
console.log('测速会按入口并行执行,每个入口内部仍按轮次顺序执行,避免同时打出过多真实请求。');
|
|
34
35
|
console.log(`模型:${model.id},测速输出上限:${maxOutputTokens}`);
|
|
35
36
|
const results = await benchmarkNodes(INGRESS_NODES, {
|
|
36
37
|
apiKey,
|
|
@@ -43,6 +44,7 @@ export async function runCli(argv = []) {
|
|
|
43
44
|
|
|
44
45
|
const recommended = results.find((item) => item.successRate > 0) || results[0];
|
|
45
46
|
const selectedNode = await chooseNode(rl, results, args.node, recommended && recommended.node.id);
|
|
47
|
+
assertNodeConfig(selectedNode);
|
|
46
48
|
const written = writeClientConfig(client.id, {
|
|
47
49
|
apiKey,
|
|
48
50
|
model: model.id,
|
|
@@ -54,7 +56,8 @@ export async function runCli(argv = []) {
|
|
|
54
56
|
|
|
55
57
|
console.log('\n已写入配置:');
|
|
56
58
|
for (const filePath of written) console.log(`- ${filePath}`);
|
|
57
|
-
console.log(`入口:${
|
|
59
|
+
console.log(`入口:${formatNodeLabel(selectedNode)}`);
|
|
60
|
+
console.log(`地址:${selectedNode.baseUrl}`);
|
|
58
61
|
} finally {
|
|
59
62
|
rl.close();
|
|
60
63
|
}
|
|
@@ -117,7 +120,7 @@ async function chooseModel(rl, models, preferred) {
|
|
|
117
120
|
if (selected) return selected;
|
|
118
121
|
console.log('\n可用模型:');
|
|
119
122
|
models.forEach((model, index) => {
|
|
120
|
-
console.log(`${index + 1}. ${model
|
|
123
|
+
console.log(`${index + 1}. ${formatModelLabel(model)}`);
|
|
121
124
|
});
|
|
122
125
|
const answer = await askRequired(rl, '请选择模型序号:');
|
|
123
126
|
const index = Math.max(1, Math.min(models.length, Number(answer) || 1)) - 1;
|
|
@@ -127,11 +130,11 @@ async function chooseModel(rl, models, preferred) {
|
|
|
127
130
|
async function askContextLength(rl, model, preferred) {
|
|
128
131
|
const max = Number(model.contextLength || 400000);
|
|
129
132
|
if (preferred) return clamp(Number(preferred), 1, max);
|
|
130
|
-
const answer = await rl.question(
|
|
133
|
+
const answer = await rl.question(`请输入上下文窗口(最大 ${max},输出上限 ${model.maxOutputTokens},默认 ${max},回车即选择默认):`);
|
|
131
134
|
return clamp(Number(answer || max), 1, max);
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
async function chooseNode(rl, results, preferred, recommendedID) {
|
|
137
|
+
export async function chooseNode(rl, results, preferred, recommendedID) {
|
|
135
138
|
const nodes = results.map((item) => item.node);
|
|
136
139
|
const preferredNode = nodes.find((node) => node.id === preferred);
|
|
137
140
|
if (preferredNode) return preferredNode;
|
|
@@ -140,19 +143,20 @@ async function chooseNode(rl, results, preferred, recommendedID) {
|
|
|
140
143
|
}
|
|
141
144
|
return (await choose(rl, '请选择最终写入的入口', nodes.map((node) => ({
|
|
142
145
|
id: node.id,
|
|
143
|
-
label:
|
|
146
|
+
label: formatNodeLabel(node),
|
|
147
|
+
raw: node
|
|
144
148
|
})))).raw;
|
|
145
149
|
}
|
|
146
150
|
|
|
147
151
|
async function choose(rl, title, items, preferred) {
|
|
148
152
|
const found = items.find((item) => item.id === preferred);
|
|
149
|
-
if (found) return { ...found, raw: found };
|
|
153
|
+
if (found) return { ...found, raw: found.raw || found };
|
|
150
154
|
console.log(`\n${title}:`);
|
|
151
155
|
items.forEach((item, index) => console.log(`${index + 1}. ${item.label}`));
|
|
152
156
|
const answer = await askRequired(rl, '请输入序号:');
|
|
153
157
|
const index = Math.max(1, Math.min(items.length, Number(answer) || 1)) - 1;
|
|
154
158
|
const item = items[index];
|
|
155
|
-
return { ...item, raw: item };
|
|
159
|
+
return { ...item, raw: item.raw || item };
|
|
156
160
|
}
|
|
157
161
|
|
|
158
162
|
async function askRequired(rl, prompt) {
|
|
@@ -180,6 +184,22 @@ function clamp(value, min, max) {
|
|
|
180
184
|
return Math.max(min, Math.min(max, Math.floor(value)));
|
|
181
185
|
}
|
|
182
186
|
|
|
187
|
+
export function formatModelLabel(model) {
|
|
188
|
+
const context = Number(model.contextLength || 0);
|
|
189
|
+
const outputTokens = Number(model.maxOutputTokens || 0);
|
|
190
|
+
return `${model.id}(上下文窗口 ${context},输出上限 ${outputTokens})`;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function formatNodeLabel(node) {
|
|
194
|
+
return `${node.label}(${describeSplit(node)})`;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function assertNodeConfig(node) {
|
|
198
|
+
if (!node || !node.baseUrl) {
|
|
199
|
+
throw new Error('入口配置异常:缺少 baseUrl,已停止写入客户端配置');
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
183
203
|
function toCamel(value) {
|
|
184
204
|
return value.replace(/-([a-z])/g, (_, char) => char.toUpperCase());
|
|
185
205
|
}
|
package/lib/help.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const PACKAGE_NAME = 'gpteam';
|
|
2
|
-
export const PACKAGE_VERSION = '0.1.
|
|
2
|
+
export const PACKAGE_VERSION = '0.1.2';
|
|
3
3
|
|
|
4
4
|
export function getHelpText() {
|
|
5
5
|
return [
|
|
@@ -13,7 +13,7 @@ export function getHelpText() {
|
|
|
13
13
|
' --api-key <key> 预填 API key',
|
|
14
14
|
' --client <id> codex / opencode / claude-code / openclaw',
|
|
15
15
|
' --model <id> 预选模型,例如 gpt-5.5',
|
|
16
|
-
' --context <tokens>
|
|
16
|
+
' --context <tokens> 预设上下文窗口',
|
|
17
17
|
' --effort <level> 按模型支持项预选,常见为 none / low / medium / high / xhigh',
|
|
18
18
|
' --node <id> jp-direct / jp-split / hk-split / us-split',
|
|
19
19
|
' --rounds <n> 每个入口测速轮数,默认 3',
|
|
@@ -21,6 +21,6 @@ export function getHelpText() {
|
|
|
21
21
|
' --help 显示帮助',
|
|
22
22
|
' --version 显示版本',
|
|
23
23
|
'',
|
|
24
|
-
'
|
|
24
|
+
'说明:测速会请求 GET /api/health 和流式 POST /v1/responses。入口之间并行测速,写新配置前会先备份旧配置。'
|
|
25
25
|
].join('\n');
|
|
26
26
|
}
|