koishi-plugin-hhs-huatu 0.2.0 → 0.2.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/lib/config.d.ts +1 -1
- package/lib/index.js +184 -21
- package/package.json +1 -1
- package/readme.md +14 -8
package/lib/config.d.ts
CHANGED
|
@@ -249,5 +249,5 @@ interface Forbidden {
|
|
|
249
249
|
strict: boolean;
|
|
250
250
|
}
|
|
251
251
|
export declare function parseForbidden(input: string): Forbidden[];
|
|
252
|
-
export declare function parseInput(session: Session, input: string, config: Config, override: boolean): string[];
|
|
252
|
+
export declare function parseInput(session: Session, input: string, config: Config, override: boolean, undesired?: string): string[];
|
|
253
253
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1152,7 +1152,60 @@ function parseForbidden(input) {
|
|
|
1152
1152
|
}
|
|
1153
1153
|
__name(parseForbidden, "parseForbidden");
|
|
1154
1154
|
var backslash = /@@__BACKSLASH__@@/g;
|
|
1155
|
-
function
|
|
1155
|
+
function cleanUndesiredParameter(undesired) {
|
|
1156
|
+
const optionPatterns = [
|
|
1157
|
+
/-K\s+["']?[^"']*["']?/g,
|
|
1158
|
+
// -K 选项
|
|
1159
|
+
/-m\s+\w+/g,
|
|
1160
|
+
// -m 选项
|
|
1161
|
+
/-s\s+\w+/g,
|
|
1162
|
+
// -s 选项
|
|
1163
|
+
/-r\s+[\w\d]+/g,
|
|
1164
|
+
// -r 选项
|
|
1165
|
+
/-o\s+\w+/g,
|
|
1166
|
+
// -o 选项
|
|
1167
|
+
/-O\s*/g,
|
|
1168
|
+
// -O 选项
|
|
1169
|
+
/-x\s+\d+/g,
|
|
1170
|
+
// -x 选项
|
|
1171
|
+
/-t\s+\d+/g,
|
|
1172
|
+
// -t 选项
|
|
1173
|
+
/-c\s+[\d.]+/g,
|
|
1174
|
+
// -c 选项
|
|
1175
|
+
/-R\s+[\d.]+/g,
|
|
1176
|
+
// -R 选项
|
|
1177
|
+
/-n\s+[\d.]+/g,
|
|
1178
|
+
// -n 选项
|
|
1179
|
+
/-N\s+[\d.]+/g,
|
|
1180
|
+
// -N 选项
|
|
1181
|
+
/-H\s*/g,
|
|
1182
|
+
// -H 选项
|
|
1183
|
+
/-S\s*/g,
|
|
1184
|
+
// -S 选项
|
|
1185
|
+
/-d\s*/g,
|
|
1186
|
+
// -d 选项
|
|
1187
|
+
/-C\s+\w+/g,
|
|
1188
|
+
// -C 选项
|
|
1189
|
+
/-D\s*/g,
|
|
1190
|
+
// -D 选项
|
|
1191
|
+
/-T\s*/g,
|
|
1192
|
+
// -T 选项
|
|
1193
|
+
/-i\s+\d+/g,
|
|
1194
|
+
// -i 选项
|
|
1195
|
+
/-b\s+\d+/g,
|
|
1196
|
+
// -b 选项
|
|
1197
|
+
/-I\s*/g
|
|
1198
|
+
// -I 选项
|
|
1199
|
+
];
|
|
1200
|
+
let cleaned = undesired.trim();
|
|
1201
|
+
for (const pattern of optionPatterns) {
|
|
1202
|
+
cleaned = cleaned.replace(pattern, "").trim();
|
|
1203
|
+
}
|
|
1204
|
+
cleaned = cleaned.replace(/^["']|["']$/g, "").trim();
|
|
1205
|
+
return cleaned;
|
|
1206
|
+
}
|
|
1207
|
+
__name(cleanUndesiredParameter, "cleanUndesiredParameter");
|
|
1208
|
+
function parseInput(session, input, config, override, undesired) {
|
|
1156
1209
|
if (!input) {
|
|
1157
1210
|
return [
|
|
1158
1211
|
null,
|
|
@@ -1167,8 +1220,21 @@ function parseInput(session, input, config, override) {
|
|
|
1167
1220
|
}
|
|
1168
1221
|
const negative = [];
|
|
1169
1222
|
const placement = session.resolve(config.placement);
|
|
1170
|
-
const appendToList = /* @__PURE__ */ __name((words, input2 = "") => {
|
|
1171
|
-
|
|
1223
|
+
const appendToList = /* @__PURE__ */ __name((words, input2 = "", isNegative = false) => {
|
|
1224
|
+
let tags;
|
|
1225
|
+
if (isNegative) {
|
|
1226
|
+
if (input2.includes(",")) {
|
|
1227
|
+
tags = input2.split(/,\s*/g);
|
|
1228
|
+
} else {
|
|
1229
|
+
tags = [input2.trim()];
|
|
1230
|
+
}
|
|
1231
|
+
} else {
|
|
1232
|
+
if (input2.includes(",")) {
|
|
1233
|
+
tags = input2.split(/,\s*/g);
|
|
1234
|
+
} else {
|
|
1235
|
+
tags = input2.split(/\s+/g).filter((tag) => tag.trim());
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1172
1238
|
if (placement === "before") tags.reverse();
|
|
1173
1239
|
for (let tag of tags) {
|
|
1174
1240
|
tag = tag.trim();
|
|
@@ -1184,7 +1250,13 @@ function parseInput(session, input, config, override) {
|
|
|
1184
1250
|
const capture = input.match(/(,\s*|\s+)(-u\s+|--undesired\s+|negative prompts?:\s*)([\s\S]+)/m);
|
|
1185
1251
|
if (capture?.[3]) {
|
|
1186
1252
|
input = input.slice(0, capture.index).trim();
|
|
1187
|
-
appendToList(negative, capture[3]);
|
|
1253
|
+
appendToList(negative, capture[3], true);
|
|
1254
|
+
}
|
|
1255
|
+
if (undesired) {
|
|
1256
|
+
const cleanedUndesired = cleanUndesiredParameter(undesired);
|
|
1257
|
+
if (cleanedUndesired) {
|
|
1258
|
+
appendToList(negative, cleanedUndesired, true);
|
|
1259
|
+
}
|
|
1188
1260
|
}
|
|
1189
1261
|
const forbidden = parseForbidden(session.resolve(config.forbidden));
|
|
1190
1262
|
const positive = input.split(/,\s*/g).filter((word) => {
|
|
@@ -1202,15 +1274,30 @@ function parseInput(session, input, config, override) {
|
|
|
1202
1274
|
if (/^<.+>$/.test(word)) return word.replace(/ /g, "_");
|
|
1203
1275
|
return config.lowerCase ? word.toLowerCase() : word;
|
|
1204
1276
|
});
|
|
1205
|
-
|
|
1277
|
+
const processedNegative = negative.filter((word) => {
|
|
1278
|
+
word = word.toLowerCase().replace(/[\x00-\x7f]/g, (s) => s.replace(/[^0-9a-zA-Z]/, " ")).replace(/\s+/, " ").trim();
|
|
1279
|
+
if (!word) return false;
|
|
1280
|
+
for (const { pattern, strict } of forbidden) {
|
|
1281
|
+
if (strict && word.split(/\W+/g).includes(pattern)) {
|
|
1282
|
+
return false;
|
|
1283
|
+
} else if (!strict && word.includes(pattern)) {
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
return true;
|
|
1288
|
+
}).map((word) => {
|
|
1289
|
+
if (/^<.+>$/.test(word)) return word.replace(/ /g, "_");
|
|
1290
|
+
return config.lowerCase ? word.toLowerCase() : word;
|
|
1291
|
+
});
|
|
1292
|
+
if (Math.max(getWordCount(positive), getWordCount(processedNegative)) > (session.resolve(config.maxWords) || Infinity)) {
|
|
1206
1293
|
return [".too-many-words"];
|
|
1207
1294
|
}
|
|
1208
1295
|
if (!override) {
|
|
1209
|
-
appendToList(positive, session.resolve(config.basePrompt));
|
|
1210
|
-
appendToList(
|
|
1211
|
-
if (config.defaultPromptSw) appendToList(positive, session.resolve(config.defaultPrompt));
|
|
1296
|
+
appendToList(positive, session.resolve(config.basePrompt), false);
|
|
1297
|
+
appendToList(processedNegative, session.resolve(config.negativePrompt), true);
|
|
1298
|
+
if (config.defaultPromptSw) appendToList(positive, session.resolve(config.defaultPrompt), false);
|
|
1212
1299
|
}
|
|
1213
|
-
return [null, positive.join(", "),
|
|
1300
|
+
return [null, positive.join(", "), processedNegative.join(", ")];
|
|
1214
1301
|
}
|
|
1215
1302
|
__name(parseInput, "parseInput");
|
|
1216
1303
|
function getWordCount(words) {
|
|
@@ -1244,7 +1331,9 @@ var STRATEGIES = {
|
|
|
1244
1331
|
formData
|
|
1245
1332
|
);
|
|
1246
1333
|
const nsfw = response.data.results[0].entities[0].classes.nsfw;
|
|
1247
|
-
|
|
1334
|
+
if (config.debugLog) {
|
|
1335
|
+
logger.info(`api4ai图片审核结果:${nsfw}`);
|
|
1336
|
+
}
|
|
1248
1337
|
return {
|
|
1249
1338
|
pass: nsfw <= config.api4ai.nsfwThreshold,
|
|
1250
1339
|
score: nsfw,
|
|
@@ -1264,11 +1353,15 @@ var STRATEGIES = {
|
|
|
1264
1353
|
});
|
|
1265
1354
|
const bucket = config.bucket || "default-1250000000";
|
|
1266
1355
|
const region = config.region?.toLowerCase() || "ap-chengdu";
|
|
1267
|
-
|
|
1356
|
+
if (config.debugLog) {
|
|
1357
|
+
logger.info(`腾讯云图片审核请求参数: Region=${region}, Bucket=${bucket}, BizType=${config.bizType}`);
|
|
1358
|
+
}
|
|
1268
1359
|
try {
|
|
1269
1360
|
const imageBase64 = Buffer.from(buffer).toString("base64");
|
|
1270
1361
|
const url = `https://${bucket}.ci.${region}.myqcloud.com/image/auditing`;
|
|
1271
|
-
|
|
1362
|
+
if (config.debugLog) {
|
|
1363
|
+
logger.info(`腾讯云图片审核请求URL: ${url}`);
|
|
1364
|
+
}
|
|
1272
1365
|
const requestData = {
|
|
1273
1366
|
Input: {
|
|
1274
1367
|
Content: imageBase64
|
|
@@ -1298,13 +1391,17 @@ var STRATEGIES = {
|
|
|
1298
1391
|
}
|
|
1299
1392
|
);
|
|
1300
1393
|
});
|
|
1301
|
-
|
|
1394
|
+
if (config.debugLog) {
|
|
1395
|
+
logger.info(`腾讯云图片审核响应:`, JSON.stringify(response));
|
|
1396
|
+
}
|
|
1302
1397
|
const result = response.JobsDetail || response.Response && response.Response.JobsDetail;
|
|
1303
1398
|
if (!result) {
|
|
1304
1399
|
throw new Error("审核结果解析失败,未找到JobsDetail字段");
|
|
1305
1400
|
}
|
|
1306
1401
|
const label = result.Label;
|
|
1307
|
-
|
|
1402
|
+
if (config.debugLog) {
|
|
1403
|
+
logger.info(`腾讯云图片审核结果: Label=${label}`);
|
|
1404
|
+
}
|
|
1308
1405
|
return {
|
|
1309
1406
|
pass: label === "Normal",
|
|
1310
1407
|
score: label === "Normal" ? 0 : 1,
|
|
@@ -1322,7 +1419,9 @@ var STRATEGIES = {
|
|
|
1322
1419
|
if (error.statusCode) {
|
|
1323
1420
|
errorMessage += ` (状态码: ${error.statusCode})`;
|
|
1324
1421
|
}
|
|
1325
|
-
|
|
1422
|
+
if (config.debugLog) {
|
|
1423
|
+
logger.error(`腾讯云图片审核失败详情:`, JSON.stringify(error, Object.getOwnPropertyNames(error)));
|
|
1424
|
+
}
|
|
1326
1425
|
}
|
|
1327
1426
|
logger.error(`腾讯云图片审核失败: ${errorMessage}`);
|
|
1328
1427
|
throw new Error(errorMessage);
|
|
@@ -1343,7 +1442,9 @@ async function auditImage(ctx, data, config) {
|
|
|
1343
1442
|
buffer = Buffer.from(data);
|
|
1344
1443
|
}
|
|
1345
1444
|
const imageSizeKB = Math.round(buffer.length / 1024);
|
|
1346
|
-
|
|
1445
|
+
if (config.debugLog) {
|
|
1446
|
+
ctx.logger.debug(`图片大小: ${imageSizeKB}KB`);
|
|
1447
|
+
}
|
|
1347
1448
|
const maxSizeKB = config.imageAudit?.maxSizeKB || 4096;
|
|
1348
1449
|
if (imageSizeKB > maxSizeKB) {
|
|
1349
1450
|
ctx.logger.warn(`图片大小(${imageSizeKB}KB)超过限制(${maxSizeKB}KB),可能影响审核性能`);
|
|
@@ -1373,7 +1474,9 @@ async function auditImage(ctx, data, config) {
|
|
|
1373
1474
|
if (!result.processingTime) {
|
|
1374
1475
|
result.processingTime = totalProcessingTime;
|
|
1375
1476
|
}
|
|
1376
|
-
|
|
1477
|
+
if (config.debugLog) {
|
|
1478
|
+
ctx.logger.debug(`图片审核完成,总耗时: ${totalProcessingTime}ms`);
|
|
1479
|
+
}
|
|
1377
1480
|
return result;
|
|
1378
1481
|
} catch (error) {
|
|
1379
1482
|
let errorMessage = "审核失败";
|
|
@@ -1387,7 +1490,7 @@ async function auditImage(ctx, data, config) {
|
|
|
1387
1490
|
if (error.statusCode) {
|
|
1388
1491
|
errorMessage += ` (状态码: ${error.statusCode})`;
|
|
1389
1492
|
}
|
|
1390
|
-
if (config.
|
|
1493
|
+
if (config.debugLog) {
|
|
1391
1494
|
ctx.logger.error(`图片审核失败详情:`, JSON.stringify(error, Object.getOwnPropertyNames(error)));
|
|
1392
1495
|
} else {
|
|
1393
1496
|
ctx.logger.error(`图片审核失败: ${errorMessage}`);
|
|
@@ -1477,6 +1580,56 @@ var inject = {
|
|
|
1477
1580
|
required: ["http"],
|
|
1478
1581
|
optional: ["translator"]
|
|
1479
1582
|
};
|
|
1583
|
+
function extractOptionsFromUndesired(undesired) {
|
|
1584
|
+
const extractedOptions = {};
|
|
1585
|
+
let cleanedUndesired = undesired.trim();
|
|
1586
|
+
const optionPatterns = [
|
|
1587
|
+
// 带引号的选项(优先级最高)
|
|
1588
|
+
{ pattern: /-K\s+["']([^"']*)["']/g, key: "chars" },
|
|
1589
|
+
{ pattern: /-m\s+["']([^"']*)["']/g, key: "model" },
|
|
1590
|
+
{ pattern: /-s\s+["']([^"']*)["']/g, key: "sampler" },
|
|
1591
|
+
{ pattern: /-r\s+["']([^"']*)["']/g, key: "resolution" },
|
|
1592
|
+
{ pattern: /-o\s+["']([^"']*)["']/g, key: "output" },
|
|
1593
|
+
{ pattern: /-C\s+["']([^"']*)["']/g, key: "scheduler" },
|
|
1594
|
+
// 不带引号的选项
|
|
1595
|
+
{ pattern: /-K\s+(\S+)/g, key: "chars" },
|
|
1596
|
+
{ pattern: /-m\s+(\w+)/g, key: "model" },
|
|
1597
|
+
{ pattern: /-s\s+(\w+)/g, key: "sampler" },
|
|
1598
|
+
{ pattern: /-r\s+([\w\d]+)/g, key: "resolution" },
|
|
1599
|
+
{ pattern: /-o\s+(\w+)/g, key: "output" },
|
|
1600
|
+
{ pattern: /-O\s*/g, key: "override", value: true },
|
|
1601
|
+
{ pattern: /-x\s+(\d+)/g, key: "seed" },
|
|
1602
|
+
{ pattern: /-t\s+(\d+)/g, key: "steps" },
|
|
1603
|
+
{ pattern: /-c\s+([\d.]+)/g, key: "scale" },
|
|
1604
|
+
{ pattern: /-R\s+([\d.]+)/g, key: "rescale" },
|
|
1605
|
+
{ pattern: /-n\s+([\d.]+)/g, key: "noise" },
|
|
1606
|
+
{ pattern: /-N\s+([\d.]+)/g, key: "strength" },
|
|
1607
|
+
{ pattern: /-H\s*/g, key: "hiresFix", value: true },
|
|
1608
|
+
{ pattern: /-S\s*/g, key: "smea", value: true },
|
|
1609
|
+
{ pattern: /-d\s*/g, key: "smeaDyn", value: true },
|
|
1610
|
+
{ pattern: /-C\s+(\w+)/g, key: "scheduler" },
|
|
1611
|
+
{ pattern: /-D\s*/g, key: "decrisper", value: true },
|
|
1612
|
+
{ pattern: /-T\s*/g, key: "noTranslator", value: true },
|
|
1613
|
+
{ pattern: /-i\s+(\d+)/g, key: "iterations" },
|
|
1614
|
+
{ pattern: /-b\s+(\d+)/g, key: "batch" },
|
|
1615
|
+
{ pattern: /-I\s*/g, key: "ignoreSpace", value: true }
|
|
1616
|
+
];
|
|
1617
|
+
for (const { pattern, key, value } of optionPatterns) {
|
|
1618
|
+
const matches = [...cleanedUndesired.matchAll(pattern)];
|
|
1619
|
+
for (const match of matches) {
|
|
1620
|
+
if (value !== void 0) {
|
|
1621
|
+
extractedOptions[key] = value;
|
|
1622
|
+
} else if (match[1]) {
|
|
1623
|
+
extractedOptions[key] = match[1];
|
|
1624
|
+
}
|
|
1625
|
+
cleanedUndesired = cleanedUndesired.replace(match[0], "").trim();
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
cleanedUndesired = cleanedUndesired.replace(/\s+/g, " ").trim();
|
|
1629
|
+
cleanedUndesired = cleanedUndesired.replace(/^["']|["']$/g, "").trim();
|
|
1630
|
+
return { cleanedUndesired, extractedOptions };
|
|
1631
|
+
}
|
|
1632
|
+
__name(extractOptionsFromUndesired, "extractOptionsFromUndesired");
|
|
1480
1633
|
function apply(ctx, config) {
|
|
1481
1634
|
const userTasks = /* @__PURE__ */ Object.create(null);
|
|
1482
1635
|
const userCooldowns = /* @__PURE__ */ Object.create(null);
|
|
@@ -2064,7 +2217,7 @@ function apply(ctx, config) {
|
|
|
2064
2217
|
ctx.logger.warn(err);
|
|
2065
2218
|
}
|
|
2066
2219
|
}
|
|
2067
|
-
const [errPath, prompt, uc] = parseInput(session, input, config, options2.override);
|
|
2220
|
+
const [errPath, prompt, uc] = parseInput(session, input, config, options2.override, options2.undesired);
|
|
2068
2221
|
if (errPath) return session.text(errPath);
|
|
2069
2222
|
let token;
|
|
2070
2223
|
try {
|
|
@@ -2633,8 +2786,18 @@ function apply(ctx, config) {
|
|
|
2633
2786
|
const cmd = ctx.command("novelai [prompts...]").alias("nai").alias("imagine").alias("nai4", { options: { model: "nai-v4-full", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4c", { options: { model: "nai-v4-curated-preview", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4-5c", { options: { model: "nai-v4-5-curated", sampler: "k_euler_a", iterations: 1, batch: 1 } }).alias("nai4-5", { options: { model: "nai-v4-5-full", sampler: "k_euler_a", iterations: 1, batch: 1 } }).userFields(["authority"]).shortcut("imagine", { i18n: true, fuzzy: true }).shortcut("enhance", { i18n: true, fuzzy: true, options: { enhance: true } }).option("enhance", "-e", { hidden: some(restricted, thirdParty, noImage) }).option("model", "-m <model>", { type: models, hidden: thirdParty }).option("resolution", "-r <resolution>", { type: resolution2 }).option("output", "-o", { type: ["minimal", "default", "verbose"] }).option("override", "-O", { hidden: restricted }).option("sampler", "-s <sampler>").option("seed", "-x <seed:number>").option("steps", "-t <step>", { type: step, hidden: restricted }).option("scale", "-c <scale:number>").option("rescale", "-R <rescale:number>").option("noise", "-n <noise:number>", { hidden: some(restricted, thirdParty) }).option("strength", "-N <strength:number>", { hidden: restricted }).option("hiresFix", "-H", { hidden: /* @__PURE__ */ __name(() => config.type !== "sd-webui", "hidden") }).option("hiresFixSteps", "<step>", { type: step, hidden: /* @__PURE__ */ __name(() => config.type !== "sd-webui", "hidden") }).option("smea", "-S", { hidden: /* @__PURE__ */ __name(() => config.model !== "nai-v3", "hidden") }).option("smeaDyn", "-d", { hidden: /* @__PURE__ */ __name(() => config.model !== "nai-v3", "hidden") }).option("scheduler", "-C <scheduler:string>", {
|
|
2634
2787
|
hidden: /* @__PURE__ */ __name(() => config.type === "naifu", "hidden"),
|
|
2635
2788
|
type: ["token", "login"].includes(config.type) ? scheduler.nai : config.type === "sd-webui" ? scheduler.sd : config.type === "stable-horde" ? scheduler.horde : []
|
|
2636
|
-
}).option("decrisper", "-D", { hidden: thirdParty }).option("undesired", "-u <undesired>").option("noTranslator", "-T", { hidden: /* @__PURE__ */ __name(() => !ctx.translator || !config.translator, "hidden") }).option("iterations", "-i <iterations:posint>", { fallback: 1, hidden: /* @__PURE__ */ __name(() => config.maxIterations <= 1, "hidden") }).option("batch", "-b <batch:option>", { fallback: 1, hidden: /* @__PURE__ */ __name(() => config.maxIterations <= 1, "hidden") }).option("chars", "-K <chars>").option("ignoreSpace", "-I", { hidden: true }).action(async ({ session, options: options2, name: name2 }, ...prompts) => {
|
|
2637
|
-
|
|
2789
|
+
}).option("decrisper", "-D", { hidden: thirdParty }).option("undesired", "-u <undesired:text>").option("noTranslator", "-T", { hidden: /* @__PURE__ */ __name(() => !ctx.translator || !config.translator, "hidden") }).option("iterations", "-i <iterations:posint>", { fallback: 1, hidden: /* @__PURE__ */ __name(() => config.maxIterations <= 1, "hidden") }).option("batch", "-b <batch:option>", { fallback: 1, hidden: /* @__PURE__ */ __name(() => config.maxIterations <= 1, "hidden") }).option("chars", "-K <chars>").option("ignoreSpace", "-I", { hidden: true }).action(async ({ session, options: options2, name: name2 }, ...prompts) => {
|
|
2790
|
+
let input = prompts.join(" ");
|
|
2791
|
+
if (options2.undesired) {
|
|
2792
|
+
const { cleanedUndesired, extractedOptions } = extractOptionsFromUndesired(options2.undesired);
|
|
2793
|
+
options2.undesired = cleanedUndesired;
|
|
2794
|
+
Object.assign(options2, extractedOptions);
|
|
2795
|
+
if (config.debugLog) {
|
|
2796
|
+
ctx.logger.info(`[Undesired Debug] 原始 undesired: ${options2.undesired}`);
|
|
2797
|
+
ctx.logger.info(`[Undesired Debug] 清理后 undesired: ${cleanedUndesired}`);
|
|
2798
|
+
ctx.logger.info(`[Undesired Debug] 提取的选项: ${JSON.stringify(extractedOptions)}`);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2638
2801
|
if (config.debugLog) {
|
|
2639
2802
|
ctx.logger.info(`[Characters Debug] 接收到的 prompts 数组: ${JSON.stringify(prompts)}`);
|
|
2640
2803
|
ctx.logger.info(`[Characters Debug] 接收到的 input: ${input}`);
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -32,17 +32,23 @@ nai4-5 场景 -K "1girl, smile@B3 --uc:frown;1boy, tall@D3 --uc:short"
|
|
|
32
32
|
nai4c 场景 -K "角色1@A1;角色2@E5"
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
(
|
|
35
|
+
**位置坐标表(5×5网格):**
|
|
36
|
+
```
|
|
37
|
+
A B C D E
|
|
38
|
+
1 A1 B1 C1 D1 E1 (顶部)
|
|
39
|
+
2 A2 B2 C2 D2 E2
|
|
40
|
+
3 A3 B3 C3 D3 E3 (中心)
|
|
41
|
+
4 A4 B4 C4 D4 E4
|
|
42
|
+
5 A5 B5 C5 D5 E5 (底部)
|
|
43
|
+
(左) (右)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**语法说明:**
|
|
43
47
|
- 格式:`A-E`(横向) + `1-5`(纵向),如 `A1`(左上) 到 `E5`(右下)
|
|
44
48
|
- 默认位置:`C3`(中心)
|
|
45
49
|
- 分隔符:`;` 或 `;`(支持中英文分号)
|
|
50
|
+
- 位置:`@位置` (如 @B3),可选
|
|
51
|
+
- 负向提示:`--uc:xxx`,可选
|
|
46
52
|
|
|
47
53
|
详细文档请查看插件目录下的 `CHARACTERS_USAGE.md`。
|
|
48
54
|
|