@lingjingai/lj-awb-cli-pre 0.3.17 → 0.3.18
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 +5 -5
- package/package.json +1 -1
- package/packages/awb-cli/README.md +0 -3
- package/packages/awb-cli/package.json +2 -2
- package/packages/awb-core/package.json +1 -1
- package/packages/awb-core/src/auth.js +1 -1
- package/packages/awb-core/src/commands.js +2 -2
- package/packages/awb-core/src/common.js +1 -1
- package/packages/awb-core/src/output.js +143 -77
- package/packages/awb-core/src/standalone.js +73 -11
- package/skills/lj-awb/SKILL.md +5 -2
- package/skills/lj-awb/VERSION +1 -1
- package/skills/lj-awb/compat.json +2 -2
- package/skills/lj-awb/modules/auth.md +1 -1
- package/skills/lj-awb/modules/create-contract.md +1 -1
- package/skills/lj-awb/modules/driver.md +12 -1
- package/skills/lj-awb/modules/model.md +10 -1
- package/skills/lj-awb/modules/task-manual.md +3 -2
- package/skills/lj-awb/references/error-codes.md +1 -1
- package/skills/lj-awb/references/model-options-read.md +8 -6
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ export LINGJING_AWB_API_ORIGIN=https://animeworkbench.lingjingai.cn
|
|
|
39
39
|
export LINGJING_AWB_ACCESS_KEY=<access_key>
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
`lj-awb` 只读取 `LINGJING_AWB_ACCESS_KEY` 作为环境变量 access key。
|
|
43
43
|
|
|
44
44
|
## 安装与运行
|
|
45
45
|
|
|
@@ -80,13 +80,13 @@ macOS 本地 wrapper `packages/awb-cli/bin/lj-awb` 会自动补充 Node 证书
|
|
|
80
80
|
保存 access key 到本地认证文件:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
lj-awb auth login
|
|
83
|
+
lj-awb auth login --access-key <access_key>
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
从环境变量读取并保存:
|
|
87
87
|
|
|
88
88
|
```bash
|
|
89
|
-
LINGJING_AWB_ACCESS_KEY=<access_key> lj-awb auth login
|
|
89
|
+
LINGJING_AWB_ACCESS_KEY=<access_key> lj-awb auth login
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
不落盘,直接让每次调用读取环境变量:
|
|
@@ -244,7 +244,7 @@ lj-awb create video \
|
|
|
244
244
|
|
|
245
245
|
## JSON 输出契约
|
|
246
246
|
|
|
247
|
-
默认输出是 compact text,适合终端和 Agent 低 token
|
|
247
|
+
默认输出是 compact text,适合终端和 Agent 低 token 读取;复杂模型命令使用 `section:` + 缩进 `key=value` 的分区格式,避免 `params`、`resources`、`intents`、`next` 混在一屏里。只有需要完整嵌套结构、稳定 JSON envelope 或脚本严格解析时才追加 `-f json` 或 `--json`。`schema` 通常使用 JSON;`model options` 和 `model create-spec` 默认 text 先读,必要时再切 JSON。
|
|
248
248
|
|
|
249
249
|
输出字段只保留后续动作不可替代的信息。默认不返回平台原始 `raw`、COS 签名细节、重复的首个结果字段;任务结果统一使用 `resultUrls` 表达。
|
|
250
250
|
|
|
@@ -270,7 +270,7 @@ lj-awb create video \
|
|
|
270
270
|
"error": {
|
|
271
271
|
"type": "auth_required",
|
|
272
272
|
"message": "缺少 access key",
|
|
273
|
-
"hint": "请设置 LINGJING_AWB_ACCESS_KEY
|
|
273
|
+
"hint": "请设置 LINGJING_AWB_ACCESS_KEY,或运行 lj-awb auth login。"
|
|
274
274
|
},
|
|
275
275
|
"meta": {
|
|
276
276
|
"command": "account info",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingjingai/lj-awb-cli-pre",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "Lingjing AWB CLI monorepo with shared core, standalone CLI, and agent skills (pre-release build pointing to https://animeworkbench-pre.lingjingai.cn)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lingjingai/awb-cli-bin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "Standalone CLI for Lingjing AWB",
|
|
5
5
|
"private": true,
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,6 +13,6 @@
|
|
|
13
13
|
"README.md"
|
|
14
14
|
],
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@lingjingai/awb-core": "0.3.
|
|
16
|
+
"@lingjingai/awb-core": "0.3.18"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
@@ -37,7 +37,7 @@ export async function resolveAuthContext({ required = true } = {}) {
|
|
|
37
37
|
throw new LingjingAwbCliError('缺少 access key', {
|
|
38
38
|
type: 'auth_required',
|
|
39
39
|
exitCode: 3,
|
|
40
|
-
hint: '请设置 LINGJING_AWB_ACCESS_KEY
|
|
40
|
+
hint: '请设置 LINGJING_AWB_ACCESS_KEY,或运行 lj-awb auth login。',
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
return {
|
|
@@ -169,7 +169,7 @@ export function registerAwbCommands(cli) {
|
|
|
169
169
|
'LINGJING_AWB_ACCESS_KEY=<key> lj-awb auth login',
|
|
170
170
|
'lj-awb auth login --access-key <key> --skip-verify',
|
|
171
171
|
],
|
|
172
|
-
hint: '不要写成 lj-awb auth <key>。不传 --access-key
|
|
172
|
+
hint: '不要写成 lj-awb auth <key>。不传 --access-key 时只读取 LINGJING_AWB_ACCESS_KEY 环境变量。',
|
|
173
173
|
}),
|
|
174
174
|
args: [
|
|
175
175
|
{ name: 'access-key', valueName: 'key', description: '平台 access key;不传时读取环境变量' },
|
|
@@ -178,7 +178,7 @@ export function registerAwbCommands(cli) {
|
|
|
178
178
|
],
|
|
179
179
|
func: async (_ctx, kwargs) => {
|
|
180
180
|
const skipVerify = toBool(kwargs.skipVerify);
|
|
181
|
-
const accessKey = trimSecret(kwargs.accessKey || process.env.LINGJING_AWB_ACCESS_KEY ||
|
|
181
|
+
const accessKey = trimSecret(kwargs.accessKey || process.env.LINGJING_AWB_ACCESS_KEY || '');
|
|
182
182
|
if (!accessKey) {
|
|
183
183
|
throw new LingjingAwbCliError('缺少 access key', {
|
|
184
184
|
type: 'argument_error',
|
|
@@ -14,7 +14,7 @@ export const API_ORIGIN = (
|
|
|
14
14
|
export const APP_HOME_DIR = process.env.LINGJING_AWB_STATE_DIR || process.env.ANIME_STATE_DIR || path.join(os.homedir(), '.lingjingai', 'awb');
|
|
15
15
|
export const AUTH_PATH = process.env.LINGJING_AWB_AUTH_PATH || process.env.ANIME_AUTH_PATH || path.join(APP_HOME_DIR, 'auth.json');
|
|
16
16
|
export const STATE_PATH = process.env.LINGJING_AWB_STATE_PATH || process.env.ANIME_STATE_PATH || path.join(APP_HOME_DIR, 'state.json');
|
|
17
|
-
export const ACCESS_KEY_ENV_NAMES = ['LINGJING_AWB_ACCESS_KEY'
|
|
17
|
+
export const ACCESS_KEY_ENV_NAMES = ['LINGJING_AWB_ACCESS_KEY'];
|
|
18
18
|
export const TASK_UPLOAD_SCENE = {
|
|
19
19
|
IMAGE_CREATE: 'material-image-draw',
|
|
20
20
|
IMAGE_EDIT: 'material-image-edit',
|
|
@@ -681,6 +681,42 @@ function renderRecord(record, preferredKeys = [], excludedKeys = []) {
|
|
|
681
681
|
return keys.map((key) => `${key}=${shortValue(record[key])}`).join(' ');
|
|
682
682
|
}
|
|
683
683
|
|
|
684
|
+
function recordScalarKeys(record, preferredKeys = [], excludedKeys = []) {
|
|
685
|
+
const excluded = new Set(excludedKeys);
|
|
686
|
+
return [
|
|
687
|
+
...preferredKeys.filter((key) => !excluded.has(key) && record[key] !== undefined),
|
|
688
|
+
...Object.keys(record).filter((key) => !excluded.has(key) && !preferredKeys.includes(key) && !Array.isArray(record[key]) && !isPlainObject(record[key])),
|
|
689
|
+
];
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function pushSectionRecord(lines, title, record, preferredKeys = []) {
|
|
693
|
+
const compacted = compactRecord(record);
|
|
694
|
+
lines.push(`${title}:`);
|
|
695
|
+
for (const key of recordScalarKeys(compacted, preferredKeys)) {
|
|
696
|
+
lines.push(` ${key}=${shortValue(compacted[key])}`);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function pushSectionCount(lines, title, count) {
|
|
701
|
+
lines.push(`${title}:`);
|
|
702
|
+
lines.push(` count=${count}`);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
function pushSectionKeyValue(lines, key, value) {
|
|
706
|
+
if (value === undefined || value === null || value === '') return;
|
|
707
|
+
lines.push(` ${key}=${value}`);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function pushSectionItem(lines, index, value) {
|
|
711
|
+
if (value === undefined || value === null || value === '') return;
|
|
712
|
+
lines.push(` [${index}]=${value}`);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
function pushSectionRecordItem(lines, index, record, preferredKeys = []) {
|
|
716
|
+
const rendered = renderRecord(compactRecord(record), preferredKeys);
|
|
717
|
+
if (rendered) lines.push(` [${index}]: ${rendered}`);
|
|
718
|
+
}
|
|
719
|
+
|
|
684
720
|
function firstList(data) {
|
|
685
721
|
for (const [key, value] of Object.entries(data || {})) {
|
|
686
722
|
if (['resultUrls', 'originUrls'].includes(key)) continue;
|
|
@@ -925,70 +961,71 @@ function formatModelOptionsOutput(data = {}) {
|
|
|
925
961
|
const resources = Array.isArray(data.resources) ? data.resources : [];
|
|
926
962
|
const constraints = Array.isArray(data.constraints) ? data.constraints : [];
|
|
927
963
|
const lines = ['ok model options'];
|
|
928
|
-
lines
|
|
964
|
+
pushSectionRecord(lines, 'summary', {
|
|
929
965
|
modelGroupCode: data.modelGroupCode,
|
|
930
966
|
taskKind: data.taskKind,
|
|
931
|
-
}
|
|
932
|
-
lines
|
|
967
|
+
}, ['modelGroupCode', 'taskKind']);
|
|
968
|
+
pushSectionCount(lines, 'params', params.length);
|
|
933
969
|
for (const [index, param] of params.entries()) {
|
|
934
|
-
lines
|
|
970
|
+
pushSectionRecordItem(lines, index, {
|
|
935
971
|
key: param.key,
|
|
936
972
|
type: param.valueType,
|
|
937
973
|
values: valueList(param.values),
|
|
938
974
|
default: param.defaultValue,
|
|
939
975
|
maxLength: param.maxLength,
|
|
940
976
|
required: param.required,
|
|
941
|
-
}
|
|
977
|
+
}, ['key', 'type', 'values', 'default', 'maxLength', 'required']);
|
|
942
978
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
])}`);
|
|
977
|
-
}
|
|
979
|
+
pushSectionCount(lines, 'resources', resources.length);
|
|
980
|
+
for (const [index, item] of resources.entries()) {
|
|
981
|
+
pushSectionRecordItem(lines, index, {
|
|
982
|
+
mode: item.mode,
|
|
983
|
+
media: item.mediaType,
|
|
984
|
+
usage: item.usage,
|
|
985
|
+
valueShapes: textValueShapes(item.valueShapes),
|
|
986
|
+
formats: textResourceFormats(item),
|
|
987
|
+
webpInput: textWebpInput(item),
|
|
988
|
+
autoConvert: textResourceAutoConvert(item),
|
|
989
|
+
files: textFileCount(item),
|
|
990
|
+
maxSizeKB: item.maxSizeKB,
|
|
991
|
+
durationMs: textDurationRange(item),
|
|
992
|
+
totalDurationMs: item.maxTotalDurationMs != null ? `<=${item.maxTotalDurationMs}` : undefined,
|
|
993
|
+
items: textItemCount(item),
|
|
994
|
+
maxPromptLength: item.maxPromptLength,
|
|
995
|
+
lastFrameOnly: item.supportLastFrameOnly,
|
|
996
|
+
}, [
|
|
997
|
+
'mode',
|
|
998
|
+
'media',
|
|
999
|
+
'usage',
|
|
1000
|
+
'valueShapes',
|
|
1001
|
+
'formats',
|
|
1002
|
+
'webpInput',
|
|
1003
|
+
'autoConvert',
|
|
1004
|
+
'files',
|
|
1005
|
+
'maxSizeKB',
|
|
1006
|
+
'durationMs',
|
|
1007
|
+
'totalDurationMs',
|
|
1008
|
+
'items',
|
|
1009
|
+
'maxPromptLength',
|
|
1010
|
+
'lastFrameOnly',
|
|
1011
|
+
]);
|
|
978
1012
|
}
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1013
|
+
pushSectionCount(lines, 'constraints', constraints.length);
|
|
1014
|
+
for (const [index, item] of constraints.entries()) {
|
|
1015
|
+
pushSectionRecordItem(lines, index, {
|
|
1016
|
+
target: item.target,
|
|
1017
|
+
targetConfig: item.targetConfigCode && item.targetConfigCode !== item.target ? item.targetConfigCode : undefined,
|
|
1018
|
+
allowedValues: textAllowValues(item.allowValues),
|
|
1019
|
+
when: textConstraintConditions(item.conditions),
|
|
1020
|
+
effect: item.effect,
|
|
1021
|
+
priority: item.priority,
|
|
1022
|
+
name: item.name,
|
|
1023
|
+
}, ['target', 'targetConfig', 'allowedValues', 'when', 'effect', 'priority', 'name']);
|
|
1024
|
+
}
|
|
1025
|
+
if (data.modelGroupCode) {
|
|
1026
|
+
lines.push('next:');
|
|
1027
|
+
pushSectionKeyValue(lines, 'createSpec', `${commandPrefix()} model create-spec --model-group-code ${data.modelGroupCode}`);
|
|
1028
|
+
pushSectionKeyValue(lines, 'reason', '读取创建命令写法、资源语法、fee/dry-run 前置步骤和示例');
|
|
992
1029
|
}
|
|
993
1030
|
return `${lines.join('\n')}\n`;
|
|
994
1031
|
}
|
|
@@ -1000,44 +1037,63 @@ function textKeyBinding(value) {
|
|
|
1000
1037
|
return value;
|
|
1001
1038
|
}
|
|
1002
1039
|
|
|
1040
|
+
function modelFeeNextCommand(data = {}) {
|
|
1041
|
+
if (!data.feeCommand || !data.modelGroupCode) return null;
|
|
1042
|
+
const args = [
|
|
1043
|
+
`${commandPrefix()} ${data.feeCommand}`,
|
|
1044
|
+
`--model-group-code ${data.modelGroupCode}`,
|
|
1045
|
+
'--prompt "<prompt>"',
|
|
1046
|
+
];
|
|
1047
|
+
if (data.taskKind === 'video') args.push('--duration <duration>', '--ratio <ratio>', '--quality <quality>');
|
|
1048
|
+
if (data.taskKind === 'image') args.push('--ratio <ratio>', '--quality <quality>');
|
|
1049
|
+
return args.join(' ');
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
function modelCreateExampleWithCode(example, modelGroupCode) {
|
|
1053
|
+
if (!example || !modelGroupCode) return rewriteCommandPrefix(example);
|
|
1054
|
+
return rewriteCommandPrefix(String(example).replaceAll('--model-group-code <code>', `--model-group-code ${modelGroupCode}`));
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1003
1057
|
function formatModelCreateSpecOutput(data = {}) {
|
|
1004
1058
|
const intents = Array.isArray(data.supportedIntents) ? data.supportedIntents : [];
|
|
1005
1059
|
const examples = Array.isArray(data.examples) ? data.examples : [];
|
|
1006
1060
|
const lines = ['ok model create-spec'];
|
|
1007
|
-
lines
|
|
1061
|
+
pushSectionRecord(lines, 'summary', {
|
|
1008
1062
|
modelGroupCode: data.modelGroupCode,
|
|
1009
1063
|
taskKind: data.taskKind,
|
|
1010
1064
|
create: data.createCommand,
|
|
1011
1065
|
fee: data.feeCommand,
|
|
1012
1066
|
statusTaskType: data.statusCommandTaskType,
|
|
1013
|
-
}
|
|
1014
|
-
lines
|
|
1015
|
-
|
|
1067
|
+
}, ['modelGroupCode', 'taskKind', 'create', 'fee', 'statusTaskType']);
|
|
1068
|
+
pushSectionRecord(lines, 'input', {
|
|
1069
|
+
summary: data.inputRequirement?.summary,
|
|
1016
1070
|
acceptedModes: valueList(data.inputRequirement?.acceptedModes),
|
|
1017
|
-
}
|
|
1018
|
-
lines
|
|
1071
|
+
}, ['summary', 'acceptedModes']);
|
|
1072
|
+
pushSectionCount(lines, 'intents', intents.length);
|
|
1019
1073
|
for (const [index, intent] of intents.entries()) {
|
|
1020
|
-
lines
|
|
1074
|
+
pushSectionRecordItem(lines, index, {
|
|
1021
1075
|
mode: intent.mode,
|
|
1022
1076
|
intent: intent.intent,
|
|
1023
1077
|
args: valueList(intent.requiredArgs),
|
|
1024
1078
|
usage: valueList(intent.resourceUsages),
|
|
1025
1079
|
resources: valueList(intent.resourceSyntax),
|
|
1026
1080
|
key: textKeyBinding(intent.promptBinding),
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
if (examples.length) {
|
|
1030
|
-
lines.push(`examples.count=${examples.length}`);
|
|
1031
|
-
for (const [index, example] of examples.entries()) lines.push(`example[${index}]=${example}`);
|
|
1081
|
+
}, ['mode', 'intent', 'args', 'usage', 'resources', 'key']);
|
|
1032
1082
|
}
|
|
1033
|
-
|
|
1083
|
+
pushSectionCount(lines, 'examples', examples.length);
|
|
1084
|
+
for (const [index, example] of examples.entries()) pushSectionItem(lines, index, rewriteCommandPrefix(example));
|
|
1085
|
+
lines.push('next:');
|
|
1086
|
+
if (data.optionsCommand) pushSectionKeyValue(lines, 'options', `${commandPrefix()} ${data.optionsCommand}`);
|
|
1087
|
+
const feeNext = modelFeeNextCommand(data);
|
|
1088
|
+
if (feeNext) pushSectionKeyValue(lines, 'fee', feeNext);
|
|
1089
|
+
if (examples.length) pushSectionKeyValue(lines, 'dryRun', modelCreateExampleWithCode(examples[0], data.modelGroupCode));
|
|
1034
1090
|
return `${lines.join('\n')}\n`;
|
|
1035
1091
|
}
|
|
1036
1092
|
|
|
1037
1093
|
function formatGuideTableRows(rows = []) {
|
|
1038
1094
|
return (Array.isArray(rows) ? rows : []).map((item, index) => {
|
|
1039
1095
|
const values = valueList(item.values);
|
|
1040
|
-
return `
|
|
1096
|
+
return ` [${index}]: ${renderRecord(compactRecord({
|
|
1041
1097
|
name: item.field,
|
|
1042
1098
|
values,
|
|
1043
1099
|
desc: item.description,
|
|
@@ -1047,20 +1103,20 @@ function formatGuideTableRows(rows = []) {
|
|
|
1047
1103
|
|
|
1048
1104
|
function formatModelInputGuideOutput(data = {}) {
|
|
1049
1105
|
const lines = ['ok model input-guide'];
|
|
1050
|
-
lines
|
|
1106
|
+
pushSectionCount(lines, 'commonFields', Array.isArray(data.commonFields) ? data.commonFields.length : 0);
|
|
1051
1107
|
lines.push(...formatGuideTableRows(data.commonFields));
|
|
1052
|
-
lines
|
|
1108
|
+
pushSectionCount(lines, 'resourceFields', Array.isArray(data.resourceFields) ? data.resourceFields.length : 0);
|
|
1053
1109
|
lines.push(...formatGuideTableRows(data.resourceFields));
|
|
1054
1110
|
const rules = Array.isArray(data.resourceRules) ? data.resourceRules : [];
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
for (const [index, rule] of rules.entries()) lines.push(`rule[${index}]=${rule}`);
|
|
1058
|
-
}
|
|
1111
|
+
pushSectionCount(lines, 'resourceRules', rules.length);
|
|
1112
|
+
for (const [index, rule] of rules.entries()) pushSectionItem(lines, index, rule);
|
|
1059
1113
|
const referenceKeyGuide = Array.isArray(data.referenceKeyGuide) ? data.referenceKeyGuide : [];
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
}
|
|
1114
|
+
pushSectionCount(lines, 'referenceKey', referenceKeyGuide.length);
|
|
1115
|
+
for (const [index, item] of referenceKeyGuide.entries()) pushSectionItem(lines, index, item);
|
|
1116
|
+
lines.push('next:');
|
|
1117
|
+
pushSectionKeyValue(lines, 'imageModels', `${commandPrefix()} model image-models --model <keyword>`);
|
|
1118
|
+
pushSectionKeyValue(lines, 'videoModels', `${commandPrefix()} model video-models --model <keyword>`);
|
|
1119
|
+
pushSectionKeyValue(lines, 'options', `${commandPrefix()} model options --model-group-code <modelGroupCode>`);
|
|
1064
1120
|
return `${lines.join('\n')}\n`;
|
|
1065
1121
|
}
|
|
1066
1122
|
|
|
@@ -1072,9 +1128,17 @@ function modelListNextCommand(commandName) {
|
|
|
1072
1128
|
if (commandName === 'model image-models' || commandName === 'model video-models') {
|
|
1073
1129
|
return `${commandPrefix()} model options --model-group-code <modelGroupCode>`;
|
|
1074
1130
|
}
|
|
1131
|
+
if (commandName === 'model asset-review-models') {
|
|
1132
|
+
return `${commandPrefix()} create asset-groups --platform <platform> --name "<keyword>"`;
|
|
1133
|
+
}
|
|
1075
1134
|
return null;
|
|
1076
1135
|
}
|
|
1077
1136
|
|
|
1137
|
+
function modelListAgentHint(commandName) {
|
|
1138
|
+
if (commandName !== 'model image-models' && commandName !== 'model video-models') return null;
|
|
1139
|
+
return '对每个候选运行 model options,并把候选模型、参数取值、默认值和资源能力展示给用户后再推荐或进入 fee/dry-run。';
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1078
1142
|
function statusNextCommand(commandName, normalized) {
|
|
1079
1143
|
if (!isPlainObject(normalized) || normalized.isTerminal !== false || !normalized.taskId) return null;
|
|
1080
1144
|
const taskType = normalized.taskType || (commandName === 'task video-status' ? 'VIDEO_GROUP' : 'IMAGE_CREATE');
|
|
@@ -1206,6 +1270,8 @@ export function formatTextOutput(commandName, data, context = {}) {
|
|
|
1206
1270
|
if (normalized.dryRun && !context.confirmCommand && context.executeCommand) lines.push(`next=${context.executeCommand}`);
|
|
1207
1271
|
const modelNext = modelListNextCommand(commandName);
|
|
1208
1272
|
if (modelNext) lines.push(`next=${modelNext}`);
|
|
1273
|
+
const modelHint = modelListAgentHint(commandName);
|
|
1274
|
+
if (modelHint) lines.push(`agentHint=${shortValue(modelHint)}`);
|
|
1209
1275
|
const waitNext = statusNextCommand(commandName, normalized);
|
|
1210
1276
|
if (waitNext) lines.push(`next=${waitNext}`);
|
|
1211
1277
|
const subtitleNext = subtitleRemoveNextCommand(commandName, normalized);
|
|
@@ -24,6 +24,7 @@ const OPTION_SYNONYMS = {
|
|
|
24
24
|
threads: ['concurrency'],
|
|
25
25
|
output: ['format'],
|
|
26
26
|
json: ['format'],
|
|
27
|
+
keyword: ['model'],
|
|
27
28
|
input: ['inputFile'],
|
|
28
29
|
in: ['inputFile'],
|
|
29
30
|
dry: ['dryRun'],
|
|
@@ -60,6 +61,16 @@ function suggestSimilarOptions(unknownKey, allowedKeys) {
|
|
|
60
61
|
for (const syn of synonyms) {
|
|
61
62
|
if (allowedKeys.includes(syn)) record(syn, -1);
|
|
62
63
|
}
|
|
64
|
+
if (unknownKey.length === 1) {
|
|
65
|
+
const ch = unknownKey.toLowerCase();
|
|
66
|
+
for (const allowed of allowedKeys) {
|
|
67
|
+
if (allowed.toLowerCase().startsWith(ch)) record(allowed, 1);
|
|
68
|
+
}
|
|
69
|
+
return [...scored.entries()]
|
|
70
|
+
.sort((x, y) => x[1] - y[1])
|
|
71
|
+
.slice(0, 3)
|
|
72
|
+
.map(([key]) => formatOptionName(key));
|
|
73
|
+
}
|
|
63
74
|
for (const allowed of allowedKeys) {
|
|
64
75
|
const a = unknownKey.toLowerCase();
|
|
65
76
|
const b = allowed.toLowerCase();
|
|
@@ -153,6 +164,7 @@ function assignKwarg(kwargs, key, value) {
|
|
|
153
164
|
function parseArgv(argv) {
|
|
154
165
|
const commandParts = [];
|
|
155
166
|
const kwargs = {};
|
|
167
|
+
const kwargRawFlags = {};
|
|
156
168
|
let format = 'text';
|
|
157
169
|
|
|
158
170
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -182,10 +194,27 @@ function parseArgv(argv) {
|
|
|
182
194
|
}
|
|
183
195
|
continue;
|
|
184
196
|
}
|
|
197
|
+
if (token.length > 1 && token.startsWith('-') && /^-[A-Za-z]/.test(token)) {
|
|
198
|
+
const raw = token.slice(1);
|
|
199
|
+
const eqIndex = raw.indexOf('=');
|
|
200
|
+
const keyPart = eqIndex >= 0 ? raw.slice(0, eqIndex) : raw;
|
|
201
|
+
const inlineValue = eqIndex >= 0 ? raw.slice(eqIndex + 1) : undefined;
|
|
202
|
+
const key = normalizeKey(keyPart);
|
|
203
|
+
if (!(key in kwargRawFlags)) kwargRawFlags[key] = token;
|
|
204
|
+
if (inlineValue !== undefined) {
|
|
205
|
+
assignKwarg(kwargs, key, inlineValue);
|
|
206
|
+
} else if (argv[index + 1] && !argv[index + 1].startsWith('-')) {
|
|
207
|
+
assignKwarg(kwargs, key, argv[index + 1]);
|
|
208
|
+
index += 1;
|
|
209
|
+
} else {
|
|
210
|
+
assignKwarg(kwargs, key, true);
|
|
211
|
+
}
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
185
214
|
commandParts.push(token);
|
|
186
215
|
}
|
|
187
216
|
|
|
188
|
-
return { commandName: commandParts.join(' '), kwargs, format };
|
|
217
|
+
return { commandName: commandParts.join(' '), kwargs, kwargRawFlags, format };
|
|
189
218
|
}
|
|
190
219
|
|
|
191
220
|
async function readVersion() {
|
|
@@ -812,6 +841,23 @@ function buildCommandWorkflow(command) {
|
|
|
812
841
|
if (command.name === 'create asset') {
|
|
813
842
|
next.push('读取 data.assetPath', '若后端返回审核 taskId,再运行 task wait --task-type ASSET_REGISTER');
|
|
814
843
|
}
|
|
844
|
+
if (command.name === 'model image-models' || command.name === 'model video-models') {
|
|
845
|
+
next.push('读取 data.models[] 得到全部候选;用户给了模型口语名时保留同族全部候选,不只取第一个');
|
|
846
|
+
next.push('对每个候选运行 model options --model-group-code <modelGroupCode> 查看真实参数和素材约束');
|
|
847
|
+
next.push('先向用户展示候选模型 + 参数取值 + 资源能力,再推荐模型或进入 fee/dry-run');
|
|
848
|
+
}
|
|
849
|
+
if (command.name === 'model asset-review-models') {
|
|
850
|
+
next.push('读取 data.models[].platform', '用 create asset-groups --platform <platform> 查重或创建素材组');
|
|
851
|
+
}
|
|
852
|
+
if (command.name === 'model options') {
|
|
853
|
+
next.push('读取 params/resources/constraints 确认参数与素材约束', '运行 model create-spec --model-group-code <code> 查看创建写法和示例');
|
|
854
|
+
}
|
|
855
|
+
if (command.name === 'model create-spec') {
|
|
856
|
+
next.push('按 examples / supportedIntents 组装命令', '用户确认关键参数后先运行 create image-fee 或 create video-fee,再 dry-run');
|
|
857
|
+
}
|
|
858
|
+
if (command.name === 'model input-guide') {
|
|
859
|
+
next.push('运行 model image-models 或 model video-models 选择模型', '选定模型后运行 model options --model-group-code <code>');
|
|
860
|
+
}
|
|
815
861
|
if (['create image-batch', 'create video-batch', 'create subject-batch'].includes(command.name)) {
|
|
816
862
|
next.push('读取每项 status / taskId / error', '使用 task record-poll 或对应 wait 命令恢复批量结果');
|
|
817
863
|
}
|
|
@@ -844,6 +890,7 @@ function buildAgentContract() {
|
|
|
844
890
|
invalidation: '用户切换账号、团队、项目组、模型、素材、prompt 或关键参数后,只刷新受影响的缓存;不要重复跑未变化的 model options / create-spec / fee。',
|
|
845
891
|
},
|
|
846
892
|
workflowPolicy: [
|
|
893
|
+
'模型口语名命中后必须先展示候选模型、真实参数取值和资源能力;没有完成用户可见候选清单前,不得代选默认模型 / 参数,也不得进入 fee 或 dry-run。',
|
|
847
894
|
'模型探索阶段只读 model list / options / create-spec;fee 只在用户确认关键参数后跑一次。',
|
|
848
895
|
'supportsDryRun=true 的写入 / 扣费命令先 dry-run,确认后 yes;不要把 dry-run 当参数探索工具反复跑。',
|
|
849
896
|
'用户给出多条同模型同参数任务时优先 batch + task-record-file,不要单条循环 create。',
|
|
@@ -886,6 +933,12 @@ function buildAgentContract() {
|
|
|
886
933
|
},
|
|
887
934
|
],
|
|
888
935
|
},
|
|
936
|
+
modelCandidatePresentation: {
|
|
937
|
+
purpose: '把 model image-models / video-models 与每个候选的 model options 转成用户可见清单,避免 Agent 内部看完 options 后直接代选。',
|
|
938
|
+
trigger: '用户问有哪些模型、给出口语名、或模型列表返回候选时都触发;即使只有一个候选也要展示真实可选项。',
|
|
939
|
+
requiredVisibleFields: ['displayName', 'modelDesc', 'taskQueueNum', 'quality values/default', 'ratio values/default', 'duration or generateNum values/default', 'resource modes/media/usages', 'channel or fast/pro differences'],
|
|
940
|
+
blockedBeforePresentation: ['create image-fee', 'create video-fee', 'create image --dry-run', 'create video --dry-run', 'default model recommendation', 'default quality/ratio/duration choice'],
|
|
941
|
+
},
|
|
889
942
|
modelOptions: {
|
|
890
943
|
command: `${commandPrefix()} model options --model-group-code <code>`,
|
|
891
944
|
jsonCommand: `${commandPrefix()} model options --model-group-code <code> -f json`,
|
|
@@ -1182,28 +1235,37 @@ function printCommandHelp(command) {
|
|
|
1182
1235
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
1183
1236
|
}
|
|
1184
1237
|
|
|
1185
|
-
function validateCommandOptions(command, kwargs) {
|
|
1238
|
+
function validateCommandOptions(command, kwargs, kwargRawFlags = {}) {
|
|
1186
1239
|
const allowedKeys = (command.args || []).map((arg) => normalizeKey(arg.name));
|
|
1187
1240
|
const allowed = new Set(allowedKeys);
|
|
1188
1241
|
const unknown = Object.keys(kwargs).filter((key) => !allowed.has(key));
|
|
1189
1242
|
if (!unknown.length) return;
|
|
1243
|
+
const displayFlag = (key) => kwargRawFlags[key] || formatOptionName(key);
|
|
1190
1244
|
const suggestions = {};
|
|
1191
1245
|
for (const unk of unknown) {
|
|
1192
1246
|
const matches = suggestSimilarOptions(unk, allowedKeys);
|
|
1193
|
-
if (matches.length) suggestions[
|
|
1247
|
+
if (matches.length) suggestions[displayFlag(unk)] = matches;
|
|
1194
1248
|
}
|
|
1249
|
+
const usedShortForm = unknown.some((key) => {
|
|
1250
|
+
const flag = kwargRawFlags[key];
|
|
1251
|
+
return flag && flag.startsWith('-') && !flag.startsWith('--');
|
|
1252
|
+
});
|
|
1195
1253
|
const baseHint = `运行 ${commandPrefix()} ${command.name} -h 查看可用参数。`;
|
|
1254
|
+
const shortHint = usedShortForm
|
|
1255
|
+
? `本 CLI 仅支持 -f / -h / -v 等少数短选项,业务参数请使用 --xxx 长形式。`
|
|
1256
|
+
: '';
|
|
1196
1257
|
const suggestionParts = Object.entries(suggestions).map(([opt, matches]) => `${opt} → ${matches.join(' / ')}`);
|
|
1197
|
-
const
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1258
|
+
const hintParts = [];
|
|
1259
|
+
if (suggestionParts.length) hintParts.push(`你是不是想用:${suggestionParts.join(';')}?`);
|
|
1260
|
+
if (shortHint) hintParts.push(shortHint);
|
|
1261
|
+
hintParts.push(baseHint);
|
|
1262
|
+
throw new LingjingAwbCliError(`未知参数:${unknown.map(displayFlag).join(', ')}`, {
|
|
1201
1263
|
type: 'unknown_option',
|
|
1202
1264
|
exitCode: 2,
|
|
1203
|
-
hint,
|
|
1265
|
+
hint: hintParts.join(' '),
|
|
1204
1266
|
details: {
|
|
1205
1267
|
command: command.name,
|
|
1206
|
-
unknownOptions: unknown.map(
|
|
1268
|
+
unknownOptions: unknown.map(displayFlag),
|
|
1207
1269
|
...(Object.keys(suggestions).length ? { suggestions } : {}),
|
|
1208
1270
|
},
|
|
1209
1271
|
});
|
|
@@ -1265,7 +1327,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1265
1327
|
return;
|
|
1266
1328
|
}
|
|
1267
1329
|
|
|
1268
|
-
const { commandName: rawCommandName, kwargs, format } = parseArgv(argv);
|
|
1330
|
+
const { commandName: rawCommandName, kwargs, kwargRawFlags, format } = parseArgv(argv);
|
|
1269
1331
|
const commandName = resolveCommandName(rawCommandName, commands);
|
|
1270
1332
|
const command = commands.find((item) => item.name === commandName);
|
|
1271
1333
|
if (!command) {
|
|
@@ -1289,7 +1351,7 @@ export async function runStandaloneCli(argv = process.argv.slice(2)) {
|
|
|
1289
1351
|
|
|
1290
1352
|
const startedAt = Date.now();
|
|
1291
1353
|
try {
|
|
1292
|
-
validateCommandOptions(command, kwargs);
|
|
1354
|
+
validateCommandOptions(command, kwargs, kwargRawFlags);
|
|
1293
1355
|
const data = command.virtual === 'schema'
|
|
1294
1356
|
? buildCommandSchema(commands, kwargs, version)
|
|
1295
1357
|
: await command.func({ command }, kwargs);
|
package/skills/lj-awb/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: lj-awb
|
|
3
|
-
version: 0.3.
|
|
3
|
+
version: 0.3.18
|
|
4
4
|
description: "灵境 AWB CLI skill。使用 `lj-awb` 命令调用动漫平台 / AWB 云端能力,覆盖认证、项目组、积分、模型发现、上传、统一 create 创建域、任务查询、最终产物 artifact CRUD 与本地 JSON 导入。用户说生图、生视频、主体、音色、素材加白、去字幕、artifact 写入或查询时使用。正式生成、切换项目组、清空认证、artifact 写入等写入或扣费动作前必须确认。"
|
|
5
5
|
metadata:
|
|
6
6
|
requires:
|
|
@@ -43,6 +43,8 @@ LINGJING_AWB_CMD="$(bash "$(dirname "$0")/scripts/resolve-lj-awb-cmd.sh")"
|
|
|
43
43
|
lj-awb schema -f json
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
+
如果只是不确定某个命令参数,优先读精确契约,例如 `lj-awb schema --domain model --command video-models -f json`。schema 查询必须先返回,再组织业务命令;不要把 schema 查询和猜测命令放进同一批并行调用。
|
|
47
|
+
|
|
46
48
|
如果用户已经给出完整只读命令,且该命令在本 skill 已知范围内,可以直接执行,不必为了形式补跑 schema。命令名、参数名、requiredOptions、safety、workflow.nextActions 都以 schema 为准。旧根域 `image` / `video` / `asset` / `subject` 不存在,不要尝试旧入口。
|
|
47
49
|
|
|
48
50
|
## 能力地图
|
|
@@ -84,7 +86,8 @@ lj-awb schema -f json
|
|
|
84
86
|
- `fee` 是最终估价,不是参数探索工具。不要为多个 quality / duration / ratio / 渠道组合反复跑 fee。
|
|
85
87
|
- 正式图片 / 视频任务要带 `--project-group-no`,除非命令 schema 没有该参数。
|
|
86
88
|
- 命令返回 `nextCommand`、`nextRefSubject`、`nextVoiceArg` 时优先复用返回值,不手拼。
|
|
87
|
-
- compact text
|
|
89
|
+
- compact text 是默认输出;复杂模型命令按 `section:` + 缩进 `key=value` 输出,先按分区读 `summary` / `params` / `resources` / `constraints` / `intents` / `examples` / `next`。只有 schema、options、create-spec、完整嵌套结构或脚本解析时用 `-f json`。
|
|
90
|
+
- 模型口语名命中后必须过“候选展示门”:先把候选模型、真实参数取值和资源能力转成用户可见清单,再推荐或追问;不能只在内部读完 `model options` 就直接代选默认模型 / 参数。
|
|
88
91
|
- 积分口径只把 `billingPointBalance` 当可扣积分余额;`projectBudgetBalance` 是项目组预算,不要混说。
|
|
89
92
|
- 不直连 material / asset / 外部服务;所有业务能力统一通过 `lj-awb`。
|
|
90
93
|
|
package/skills/lj-awb/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.3.
|
|
1
|
+
0.3.18
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
## 规则
|
|
20
20
|
|
|
21
|
-
-
|
|
21
|
+
- 自动化环境只使用 `LINGJING_AWB_ACCESS_KEY`。
|
|
22
22
|
- 不要把 access key 明文写入对话、日志、文档或任务台账。
|
|
23
23
|
- `auth status` 只读本地配置;正式创作前用 `auth verify`、`account info` 或 `doctor --verify` 确认远端可用。
|
|
24
24
|
- `auth clear` 是本地破坏性动作,必须先确认或 dry-run。
|
|
@@ -105,7 +105,7 @@ Agent 必须按顺序做:
|
|
|
105
105
|
|
|
106
106
|
1. 本轮尚未验证时运行 `doctor --verify`,确认认证、API、项目组和 UTF-8 环境。
|
|
107
107
|
2. 没有可复用候选时运行 `model image-models` 或 `model video-models`,按用户目的筛候选模型。
|
|
108
|
-
3.
|
|
108
|
+
3. 向用户展示候选是硬门槛,**不是只列名字,也不是内部摘要**;没有完成这一步,不得推荐默认模型 / 参数,不得进入 `fee` 或 `create --dry-run`。按三步走:
|
|
109
109
|
- **基本信息**:`displayName` + `modelDesc`(运营填的特性描述,没填就只报 displayName)+ `taskQueueNum` 的人话表达(0 = 队列空闲 / 1-10 = 排队中 / >10 = 等待较长)。
|
|
110
110
|
- **参数取值**:对每个候选**并行**跑 `lj-awb model options --model-group-code <code> -f json`,把可控参数实际取值列出来:quality 档位列表 / ratio 比例列表 / video 的 duration 范围 / image 的 generateNum 上限 / 支持的资源模式(reference · first_frame · last_frame · audio · subject) / 是否暴露 needAudio / feeCalcType 转人话。
|
|
111
111
|
- **追问参数**:用户看完真实选项后再追问 quality / ratio / duration / generateNum。**不要在用户看过 `model options` 输出前就给出 16:9 / 720P 这类自编选项**——必须是模型真实支持的值。
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
## 核心原则
|
|
6
6
|
|
|
7
7
|
- CLI schema 是命令事实来源:非单条查询任务、命令不确定或涉及写入 / 扣费时,本轮只读一次 `lj-awb schema -f json`,命令名、参数名、安全规则和 nextActions 都以 schema 为准。
|
|
8
|
+
- 命令或参数不确定时,schema 查询是阻塞前置步骤:先运行精确 schema(例如 `lj-awb schema --domain model --command video-models -f json`)并等结果返回,再根据 `options[].flag` 组织业务命令;不要把 schema 查询和猜测命令放在同一批并行工具调用里。
|
|
8
9
|
- Skill 是驱动器,不是命令百科。先在本模块决定链路,只在需要时加载一个细节模块。
|
|
9
10
|
- 同一对话维护 AWB 状态账本;除非用户切换账号、团队、项目组、模型、素材、prompt 或关键参数,否则不要重复跑同样的查询。
|
|
10
11
|
- 写入 / 扣费命令遵循 schema safety:`supportsDryRun=true` 先 dry-run,`requiresConfirmation=true` 经用户确认后再 `--yes`。
|
|
@@ -51,13 +52,23 @@ CLI 当前能力分为:
|
|
|
51
52
|
1. 若本轮未验证远端认证和项目组,运行 `doctor --verify`,必要时 `project current`。
|
|
52
53
|
2. 根据用户目标选任务类型:生图查 `model image-models`,生视频查 `model video-models`。
|
|
53
54
|
3. 如果同一关键词已有候选,不重复查列表;只在用户换模型关键词时刷新。
|
|
54
|
-
4. 对候选跑 `model options
|
|
55
|
+
4. 对候选跑 `model options`,通过“模型候选展示门”把真实参数和资源能力展示给用户;用户选定模型后补 `model create-spec`。
|
|
55
56
|
5. 按 [`task-manual.md`](task-manual.md) 选择资源模式,按 [`create-contract.md`](create-contract.md) 校验素材、占位符、`needAudio` 和模式互斥。
|
|
56
57
|
6. 只追问会影响价格 / 效果且用户未给出的关键参数;用户说“默认”才使用 defaultValue。
|
|
57
58
|
7. 参数确认后跑一次 `create image-fee` 或 `create video-fee`,不要用 fee 做多组合探索。
|
|
58
59
|
8. 跑一次 `create image/video --dry-run` 展示最终 prompt、资源、参数、积分。
|
|
59
60
|
9. 用户确认后 `--yes`。返回 `nextCommand` 时直接执行它等待结果。
|
|
60
61
|
|
|
62
|
+
### 模型候选展示门
|
|
63
|
+
|
|
64
|
+
只要用户给了模型口语名(如 `sd2`、`gpt`、`banana`、`可灵`)或模型查询返回候选,Agent 必须先完成这个门槛,才能推荐默认模型 / 默认参数、进入 `fee` 或 `create --dry-run`。
|
|
65
|
+
|
|
66
|
+
1. 用 schema 或命令帮助确认过滤参数名,模型关键词统一走 `--model <keyword>`,不要猜 `--keyword`。
|
|
67
|
+
2. 跑 `model image-models --model <keyword> -f json` 或 `model video-models --model <keyword> -f json`。用户已经给出口语名时,同族候选全部保留;不要只取第一个。
|
|
68
|
+
3. 对每个候选并行跑 `model options --model-group-code <code> -f json`,读取真实 `quality`、`ratio`、`duration` / `generateNum`、默认值、资源模式和约束。
|
|
69
|
+
4. 必须向用户输出可见清单:模型显示名、描述、排队状态、可选参数、默认值、支持的资源模式(图片 / 视频 / 音频 reference、首帧 / 首尾帧、主体等)、渠道差异。`modelGroupCode` 默认只内部使用,除非用户主动问技术 ID。
|
|
70
|
+
5. 用户看完清单后再问关键参数或给建议。即使只命中 1 个候选,也要展示该模型的真实可选项;不要用“我倾向 4:3 / 5s / 720P”代替候选清单。
|
|
71
|
+
|
|
61
72
|
### 批量任务
|
|
62
73
|
|
|
63
74
|
- 用户给出 2 条以上同模型同参数任务时,优先建议 `create image-batch` / `create video-batch`。
|
|
@@ -22,7 +22,16 @@ lj-awb model image-models --model "<keyword>"
|
|
|
22
22
|
lj-awb model video-models --model "<keyword>"
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
候选展示走 [`driver.md`](driver.md)
|
|
25
|
+
候选展示走 [`driver.md`](driver.md) 的“模型候选展示门”:基本信息 → 并行跑 `model options` 列真实参数取值和资源能力 → 看完真实选项后追问。不要把原始 JSON 整段粘给用户,但必须把候选转成用户可读清单;不能只在内部读完 options 后直接代选模型或参数。
|
|
26
|
+
|
|
27
|
+
用户可见清单至少包含:
|
|
28
|
+
|
|
29
|
+
- 模型显示名、模型描述和排队状态。
|
|
30
|
+
- `quality`、`ratio`、视频 `duration` 或图片 `generateNum` 的可选值与默认值。
|
|
31
|
+
- 资源能力:reference 支持哪些媒体,是否支持音频参考、视频参考、首帧 / 首尾帧、主体引用。
|
|
32
|
+
- 同底模不同渠道 / Fast / Pro 等差异;如果参数完全一致,可以说明主要按队列、渠道或用户偏好选择。
|
|
33
|
+
|
|
34
|
+
模型列表里 `inputModes` / `params` 摘要只用于**初筛**;最终枚举值和限制以 `model options` 为准。`modelGroupCode` 是技术 ID,agent 内部跑命令用,**默认不展示给用户**。
|
|
26
35
|
|
|
27
36
|
## 获取参数约束
|
|
28
37
|
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
## 创作前固定流程
|
|
6
6
|
|
|
7
7
|
1. 本轮尚未验证时运行 `lj-awb doctor --verify`,确认认证、API、项目组、UTF-8。
|
|
8
|
-
2. 没有可复用候选时运行 `model image-models` 或 `model video-models
|
|
9
|
-
3.
|
|
8
|
+
2. 没有可复用候选时运行 `model image-models` 或 `model video-models`,按用户目标筛候选;用户给了口语名时保留同族全部候选,不只取第一项。
|
|
9
|
+
3. 进入“模型候选展示门”:对候选跑 `model options --model-group-code <code> -f json`,把每个候选的参数取值、默认值、素材约束和资源模式转成用户可见清单。
|
|
10
10
|
4. 选中模型的创建规格未缓存时运行 `model create-spec --model-group-code <code> -f json`。
|
|
11
11
|
5. 用 `inputRequirement` 判断是否必须有视觉输入;用 `supportedIntents` 判断素材模式;用 `model options.params` 判断 `ratio`、`quality`、`duration` 等枚举。
|
|
12
12
|
6. 在 `fee` 或 `create --dry-run` 前,先确认用户未提供但会影响价格 / 效果的关键参数:视频 `quality`、`duration`、约束后仍可选的 `ratio`,以及用户明确要输出音效时的 `needAudio`;图片 `quality`、`ratio`、`generateNum`。
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
## 确认选项和验证拆开
|
|
18
18
|
|
|
19
19
|
- `model options` / `model create-spec` 是只读验证,可以先跑,用来收集候选值、默认值、素材模式和约束。
|
|
20
|
+
- `model options` 结果必须先转成用户可见候选清单;不能只在内部看完后直接说“我建议 4:3、720P、5s”。
|
|
20
21
|
- `fee` 会基于已选参数估价,`create --dry-run` 会基于已选参数组装请求;如果用户还没给 `quality`、`duration`、可选 `ratio`、`generateNum` 这类关键参数,先问用户,不要先用默认值跑一遍。
|
|
21
22
|
- 追问时给出候选值和默认值,例如:“还差会影响价格 / 效果的参数:画质 720 / 1080(默认 720),时长 3-15 秒(默认 5)。你希望用哪组?”
|
|
22
23
|
- 用户回复“按默认”后,才可以使用 `model options.params[].defaultValue` 继续估价和 dry-run。
|
|
@@ -18,7 +18,7 @@ CLI 失败时 envelope 形如:
|
|
|
18
18
|
| `1` | `subject_voice_failed` | 主体音色创建任务已失败 | 看 `details.errorMessage`,调整音频 / 视频来源后重新创建 |
|
|
19
19
|
| `2` | `argument_error` | 必填参数缺失、参数格式 / 枚举不合法 | 按 `hint` 补参数;用 `lj-awb schema --command <name> -f json` 查 `requiredOptions / requiredAnyOptions` |
|
|
20
20
|
| `2` | `unknown_command` / `unknown_option` | 命令名 / 参数名打错 | 跑 `lj-awb -h` 或对应子域 `lj-awb <domain> -h` 重查命令 |
|
|
21
|
-
| `3` | `auth_required` | 缺少 access key | 提示用户 `lj-awb auth login --access-key <key
|
|
21
|
+
| `3` | `auth_required` | 缺少 access key | 提示用户 `lj-awb auth login --access-key <key>` 或设置 `LINGJING_AWB_ACCESS_KEY` |
|
|
22
22
|
| `3` | `auth_failed` | access key 无效 / 已过期(HTTP 401) | 跑 `auth verify` 确认;提示重新登录 |
|
|
23
23
|
| `10` | `confirmation_required` | 写入 / 扣费 / 切换上下文动作未确认 | 向用户复述 `details.action` 后追加 `--yes` 重跑 |
|
|
24
24
|
| `20` | `task_still_running` | 等待窗口结束但任务未终态 | 直接续跑 `task wait --task-id ... --wait-seconds <n>`,**不代表失败** |
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## 默认 Text 怎么读
|
|
6
6
|
|
|
7
|
+
- 默认 text 是“分区 key=value”:先看 `summary:`,再看 `params:`、`resources:`、`constraints:`,最后看 `next:`。每个分区的 `count` 表示列表数量,列表项使用 `[0]: key=value ...`。
|
|
7
8
|
- `params[]`:模型暴露的可控参数和约束;这里只给参数 key,不给 create 命令 flag。
|
|
8
9
|
- `values`:枚举取值,只能从这里选。
|
|
9
10
|
- `default`:模型默认值;用户没明确指定时通常不必显式传,但清晰度、比例、时长这类关键项仍要确认。
|
|
@@ -30,12 +31,13 @@ JSON 里会保留更完整的参数 / 资源 / 条件约束字段,但仍不承
|
|
|
30
31
|
## 使用顺序
|
|
31
32
|
|
|
32
33
|
1. `model image-models` / `model video-models` 按关键词找候选。
|
|
33
|
-
2. `model options --model-group-code <code
|
|
34
|
-
3.
|
|
35
|
-
4.
|
|
36
|
-
5.
|
|
37
|
-
6. `
|
|
38
|
-
7.
|
|
34
|
+
2. 对候选逐个运行 `model options --model-group-code <code>`,读取可传参数和素材约束。
|
|
35
|
+
3. 把候选模型、参数取值、默认值、资源能力转成用户可见清单;这是进入推荐 / 追问 / fee 前的硬门槛。
|
|
36
|
+
4. `model create-spec --model-group-code <code>` 读取输入模式、素材绑定规则和示例命令。
|
|
37
|
+
5. 如果用户缺少关键价格 / 效果参数,先展示候选值和默认值,请用户选择或确认“按默认”。
|
|
38
|
+
6. `fee` 估价。
|
|
39
|
+
7. `create --dry-run` 预览请求。
|
|
40
|
+
8. 用户确认后 `create --yes`。
|
|
39
41
|
|
|
40
42
|
## 判断口径
|
|
41
43
|
|