@uwa4d/openapi-mcp 0.2.0-beta.7 → 0.2.0-beta.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 +69 -6
- package/dist/annotate-dashboard.d.ts +48 -0
- package/dist/annotate-dashboard.js +413 -0
- package/dist/client.js +4 -13
- package/dist/composite/report-diagnosis.js +178 -6
- package/dist/error-hints.d.ts +13 -0
- package/dist/error-hints.js +55 -0
- package/dist/indicator-dashboard-keys.d.ts +7 -1
- package/dist/indicator-dashboard-keys.js +20 -0
- package/dist/presets.js +5 -0
- package/dist/server-instructions.d.ts +5 -0
- package/dist/server-instructions.js +16 -0
- package/dist/server.js +2 -1
- package/dist/tools.d.ts +5 -0
- package/dist/tools.js +192 -36
- package/dist/version-guide.d.ts +5 -0
- package/dist/version-guide.js +82 -4
- package/package.json +1 -1
- package/spec/uwa-openapi.json +1 -1
package/dist/tools.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { UwaApiError } from './client.js';
|
|
3
3
|
import { INDICATOR_DASHBOARD_PANEL_GUIDE, versionGuideFor } from './version-guide.js';
|
|
4
|
-
import {
|
|
4
|
+
import { partitionIndicatorDashboards, INDICATOR_DASHBOARD_KEYS, INDICATOR_DASHBOARD_NEGATIVE_EXAMPLES, } from './indicator-dashboard-keys.js';
|
|
5
|
+
import { annotateStatisticDashboard, annotateOverviewStatisticPss, extractCurveFilterArgs, filterCurveData, hasCurveFilter, isIndicatorCurveDashboard, isIndicatorStatisticDashboard, isOverviewStatisticOp, stutterRootCauseGate, stripOuterXAxisWhenFramesOnly, } from './annotate-dashboard.js';
|
|
5
6
|
const ENGINE_LABEL = { unity: 'Unity', unreal: 'UE' };
|
|
6
7
|
/**
|
|
7
8
|
* 预签名数据默认完整返回,不按条数截断——实测各接口解压后在 10 KB ~ 500 KB 之间,
|
|
@@ -86,6 +87,13 @@ function describe(op) {
|
|
|
86
87
|
}
|
|
87
88
|
return lines.join('\n');
|
|
88
89
|
}
|
|
90
|
+
const SERVICE_META_TYPE_ENUM = 'PA(真人真机)、GOT_ONLINE(GOT Online)、LT(本地资源检测)、AB(AssetBundle 检测)、ATP(自动化功能测试)、BR(Build 分析)';
|
|
91
|
+
/** 个别接口默认 apiVersion 与上游文档「推荐值」不一致时,在 MCP 侧纠偏。 */
|
|
92
|
+
function defaultApiVersionFor(op) {
|
|
93
|
+
if (op.id === 'gotonline_overview_method_group_statistic')
|
|
94
|
+
return 'v1.0.2';
|
|
95
|
+
return op.defaultApiVersion;
|
|
96
|
+
}
|
|
89
97
|
export function buildInputSchema(op) {
|
|
90
98
|
const shape = {};
|
|
91
99
|
for (const p of [...op.query, ...op.body]) {
|
|
@@ -95,30 +103,58 @@ export function buildInputSchema(op) {
|
|
|
95
103
|
desc = [
|
|
96
104
|
desc,
|
|
97
105
|
'传参名为面板标识符:后缀 _max/_avg 等为历史命名,不表示只能取峰值或均值。',
|
|
98
|
-
|
|
106
|
+
'每个面板一次返回全部统计量,勿按 mean/max/min 拆成多个面板名。',
|
|
107
|
+
`合法取值共 ${INDICATOR_DASHBOARD_KEYS.length} 个;可先调 gotonline_overview_indicator_dashboard_keys 拉全量列表(命名含 @ 等,不可从示例归纳)。`,
|
|
99
108
|
`常见误传(非面板名):${INDICATOR_DASHBOARD_NEGATIVE_EXAMPLES.join('、')}。`,
|
|
100
109
|
`示例面板名:${sampleKeys.join('、')}。`,
|
|
101
110
|
].join(' ');
|
|
102
111
|
}
|
|
112
|
+
if (p.name === 'serviceMetaTypeList') {
|
|
113
|
+
desc = `${desc.replace(/见下方枚举值/, '枚举如下').replace(/,$/, '')}。合法值:${SERVICE_META_TYPE_ENUM}。`;
|
|
114
|
+
}
|
|
115
|
+
if (p.name === 'stackMethodName') {
|
|
116
|
+
desc = `${desc} 响应含 _resolvedMethod 时请核对实际命中的 stackMethodId(同名会自动取耗时最大者)。`;
|
|
117
|
+
}
|
|
103
118
|
const base = zodFor(p).describe(p.example ? `${desc}(示例:${p.example})` : desc);
|
|
104
119
|
shape[p.name] = p.required ? base : base.optional();
|
|
105
120
|
}
|
|
106
121
|
if (op.apiVersions.length > 1) {
|
|
122
|
+
const def = defaultApiVersionFor(op);
|
|
107
123
|
shape['apiVersion'] = z
|
|
108
124
|
.enum(op.apiVersions)
|
|
109
125
|
.optional()
|
|
110
|
-
.describe(`接口版本,默认 ${
|
|
126
|
+
.describe(`接口版本,默认 ${def}。不同版本的返回字段有差异,详见文档`);
|
|
111
127
|
}
|
|
112
128
|
if (op.returnsPresignUrl) {
|
|
129
|
+
const isHeavyTree = op.id.includes('stack_overall_tree') ||
|
|
130
|
+
op.id.includes('stack_stutter') ||
|
|
131
|
+
op.id.includes('stack_tree');
|
|
113
132
|
shape['download'] = z
|
|
114
133
|
.boolean()
|
|
115
134
|
.optional()
|
|
116
|
-
.describe(
|
|
135
|
+
.describe(isHeavyTree
|
|
136
|
+
? '是否自动下载并解析预签名数据,默认 true。大报告堆栈树建议 download=false,只取 dataPresignUrl 自行处理,避免撑爆上下文'
|
|
137
|
+
: '是否自动下载并解析预签名数据,默认 true。设为 false 时只返回 dataPresignUrl');
|
|
117
138
|
shape['maxRows'] = z
|
|
118
139
|
.number()
|
|
119
140
|
.optional()
|
|
120
141
|
.describe('可选的条目数上限。默认返回全部数据,只有在明确只需要前 N 条时才传这个参数');
|
|
121
142
|
}
|
|
143
|
+
if (isIndicatorCurveDashboard(op.path)) {
|
|
144
|
+
shape['valueGt'] = z
|
|
145
|
+
.number()
|
|
146
|
+
.optional()
|
|
147
|
+
.describe('MCP 侧过滤:只保留数值 > 该阈值的曲线点(例:帧耗时慢帧传 40,单位与曲线一致多为 ms)');
|
|
148
|
+
shape['valueLt'] = z.number().optional().describe('MCP 侧过滤:只保留数值 < 该阈值的曲线点');
|
|
149
|
+
shape['topN'] = z
|
|
150
|
+
.number()
|
|
151
|
+
.optional()
|
|
152
|
+
.describe('MCP 侧过滤:在阈值过滤后按数值降序只保留最大的 N 个点');
|
|
153
|
+
shape['returnFramesOnly'] = z
|
|
154
|
+
.boolean()
|
|
155
|
+
.optional()
|
|
156
|
+
.describe('MCP 侧:为 true 时每条曲线只返回 frames[] + values[],进一步压缩体积');
|
|
157
|
+
}
|
|
122
158
|
return shape;
|
|
123
159
|
}
|
|
124
160
|
/** 找出对象里最长的那个数组字段,它通常就是主数据列表。 */
|
|
@@ -278,7 +314,7 @@ export function annotateDashboardEmptyResult(op, data, args) {
|
|
|
278
314
|
const emptyColumns = !Array.isArray(columns) || columns.length === 0;
|
|
279
315
|
if (!emptyStat && !emptyColumns)
|
|
280
316
|
return data;
|
|
281
|
-
const unknown =
|
|
317
|
+
const { unknown } = partitionIndicatorDashboards(requested);
|
|
282
318
|
const hintParts = [
|
|
283
319
|
'indicatorDashboards 可能传错,导致 statistic/columns 为空。',
|
|
284
320
|
'面板传参名≠统计维度后缀(如 fps_mean、frametime_min 是返回字段,不是面板名)。',
|
|
@@ -295,6 +331,41 @@ export function annotateDashboardEmptyResult(op, data, args) {
|
|
|
295
331
|
};
|
|
296
332
|
}
|
|
297
333
|
const RECORD_ARRAY_KEYS = /^gotOnline.*Records$/;
|
|
334
|
+
function hasNonEmptyRecords(v) {
|
|
335
|
+
return Array.isArray(v) && v.length > 0;
|
|
336
|
+
}
|
|
337
|
+
/** 各 GOT Online 子模块是否有报告;供模型「无 GPU 停手」。 */
|
|
338
|
+
export function buildModuleAvailable(group) {
|
|
339
|
+
return {
|
|
340
|
+
overview: hasNonEmptyRecords(group['gotOnlineOverviewRecords']),
|
|
341
|
+
gpu: hasNonEmptyRecords(group['gotOnlineGPURecords']),
|
|
342
|
+
mono: hasNonEmptyRecords(group['gotOnlineMonoRecords']),
|
|
343
|
+
lua: hasNonEmptyRecords(group['gotOnlineLuaRecords']),
|
|
344
|
+
resource: hasNonEmptyRecords(group['gotOnlineResourceRecords']),
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
export function annotateModuleAvailable(data) {
|
|
348
|
+
if (!isObj(data) || !Array.isArray(data['content']))
|
|
349
|
+
return data;
|
|
350
|
+
const content = data['content'].map((g) => {
|
|
351
|
+
if (!isObj(g))
|
|
352
|
+
return g;
|
|
353
|
+
const module_available = buildModuleAvailable(g);
|
|
354
|
+
const missing = Object.entries(module_available)
|
|
355
|
+
.filter(([, ok]) => !ok)
|
|
356
|
+
.map(([k]) => k);
|
|
357
|
+
return {
|
|
358
|
+
...g,
|
|
359
|
+
module_available,
|
|
360
|
+
...(missing.length
|
|
361
|
+
? {
|
|
362
|
+
_moduleHint: `本项目组缺少模块:${missing.join('、')}。module_available 为 false 的模块请停手,禁止用 Overview 数据冒充分析。`,
|
|
363
|
+
}
|
|
364
|
+
: {}),
|
|
365
|
+
};
|
|
366
|
+
});
|
|
367
|
+
return { ...data, content };
|
|
368
|
+
}
|
|
298
369
|
/**
|
|
299
370
|
* 报告列表接口一次返回、按项目组分组。但上游会把同一份报告挂到每个项目组下
|
|
300
371
|
* (沙箱实测多组两两交集 100%),直接按组统计会成倍放大。
|
|
@@ -304,10 +375,10 @@ const RECORD_ARRAY_KEYS = /^gotOnline.*Records$/;
|
|
|
304
375
|
*/
|
|
305
376
|
export function annotateProjectGroups(data) {
|
|
306
377
|
if (!isObj(data) || !Array.isArray(data['content']))
|
|
307
|
-
return data;
|
|
378
|
+
return annotateModuleAvailable(data);
|
|
308
379
|
const groups = data['content'].filter(isObj);
|
|
309
380
|
if (groups.length < 2)
|
|
310
|
-
return data;
|
|
381
|
+
return annotateModuleAvailable(data);
|
|
311
382
|
// dataKey → 出现过的项目组;以及「该报告在哪个数组字段里、原文是什么」
|
|
312
383
|
const groupsOf = new Map();
|
|
313
384
|
const firstSeen = new Map();
|
|
@@ -336,7 +407,7 @@ export function annotateProjectGroups(data) {
|
|
|
336
407
|
}
|
|
337
408
|
const duplicated = [...groupsOf.values()].some((gs) => gs.size > 1);
|
|
338
409
|
if (!duplicated)
|
|
339
|
-
return data;
|
|
410
|
+
return annotateModuleAvailable(data);
|
|
340
411
|
// 已知项目组壳子(保留名称、engine 等元信息),报告重新按真实归属灌入
|
|
341
412
|
const shells = new Map();
|
|
342
413
|
for (const g of groups) {
|
|
@@ -371,15 +442,15 @@ export function annotateProjectGroups(data) {
|
|
|
371
442
|
}
|
|
372
443
|
// 丢掉合并后一个报告都没有的空组(它们原先只有别人的副本)
|
|
373
444
|
const content = [...shells.values()].filter((g) => Object.entries(g).some(([k, v]) => RECORD_ARRAY_KEYS.test(k) && Array.isArray(v) && v.length > 0));
|
|
374
|
-
return {
|
|
445
|
+
return annotateModuleAvailable({
|
|
375
446
|
...data,
|
|
376
447
|
content,
|
|
377
448
|
_deduplicated: true,
|
|
378
449
|
_uniqueReportCount: placed,
|
|
379
|
-
_dedupeNote: `上游把同一份报告重复挂在多个项目组下,已按 link 中 project=
|
|
450
|
+
_dedupeNote: `上游把同一份报告重复挂在多个项目组下,已按 link 中 project= 合并归位,去重后本页 ${placed} 份` +
|
|
380
451
|
(orphaned ? `(其中 ${orphaned} 份所属项目组不在本页,已单独列出)` : '') +
|
|
381
|
-
'
|
|
382
|
-
};
|
|
452
|
+
'。按项目组统计请用本页 _uniqueReportCount / 各组 Records;跨页汇总须自行按 dataKey 去重,勿把各页条数简单相加。',
|
|
453
|
+
});
|
|
383
454
|
}
|
|
384
455
|
/** MCP 的 content.text 必须是字符串,undefined 会让客户端校验失败。 */
|
|
385
456
|
function asText(value) {
|
|
@@ -389,44 +460,128 @@ function asText(value) {
|
|
|
389
460
|
return '(接口无返回内容)';
|
|
390
461
|
return JSON.stringify(value, null, 2);
|
|
391
462
|
}
|
|
463
|
+
/** 空 Bound / 空日志列表等:补可教模型的语义,避免误判。 */
|
|
464
|
+
export function annotateEmptyStateSemantics(op, data) {
|
|
465
|
+
if (op.id === 'gotonline_gpu_bound') {
|
|
466
|
+
const intervals = Array.isArray(data) ? data : isObj(data) && Array.isArray(data['data']) ? data['data'] : null;
|
|
467
|
+
if (intervals && intervals.length === 0) {
|
|
468
|
+
const base = isObj(data) ? data : { data: intervals };
|
|
469
|
+
return {
|
|
470
|
+
...base,
|
|
471
|
+
gpuBound: [],
|
|
472
|
+
forbidden_claims: ['GPU是主要瓶颈', 'GPU Bound明显', 'GPU主瓶颈'],
|
|
473
|
+
_hint: 'GPU Bound 为空数组:仅表示在当前 targetFps 下未检出 Bound 区间,不等于 GPU 完全无压力;禁止据此断言 GPU 是主要瓶颈。',
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (op.id === 'gotonline_overview_log_export') {
|
|
478
|
+
const empty = (Array.isArray(data) && data.length === 0) ||
|
|
479
|
+
(isObj(data) && Array.isArray(data['data']) && data['data'].length === 0);
|
|
480
|
+
if (empty) {
|
|
481
|
+
const base = isObj(data) ? data : { entries: data };
|
|
482
|
+
return {
|
|
483
|
+
...base,
|
|
484
|
+
_hint: '运行日志 1.0 返回空列表:可能是真无条目,也可能是新报告应改用 gotonline_lg_runtime_log_entries_presign(日志 2.0)。禁止说成「一定没有日志」。',
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
if (op.id === 'gotonline_overview_event_lower_memory_frames') {
|
|
489
|
+
const empty = (Array.isArray(data) && data.length === 0) ||
|
|
490
|
+
(isObj(data) && Array.isArray(data['data']) && data['data'].length === 0);
|
|
491
|
+
if (empty) {
|
|
492
|
+
const base = isObj(data) ? data : { frames: data };
|
|
493
|
+
return {
|
|
494
|
+
...base,
|
|
495
|
+
_hint: '未触发 Low Memory 事件(空数组)。不能推出内存完全没问题,请结合 PSS 峰值等综合判断。',
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return data;
|
|
500
|
+
}
|
|
392
501
|
export function makeHandler(op, client, defaultMaxRows, maxChars) {
|
|
393
502
|
return async (args) => {
|
|
394
503
|
try {
|
|
395
|
-
const apiVersion = args['apiVersion'] ?? op
|
|
504
|
+
const apiVersion = args['apiVersion'] ?? defaultApiVersionFor(op);
|
|
396
505
|
const wantDownload = args['download'] ?? true;
|
|
397
506
|
const maxRows = args['maxRows'] ?? defaultMaxRows;
|
|
507
|
+
const { filter: curveFilter, cleaned: argsForApi } = isIndicatorCurveDashboard(op.path)
|
|
508
|
+
? extractCurveFilterArgs(args)
|
|
509
|
+
: { filter: {}, cleaned: args };
|
|
510
|
+
let acceptedDashboards = [];
|
|
511
|
+
let unknownDashboards = [];
|
|
512
|
+
if (typeof argsForApi['indicatorDashboards'] === 'string') {
|
|
513
|
+
const part = partitionIndicatorDashboards(argsForApi['indicatorDashboards']);
|
|
514
|
+
acceptedDashboards = part.accepted;
|
|
515
|
+
unknownDashboards = part.unknown;
|
|
516
|
+
if (part.all.length && !acceptedDashboards.length) {
|
|
517
|
+
return {
|
|
518
|
+
content: [
|
|
519
|
+
{
|
|
520
|
+
type: 'text',
|
|
521
|
+
text: asText({
|
|
522
|
+
error: 'PARAMETERS_WRONG',
|
|
523
|
+
message: `indicatorDashboards 全部无法识别:${unknownDashboards.join('、')}`,
|
|
524
|
+
_unknownDashboards: unknownDashboards,
|
|
525
|
+
_acceptedDashboards: [],
|
|
526
|
+
_hint: `这些是面板标识符,不是统计维度后缀。` +
|
|
527
|
+
`误传示例:${INDICATOR_DASHBOARD_NEGATIVE_EXAMPLES.join('、')};` +
|
|
528
|
+
`合法示例:fps_avg、frametime@gt_40_pct。` +
|
|
529
|
+
`请调 gotonline_overview_indicator_dashboard_keys 获取全量 ${INDICATOR_DASHBOARD_KEYS.length} 个合法值。`,
|
|
530
|
+
}),
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
isError: true,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (acceptedDashboards.length) {
|
|
537
|
+
argsForApi['indicatorDashboards'] = acceptedDashboards.join(',');
|
|
538
|
+
}
|
|
539
|
+
}
|
|
398
540
|
const query = {};
|
|
399
541
|
for (const p of op.query)
|
|
400
|
-
if (
|
|
401
|
-
query[p.name] =
|
|
542
|
+
if (argsForApi[p.name] !== undefined)
|
|
543
|
+
query[p.name] = argsForApi[p.name];
|
|
402
544
|
let body;
|
|
403
545
|
if (op.body.length) {
|
|
404
546
|
body = {};
|
|
405
547
|
for (const p of op.body)
|
|
406
|
-
if (
|
|
407
|
-
body[p.name] =
|
|
548
|
+
if (argsForApi[p.name] !== undefined)
|
|
549
|
+
body[p.name] = argsForApi[p.name];
|
|
408
550
|
}
|
|
409
551
|
const raw = await client.call(op.method, op.path, apiVersion, query, body);
|
|
410
|
-
|
|
411
|
-
if (
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}),
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
552
|
+
let processed = annotateProjectGroups(annotateCurveAxes(raw));
|
|
553
|
+
if (isIndicatorStatisticDashboard(op.path) && acceptedDashboards.length) {
|
|
554
|
+
processed = annotateStatisticDashboard(processed, acceptedDashboards, unknownDashboards);
|
|
555
|
+
}
|
|
556
|
+
else if (isOverviewStatisticOp(op.id, op.path)) {
|
|
557
|
+
processed = annotateOverviewStatisticPss(processed);
|
|
558
|
+
}
|
|
559
|
+
else if (isIndicatorCurveDashboard(op.path)) {
|
|
560
|
+
if (acceptedDashboards.length || unknownDashboards.length) {
|
|
561
|
+
processed = isObj(processed)
|
|
562
|
+
? {
|
|
563
|
+
...processed,
|
|
564
|
+
_acceptedDashboards: acceptedDashboards,
|
|
565
|
+
...(unknownDashboards.length ? { _unknownDashboards: unknownDashboards } : {}),
|
|
566
|
+
}
|
|
567
|
+
: processed;
|
|
568
|
+
}
|
|
569
|
+
if (hasCurveFilter(curveFilter)) {
|
|
570
|
+
processed = filterCurveData(processed, curveFilter);
|
|
571
|
+
if (curveFilter.returnFramesOnly) {
|
|
572
|
+
processed = stripOuterXAxisWhenFramesOnly(processed, curveFilter);
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
processed = annotateCurveAxes(processed);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
let data = annotateDashboardEmptyResult(op, processed, args);
|
|
580
|
+
data = annotateEmptyStateSemantics(op, data);
|
|
581
|
+
const stutterGate = stutterRootCauseGate(op.id);
|
|
582
|
+
if (stutterGate && isObj(data)) {
|
|
583
|
+
data = { ...data, ...stutterGate };
|
|
428
584
|
}
|
|
429
|
-
const data = annotateDashboardEmptyResult(op, annotateProjectGroups(annotateCurveAxes(raw)), args);
|
|
430
585
|
const presignUrl = op.returnsPresignUrl && data && typeof data === 'object'
|
|
431
586
|
? data['dataPresignUrl']
|
|
432
587
|
: undefined;
|
|
@@ -464,6 +619,7 @@ export function makeHandler(op, client, defaultMaxRows, maxChars) {
|
|
|
464
619
|
}
|
|
465
620
|
const meta = {
|
|
466
621
|
...data,
|
|
622
|
+
...(stutterRootCauseGate(op.id) ?? {}),
|
|
467
623
|
_downloaded: true,
|
|
468
624
|
_format: payload.kind,
|
|
469
625
|
...(total !== undefined ? { _totalItems: total } : {}),
|
package/dist/version-guide.d.ts
CHANGED
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
* 挂到所有带 indicatorDashboards 的工具 description,避免模型见 _max 只读峰值。
|
|
10
10
|
*/
|
|
11
11
|
export declare const INDICATOR_DASHBOARD_PANEL_GUIDE: string;
|
|
12
|
+
/**
|
|
13
|
+
* 堆栈 / 卡顿工具路由。卡顿树已是「多帧合并后的根因视图」,
|
|
14
|
+
* 禁止误导成「对每个 >40ms 帧循环拉指定帧树再聚类」。
|
|
15
|
+
*/
|
|
16
|
+
export declare const STACK_ANALYSIS_GUIDE: string;
|
|
12
17
|
/** 写入工具 description 的「选用规则」块,key 为 operation id。 */
|
|
13
18
|
export declare const VERSION_GUIDE: Record<string, string>;
|
|
14
19
|
export declare function versionGuideFor(opId: string): string | undefined;
|
package/dist/version-guide.js
CHANGED
|
@@ -11,14 +11,35 @@
|
|
|
11
11
|
export const INDICATOR_DASHBOARD_PANEL_GUIDE = [
|
|
12
12
|
'【indicatorDashboards 传参名称——勿按后缀猜统计维度】',
|
|
13
13
|
'- 传参名(如 temperature_max、fps_avg、drawcall_cnt_max)是**面板标识符**,_max / _avg / @max 等为历史命名,**不限制**返回 mean / maximum / min 等统计类型。',
|
|
14
|
+
'- 【返回粒度】每个面板一次返回其下所有子指标的全部统计量(mean / min / maximum / maximum_frame / 专属指标,以及 scene_*),',
|
|
15
|
+
' 无需也无法按统计量拆成多个面板名(勿传 fps_mean、fps_max、fps_min)。面板→子指标展开见返回体 group;入参→出参映射见 _paramFieldMap。',
|
|
14
16
|
'- 例:temperature_max 面板含子指标 temperature、android_temper_cpu、android_temper_gpu、android_temper_battery;',
|
|
15
17
|
' · 曲线接口(indicator_curve / custom_dashboard)→ 各子指标逐帧曲线;',
|
|
16
18
|
' · 统计 2.0(indicator_statistic_dashboard)→ statistic 下 *_mean / *_maximum / *_min 及 scene_* 等全量统计。',
|
|
17
19
|
'- 查温度曲线/均值/峰值:Unity Overview 且 SDK ≥ 2.5.1 时传 indicatorDashboards=temperature_max,再读 y_axis 或 statistic;勿仅看 get_overview_statistic 的汇总段。',
|
|
18
|
-
'- 合法面板名先调 gotonline_overview_indicator_dashboard_keys
|
|
19
|
-
'- 误传示例:fps_mean、frametime_gt_40_pct(缺 @)、frametime_min;合法示例:fps_avg、frametime@gt_40_pct。',
|
|
20
|
+
'- 合法面板名先调 gotonline_overview_indicator_dashboard_keys(全量枚举);勿传 fps_mean、frametime_min、android_pss_max 等统计字段名。',
|
|
21
|
+
'- 误传示例:fps_mean、android_pss_max、frametime_gt_40_pct(缺 @)、frametime_min;合法示例:fps_avg、android_memory_pss@max、frametime@gt_40_pct。',
|
|
22
|
+
'- Android PSS 峰值:只认面板 android_memory_pss@max;优先读返回顶层 pss_peak_kb / pss_peak_mb / pss_peak_frame(单位 KB)。',
|
|
20
23
|
'- UE Overview 当前 Open API 未开放 custom/indicator dashboard 路径(会 24052),UE 温度汇总见 get_ue_overview_statistic_v2 的 temperature 段。',
|
|
21
24
|
].join('\n');
|
|
25
|
+
/**
|
|
26
|
+
* 堆栈 / 卡顿工具路由。卡顿树已是「多帧合并后的根因视图」,
|
|
27
|
+
* 禁止误导成「对每个 >40ms 帧循环拉指定帧树再聚类」。
|
|
28
|
+
*/
|
|
29
|
+
export const STACK_ANALYSIS_GUIDE = [
|
|
30
|
+
'【堆栈/卡顿选用——务必先看】',
|
|
31
|
+
'- 卡顿根因主路径:用**卡顿聚合树**(已合并全部/场景内卡顿帧),不要对每个慢帧循环拉指定帧树。',
|
|
32
|
+
'- 工具分工:',
|
|
33
|
+
' · gotonline_overview_stack_stutter_full_presign → 卡顿帧列表(哪些帧卡、类型耗时拆分)',
|
|
34
|
+
' · gotonline_overview_stack_stutter_full_tree_presign → **全部卡顿帧合并后的堆栈树**(全报告卡顿共性根因)',
|
|
35
|
+
' · gotonline_overview_stack_stutter_scene_tree_presign → **指定场景内卡顿帧合并树**(主玩法段)',
|
|
36
|
+
' · gotonline_overview_stack_tree_frame_presign → **单帧**完整树(仅尖峰深挖,如已知 frameId=8439)',
|
|
37
|
+
' · gotonline_overview_stack_overall_tree_presign → 整线程全量堆栈(非卡顿专用;看持续税/全局热点)',
|
|
38
|
+
' · gotonline_overview_stack_sample_frame_presign → 树结点逐帧曲线',
|
|
39
|
+
'- 慢帧占比/挑代表帧:indicator 面板 frametime@gt_40_pct(统计或 curve+valueGt);根因归因仍走卡顿聚合树。',
|
|
40
|
+
'- 读任何堆栈树前必须配合 gotonline_overview_stack_id_map(树里是 methodId)。',
|
|
41
|
+
'- 错误假设:❌「没有批量帧树就无法做卡顿根因」——总体/场景卡顿树就是合并结果。',
|
|
42
|
+
].join('\n');
|
|
22
43
|
/** 写入工具 description 的「选用规则」块,key 为 operation id。 */
|
|
23
44
|
export const VERSION_GUIDE = {
|
|
24
45
|
get_overview_statistic_v1: [
|
|
@@ -61,7 +82,8 @@ export const VERSION_GUIDE = {
|
|
|
61
82
|
'- 自定义面板统计 2.0。需 SDK ≥ 2.5.1,支持 GPU 指标,最多 15 个面板。',
|
|
62
83
|
'- 旧接口(抽帧、无 GPU)见 gotonline_overview_custom_dashboard。',
|
|
63
84
|
'- 只要逐帧曲线不要统计值时用 gotonline_overview_indicator_curve_dashboard。',
|
|
64
|
-
'- 返回 statistic 为空时优先怀疑 indicatorDashboards 传错;响应可能含 _hint。',
|
|
85
|
+
'- 返回 statistic 为空时优先怀疑 indicatorDashboards 传错;响应可能含 _hint / _unknownDashboards。',
|
|
86
|
+
'- 响应可能含 _paramFieldMap(入参面板→出参字段)、_duplicateFlatAliases、_sceneUnitNote(scene_* 补单位标注)。',
|
|
65
87
|
INDICATOR_DASHBOARD_PANEL_GUIDE,
|
|
66
88
|
].join('\n'),
|
|
67
89
|
gotonline_overview_indicator_curve_dashboard: [
|
|
@@ -71,6 +93,8 @@ export const VERSION_GUIDE = {
|
|
|
71
93
|
'- 【横轴——勿混用】外层 x_axis 为逐帧共享横轴(1、2、3…);y_axis.{指标名}.x_axis 为该曲线专属横轴(常见步长 30:0、30、60…)。',
|
|
72
94
|
'- 读取某条曲线时:若存在专属 x_axis 必须用专属,否则才用外层共享 x_axis。把共享横轴套到 FPS 等专属曲线会错位。',
|
|
73
95
|
'- 响应另含 _axisBinding / _axisHint,按其中 xAxisSource 取值即可。',
|
|
96
|
+
'- 【过滤——MCP 侧】可选 valueGt / valueLt / topN / returnFramesOnly:只返回超阈值或 TopN 点,适合慢帧列表(例:frametime@gt_40_pct + valueGt=40)。',
|
|
97
|
+
'- 慢帧列表 ≠ 卡顿根因:根因请用 stutter_full_tree / stutter_scene_tree(卡顿帧已合并),不要对 valueGt 结果逐帧拉 stack_tree_frame。',
|
|
74
98
|
INDICATOR_DASHBOARD_PANEL_GUIDE,
|
|
75
99
|
].join('\n'),
|
|
76
100
|
gotonline_gpu_curve: [
|
|
@@ -92,11 +116,19 @@ export const VERSION_GUIDE = {
|
|
|
92
116
|
'【选用规则——务必先看】',
|
|
93
117
|
'- 运行日志 1.0。Unity:解析日 < 2026-07-09;UE:提交日 ≤ 2026-03-25。',
|
|
94
118
|
'- 新报告请用 gotonline_lg_runtime_log_entries_presign(运行日志 2.0)。',
|
|
119
|
+
'- 空列表 ≠「一定没有日志」:新报告误用本接口也可能返回空,应改调 2.0。',
|
|
95
120
|
].join('\n'),
|
|
96
121
|
gotonline_lg_runtime_log_entries_presign: [
|
|
97
122
|
'【选用规则——务必先看】',
|
|
98
123
|
'- 运行日志 2.0(预签名)。解析日 ≥ 2026-07-09 且 SDK ≥ 2.5.1。',
|
|
99
124
|
'- 更早的报告请用 gotonline_overview_log_export。',
|
|
125
|
+
'- 若返回 80108 FILE_META_NOT_EXIST:Unity = 无 LG meta / 未解析 = 无可用日志数据;UE = 方案切换过,只能说「暂不支持获取当前报告的运行日志」,勿断定报告本身无日志。',
|
|
126
|
+
].join('\n'),
|
|
127
|
+
gotonline_gpu_bound: [
|
|
128
|
+
'【选用规则——务必先看】',
|
|
129
|
+
'- 说「GPU Bound / GPU 主瓶颈」前必须先调本工具。',
|
|
130
|
+
'- 返回 [] 只能说「未检出 Bound」,禁止断言 GPU 是主要瓶颈。',
|
|
131
|
+
'- Overview 上的 WaitForVsync / RenderTexture 不能替代本接口。',
|
|
100
132
|
].join('\n'),
|
|
101
133
|
gotonline_overview_group_export: [
|
|
102
134
|
'【选用规则——务必先看】',
|
|
@@ -105,8 +137,10 @@ export const VERSION_GUIDE = {
|
|
|
105
137
|
].join('\n'),
|
|
106
138
|
gotonline_overview_method_group_statistic: [
|
|
107
139
|
'【选用规则——务必先看】',
|
|
108
|
-
'- 自定义函数组统计(Unity SDK ≥ 2.5.1;UE 提交日 > 2026-03-25
|
|
140
|
+
'- 自定义函数组统计(Unity SDK ≥ 2.5.1;UE 提交日 > 2026-03-25)。',
|
|
141
|
+
'- **MCP 默认 apiVersion=v1.0.2**(勿用 v1.0.1,会参数错误)。需要旧行为时显式传 apiVersion=v1.0.1。',
|
|
109
142
|
'- UE 旧报告请用 gotonline_overview_group_export。',
|
|
143
|
+
'- 未配置自定义函数组时 data=[];Overview 热点函数曲线优先用 method_idmap_statistic + method_curve_presign,不必依赖函数组。',
|
|
110
144
|
].join('\n'),
|
|
111
145
|
gotonline_overview_method_idmap_statistic: [
|
|
112
146
|
'【选用规则——务必先看】',
|
|
@@ -139,6 +173,50 @@ export const VERSION_GUIDE = {
|
|
|
139
173
|
'- 合法示例:fps_avg、drawcall_cnt_max、frametime@gt_40_pct、temperature_max。',
|
|
140
174
|
INDICATOR_DASHBOARD_PANEL_GUIDE,
|
|
141
175
|
].join('\n'),
|
|
176
|
+
gotonline_overview_stack_id_map: [
|
|
177
|
+
'【选用规则——务必先看】',
|
|
178
|
+
'- 函数 ID ↔ 名称映射。解析任何 stack_*_tree / stutter_*_tree 前先调本工具。',
|
|
179
|
+
STACK_ANALYSIS_GUIDE,
|
|
180
|
+
].join('\n'),
|
|
181
|
+
gotonline_overview_stack_overall_tree_presign: [
|
|
182
|
+
'【选用规则——务必先看】',
|
|
183
|
+
'- 指定线程的**全量**调用堆栈树(含非卡顿帧),适合持续税/全局热点。',
|
|
184
|
+
'- 只要卡顿根因:改用 stutter_full_tree / stutter_scene_tree,勿用本接口代替卡顿聚合。',
|
|
185
|
+
STACK_ANALYSIS_GUIDE,
|
|
186
|
+
].join('\n'),
|
|
187
|
+
gotonline_overview_stack_stutter_full_presign: [
|
|
188
|
+
'【选用规则——务必先看】',
|
|
189
|
+
'- 卡顿**帧列表**(帧号 + 卡顿类型耗时拆分),不是堆栈树。',
|
|
190
|
+
'- 要卡顿根因堆栈:接着调 stutter_full_tree_presign(或场景版 stutter_scene_tree)。',
|
|
191
|
+
STACK_ANALYSIS_GUIDE,
|
|
192
|
+
].join('\n'),
|
|
193
|
+
gotonline_overview_stack_stutter_full_tree_presign: [
|
|
194
|
+
'【选用规则——务必先看】',
|
|
195
|
+
'- **全部卡顿帧已合并**的调用堆栈树 = 卡顿根因主入口(服务端聚合,无需客户端逐帧拉树再聚类)。',
|
|
196
|
+
'- 禁止把 WaitForVsync / Gfx.WaitForPresent 写成卡顿根因或 GPU Bound 证据。',
|
|
197
|
+
'- 说「GPU 主瓶颈」前必须先查 gotonline_gpu_bound;空数组只能说「未检出 Bound」。',
|
|
198
|
+
'- 只要某一场景:用 stutter_scene_tree;只要某一尖峰帧细节:再用 stack_tree_frame。',
|
|
199
|
+
STACK_ANALYSIS_GUIDE,
|
|
200
|
+
].join('\n'),
|
|
201
|
+
gotonline_overview_stack_stutter_scene_tree_presign: [
|
|
202
|
+
'【选用规则——务必先看】',
|
|
203
|
+
'- **指定场景内卡顿帧已合并**的堆栈树;sceneIndex 来自 list_scenes 下标。',
|
|
204
|
+
'- 禁止把 WaitForVsync 当根因;GPU 主瓶颈结论须先查 gpu_bound。',
|
|
205
|
+
'- 全报告卡顿用 stutter_full_tree;单帧深挖用 stack_tree_frame。',
|
|
206
|
+
STACK_ANALYSIS_GUIDE,
|
|
207
|
+
].join('\n'),
|
|
208
|
+
gotonline_overview_stack_tree_frame_presign: [
|
|
209
|
+
'【选用规则——务必先看】',
|
|
210
|
+
'- **单帧**完整堆栈树(frameId 必填)。用于已知尖峰帧深挖,不是卡顿分析的第一步。',
|
|
211
|
+
'- 卡顿共性根因请先用 stutter_full_tree / stutter_scene_tree(已是多帧合并结果)。',
|
|
212
|
+
'- ❌ 不要对 frametime valueGt 筛出的每个慢帧循环调用本工具来「聚类」。',
|
|
213
|
+
STACK_ANALYSIS_GUIDE,
|
|
214
|
+
].join('\n'),
|
|
215
|
+
gotonline_overview_stack_sample_frame_presign: [
|
|
216
|
+
'【选用规则——务必先看】',
|
|
217
|
+
'- 堆栈树某个结点的逐帧曲线;结点 id 来自树文件。',
|
|
218
|
+
STACK_ANALYSIS_GUIDE,
|
|
219
|
+
].join('\n'),
|
|
142
220
|
};
|
|
143
221
|
export function versionGuideFor(opId) {
|
|
144
222
|
return VERSION_GUIDE[opId];
|
package/package.json
CHANGED