koishi-plugin-p-draw 1.3.2 → 1.3.5
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/index.js +89 -62
- package/lib/parse.js +14 -0
- package/lib/tags.js +6 -0
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -139,10 +139,10 @@ exports.Config = Schema.object({
|
|
|
139
139
|
// ------------------------------------------------------------------
|
|
140
140
|
const {
|
|
141
141
|
normalizeBaseUrl, escapeRe, parseGenerationSize, parseBatchCount, parseSeed, parseDenoise,
|
|
142
|
-
stripRawPrefix, parseNameTags, parsePresetList, mergeTagText,
|
|
142
|
+
stripRawPrefix, splitPositiveNegativePrompt, parseNameTags, parsePresetList, mergeTagText,
|
|
143
143
|
} = require('./lib/parse')
|
|
144
144
|
const {
|
|
145
|
-
splitTags, canonicalTagText, joinPromptParts, cleanContentTags, appendInlineProtectedTags,
|
|
145
|
+
splitTags, canonicalTagText, joinPromptParts, mergeNegativePrompts, cleanContentTags, appendInlineProtectedTags,
|
|
146
146
|
NO_ARTIST_RE, NO_STYLE_RE,
|
|
147
147
|
} = require('./lib/tags')
|
|
148
148
|
const {
|
|
@@ -488,17 +488,27 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
488
488
|
}
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
//
|
|
491
|
+
// 生成图片使用合并转发;每张图后紧跟实际使用的最终提示词,不附原消息引用。
|
|
492
492
|
async function sendImagesAsForward(session, outputs) {
|
|
493
|
-
const
|
|
493
|
+
const nodes = []
|
|
494
|
+
const fallback = []
|
|
495
|
+
for (const output of outputs) {
|
|
496
|
+
const src = typeof output === 'string' ? output : output.src
|
|
497
|
+
const prompt = typeof output === 'string' ? '' : String(output.prompt || '')
|
|
494
498
|
const materialized = await materializeImageSource(src)
|
|
495
|
-
|
|
496
|
-
|
|
499
|
+
const image = Buffer.isBuffer(materialized) ? h.image(materialized) : h.image(src)
|
|
500
|
+
nodes.push(h('message', image))
|
|
501
|
+
fallback.push(image)
|
|
502
|
+
if (prompt) {
|
|
503
|
+
nodes.push(h('message', prompt))
|
|
504
|
+
fallback.push(prompt)
|
|
505
|
+
}
|
|
506
|
+
}
|
|
497
507
|
try {
|
|
498
|
-
await session.send(h('figure',
|
|
508
|
+
await session.send(h('figure', nodes))
|
|
499
509
|
} catch (e) {
|
|
500
510
|
logger.warn(`发送转发图片失败:${e.message}`)
|
|
501
|
-
await session.send(
|
|
511
|
+
await session.send(fallback)
|
|
502
512
|
}
|
|
503
513
|
}
|
|
504
514
|
|
|
@@ -1189,7 +1199,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1189
1199
|
const verifyBaseUrl = String(cfg.verifyLlmBaseUrl || '').trim()
|
|
1190
1200
|
const verifyModel = String(cfg.verifyLlmModel || '').trim()
|
|
1191
1201
|
if (!verifyBaseUrl || !verifyModel) {
|
|
1192
|
-
return { ok: true, degraded: true, message: '', verdict: null, outputs: images }
|
|
1202
|
+
return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt }
|
|
1193
1203
|
}
|
|
1194
1204
|
const passScore = Math.max(0, Math.min(10, parseInt(cfg.multiVerifyPassScore) || 6))
|
|
1195
1205
|
const candidateCount = Math.max(1, Math.min(3, parseInt(cfg.multiCandidateCount) || 2))
|
|
@@ -1297,11 +1307,11 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1297
1307
|
data = extractVerifyJson(reply)
|
|
1298
1308
|
} catch (e) {
|
|
1299
1309
|
logger.warn(`多人视觉校验失败:${e.message}`)
|
|
1300
|
-
return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images }
|
|
1310
|
+
return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images, prompt: currentPrompt }
|
|
1301
1311
|
}
|
|
1302
1312
|
if (!data) {
|
|
1303
1313
|
logger.warn(`多人视觉校验返回无法解析:${reply.slice(0, 200)}`)
|
|
1304
|
-
return { ok: true, degraded: true, message: '', verdict: null, outputs: images }
|
|
1314
|
+
return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt: currentPrompt }
|
|
1305
1315
|
}
|
|
1306
1316
|
const verdict = verdictFromData(data)
|
|
1307
1317
|
verdict.skipped = false
|
|
@@ -1335,7 +1345,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1335
1345
|
selectedOutputs = best.outputs
|
|
1336
1346
|
selectedVerdict = best.verdict
|
|
1337
1347
|
if (!multiAccepted && !cfg.multiSendDegradedCandidate) {
|
|
1338
|
-
return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [] }
|
|
1348
|
+
return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [], prompt: best.prompt }
|
|
1339
1349
|
}
|
|
1340
1350
|
const noteParts = []
|
|
1341
1351
|
if (multiAccepted) {
|
|
@@ -1344,7 +1354,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1344
1354
|
noteParts.push(session.text('.multi-verify-degraded', selectedVerdict.issues.length ? '(' + selectedVerdict.issues.join(';').slice(0, 80) + ')' : ''))
|
|
1345
1355
|
}
|
|
1346
1356
|
if (retries) noteParts.push(session.text('.multi-verify-failed', [selectedVerdict.issues.length ? ':' + selectedVerdict.issues.join(';').slice(0, 80) : '', retries]))
|
|
1347
|
-
return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs }
|
|
1357
|
+
return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs, prompt: best.prompt }
|
|
1348
1358
|
}
|
|
1349
1359
|
|
|
1350
1360
|
function buildVerifySystemPrompt(multiPerson, planCount) {
|
|
@@ -1357,7 +1367,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1357
1367
|
}
|
|
1358
1368
|
|
|
1359
1369
|
// 共享批量执行器:一次性扣除总价,逐张生成,单张失败只退该张单价。
|
|
1360
|
-
// runOne(i) 需返回 { ok, outputs, seed, message? };返回数组为多张输出(如视觉校验候选)。
|
|
1370
|
+
// runOne(i) 需返回 { ok, outputs, seed, prompt, message? };返回数组为多张输出(如视觉校验候选)。
|
|
1361
1371
|
async function executeBatch(USERID, isAdmin, count, unitPrice, runOne) {
|
|
1362
1372
|
const results = []
|
|
1363
1373
|
let successCount = 0
|
|
@@ -1371,7 +1381,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1371
1381
|
const outputs = Array.isArray(item.outputs) ? item.outputs : (item.outputs ? [item.outputs] : [])
|
|
1372
1382
|
if (item.ok && outputs.length) {
|
|
1373
1383
|
successCount += 1
|
|
1374
|
-
results.push({ i, ok: true, outputs, seed: item.seed, note: item.note || '' })
|
|
1384
|
+
results.push({ i, ok: true, outputs, seed: item.seed, prompt: item.prompt || '', note: item.note || '' })
|
|
1375
1385
|
} else {
|
|
1376
1386
|
if (!isAdmin) await refundP(USERID, unitPrice)
|
|
1377
1387
|
if (cfg.outputLogs) logger.warn(`批量第 ${i + 1} 张生成失败(${USERID}):${item.message || '无输出'}`)
|
|
@@ -1465,25 +1475,27 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1465
1475
|
} else {
|
|
1466
1476
|
try { result = await runComfyGenerate(finalPrompt, size, { negativePrompt: multiNegative, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1467
1477
|
}
|
|
1468
|
-
if (!result.ok || !result.outputs || !result.outputs.length) return result
|
|
1469
|
-
if (!cfg.multiVerifyEnabled) {
|
|
1470
|
-
return { ok: true, outputs: result.outputs, seed: result.seed, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
|
|
1471
|
-
}
|
|
1472
|
-
const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet)
|
|
1473
|
-
if (!verified.ok) return { ok: false, message: verified.message }
|
|
1474
|
-
return { ok: true, outputs: verified.outputs, seed: result.seed, note: verified.message || '' }
|
|
1478
|
+
if (!result.ok || !result.outputs || !result.outputs.length) return result
|
|
1479
|
+
if (!cfg.multiVerifyEnabled) {
|
|
1480
|
+
return { ok: true, outputs: result.outputs, seed: result.seed, prompt: finalPrompt, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
|
|
1481
|
+
}
|
|
1482
|
+
const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet)
|
|
1483
|
+
if (!verified.ok) return { ok: false, message: verified.message }
|
|
1484
|
+
return { ok: true, outputs: verified.outputs, seed: result.seed, prompt: verified.prompt || finalPrompt, note: verified.message || '' }
|
|
1475
1485
|
}
|
|
1476
1486
|
|
|
1477
1487
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, price, runOne)
|
|
1478
1488
|
|
|
1479
|
-
// 汇总
|
|
1480
|
-
const allOutputs = []
|
|
1481
|
-
const
|
|
1489
|
+
// 汇总
|
|
1490
|
+
const allOutputs = []
|
|
1491
|
+
const forwardOutputs = []
|
|
1492
|
+
const notes = []
|
|
1482
1493
|
const seeds = []
|
|
1483
1494
|
const failures = []
|
|
1484
1495
|
for (const item of results) {
|
|
1485
|
-
if (item.ok) {
|
|
1486
|
-
allOutputs.push(...item.outputs)
|
|
1496
|
+
if (item.ok) {
|
|
1497
|
+
allOutputs.push(...item.outputs)
|
|
1498
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
|
|
1487
1499
|
if (item.seed != null) seeds.push(item.seed)
|
|
1488
1500
|
if (item.note) notes.push(item.note)
|
|
1489
1501
|
} else {
|
|
@@ -1499,7 +1511,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1499
1511
|
if (cfg.outputLogs) logger.success(`${USERID} 多人生成成功 ${successCount}/${count} 张`)
|
|
1500
1512
|
|
|
1501
1513
|
// 发图:合并转发,不引用原指令
|
|
1502
|
-
await sendImagesAsForward(session,
|
|
1514
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
1503
1515
|
|
|
1504
1516
|
const reply = []
|
|
1505
1517
|
if (count > 1) {
|
|
@@ -1638,23 +1650,26 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1638
1650
|
// 单阶段生成(共用 seed)
|
|
1639
1651
|
const runOne = async (i) => {
|
|
1640
1652
|
let result
|
|
1641
|
-
if (cfg.queueEnabled) {
|
|
1642
|
-
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1643
|
-
} else {
|
|
1644
|
-
try { result = await runComfyGenerate(stagePrompts[i], size, { seed, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1653
|
+
if (cfg.queueEnabled) {
|
|
1654
|
+
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1655
|
+
} else {
|
|
1656
|
+
try { result = await runComfyGenerate(stagePrompts[i], size, { seed, unet }) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
1657
|
+
}
|
|
1658
|
+
result.prompt = stagePrompts[i]
|
|
1659
|
+
return result
|
|
1647
1660
|
}
|
|
1648
1661
|
|
|
1649
1662
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, price, runOne)
|
|
1650
1663
|
|
|
1651
|
-
// 汇总
|
|
1652
|
-
const allOutputs = []
|
|
1653
|
-
const
|
|
1664
|
+
// 汇总
|
|
1665
|
+
const allOutputs = []
|
|
1666
|
+
const forwardOutputs = []
|
|
1667
|
+
const seeds = []
|
|
1654
1668
|
const failures = []
|
|
1655
1669
|
for (const item of results) {
|
|
1656
|
-
if (item.ok) {
|
|
1657
|
-
allOutputs.push(...item.outputs)
|
|
1670
|
+
if (item.ok) {
|
|
1671
|
+
allOutputs.push(...item.outputs)
|
|
1672
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || stagePrompts[item.i] || '' })))
|
|
1658
1673
|
if (item.seed != null) seeds.push(item.seed)
|
|
1659
1674
|
} else {
|
|
1660
1675
|
failures.push(`第 ${item.i + 1} 阶段:${item.message}`)
|
|
@@ -1669,7 +1684,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
1669
1684
|
if (cfg.outputLogs) logger.success(`${USERID} 连续图生成成功 ${successCount}/${count} 阶段(seed=${seed})`)
|
|
1670
1685
|
|
|
1671
1686
|
// 发图:合并转发,不引用原指令
|
|
1672
|
-
await sendImagesAsForward(session,
|
|
1687
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
1673
1688
|
|
|
1674
1689
|
const reply = []
|
|
1675
1690
|
reply.push(session.text('.series-ok', [count * price, successCount, seed]))
|
|
@@ -2216,10 +2231,12 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2216
2231
|
const pcheck = await precheckPoints(session, USERID, isAdmin, count * cfg.price)
|
|
2217
2232
|
if (!pcheck.ok) return pcheck.message
|
|
2218
2233
|
|
|
2219
|
-
// 原样模式
|
|
2220
|
-
const stripped = stripRawPrefix(text)
|
|
2221
|
-
const raw = stripped.raw
|
|
2222
|
-
const
|
|
2234
|
+
// 原样模式
|
|
2235
|
+
const stripped = stripRawPrefix(text)
|
|
2236
|
+
const raw = stripped.raw
|
|
2237
|
+
const promptSections = splitPositiveNegativePrompt(stripped.prompt)
|
|
2238
|
+
const userPrompt = promptSections.positive
|
|
2239
|
+
const userNegativePrompt = promptSections.negative
|
|
2223
2240
|
if (!userPrompt) return session.text('.no-prompt')
|
|
2224
2241
|
|
|
2225
2242
|
// ComfyUI 就绪
|
|
@@ -2300,9 +2317,15 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2300
2317
|
}
|
|
2301
2318
|
|
|
2302
2319
|
// i2i 运行参数:模式(style/ootd/plain)、--denoise 覆盖值、检测到的能力
|
|
2303
|
-
const i2iRun = i2iImage
|
|
2304
|
-
? { i2iImage, i2i: { mode: (i2iOpts && i2iOpts.mode) || 'plain', denoise: denoise != null ? denoise : null, caps: (i2iOpts && i2iOpts.caps) || null } }
|
|
2305
|
-
: {}
|
|
2320
|
+
const i2iRun = i2iImage
|
|
2321
|
+
? { i2iImage, i2i: { mode: (i2iOpts && i2iOpts.mode) || 'plain', denoise: denoise != null ? denoise : null, caps: (i2iOpts && i2iOpts.caps) || null } }
|
|
2322
|
+
: {}
|
|
2323
|
+
// 用户手写了 negative: 区块时,默认负面词仍保留;用户 tag 只补充未出现的部分。
|
|
2324
|
+
const generationOverrides = Object.assign(
|
|
2325
|
+
{ unet, seed },
|
|
2326
|
+
i2iRun,
|
|
2327
|
+
userNegativePrompt ? { negativePrompt: mergeNegativePrompts(cfg.negativePrompt, userNegativePrompt) } : {},
|
|
2328
|
+
)
|
|
2306
2329
|
|
|
2307
2330
|
// 性能:按张优化(perImageOptimize)时联网搜索只做一次,各图复用同一份结果
|
|
2308
2331
|
const searchCache = perImageOptimize && wantsWebSearch(userPrompt) ? await webSearch(userPrompt) : null
|
|
@@ -2314,7 +2337,8 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2314
2337
|
const optimized = await optimizePrompt(session, userPrompt, true, i2iRule, searchCache)
|
|
2315
2338
|
p = appendInlineProtectedTags(composePrompt(optimized.prompt || userPrompt, raw).prompt, userPrompt, raw)
|
|
2316
2339
|
}
|
|
2317
|
-
|
|
2340
|
+
const generated = await runComfyGenerate(p, parsedSize.size, generationOverrides)
|
|
2341
|
+
return { ...generated, prompt: p }
|
|
2318
2342
|
}, { USERID, isAdmin, totalPrice: count * cfg.price })
|
|
2319
2343
|
if (!queued.ok) return queued.message
|
|
2320
2344
|
const queuedTasks = queued.tasks
|
|
@@ -2352,22 +2376,25 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2352
2376
|
let result
|
|
2353
2377
|
if (cfg.queueEnabled) {
|
|
2354
2378
|
try { result = await queuedTasks[i] } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
2355
|
-
} else {
|
|
2356
|
-
try { result = await runComfyGenerate(p, parsedSize.size,
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2379
|
+
} else {
|
|
2380
|
+
try { result = await runComfyGenerate(p, parsedSize.size, generationOverrides) } catch (e) { result = { ok: false, message: `生成失败:${e.message}` } }
|
|
2381
|
+
}
|
|
2382
|
+
if (!result.prompt) result.prompt = p
|
|
2383
|
+
return result
|
|
2359
2384
|
}
|
|
2360
2385
|
|
|
2361
2386
|
const { results, successCount } = await executeBatch(USERID, isAdmin, count, cfg.price, runOne)
|
|
2362
2387
|
|
|
2363
|
-
// 汇总
|
|
2364
|
-
const allOutputs = []
|
|
2365
|
-
const
|
|
2366
|
-
const
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2388
|
+
// 汇总
|
|
2389
|
+
const allOutputs = []
|
|
2390
|
+
const forwardOutputs = []
|
|
2391
|
+
const seeds = []
|
|
2392
|
+
const failures = []
|
|
2393
|
+
for (const item of results) {
|
|
2394
|
+
if (item.ok) {
|
|
2395
|
+
allOutputs.push(...item.outputs)
|
|
2396
|
+
forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
|
|
2397
|
+
if (item.seed != null) seeds.push(item.seed)
|
|
2371
2398
|
} else {
|
|
2372
2399
|
failures.push(`第 ${item.i + 1} 张:${item.message}`)
|
|
2373
2400
|
}
|
|
@@ -2381,7 +2408,7 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2381
2408
|
if (cfg.outputLogs) logger.success(`${USERID} 生成成功 ${successCount}/${count} 张`)
|
|
2382
2409
|
|
|
2383
2410
|
// 发图:合并转发,不引用原指令
|
|
2384
|
-
await sendImagesAsForward(session,
|
|
2411
|
+
await sendImagesAsForward(session, forwardOutputs)
|
|
2385
2412
|
|
|
2386
2413
|
const reply = []
|
|
2387
2414
|
if (count > 1) {
|
|
@@ -2534,5 +2561,5 @@ exports.apply = async function apply(ctx, cfg) {
|
|
|
2534
2561
|
})
|
|
2535
2562
|
|
|
2536
2563
|
// 暴露内部接口供自动化测试调用(Koishi 忽略 apply 返回值,不影响生产行为)
|
|
2537
|
-
return { couponConfirmFlow, buyCouponsAndConsume, normalizeConfirm, resolveCouponPrice, handleGenerateI2I, extractImageFromSession, uploadImageToComfyui, taggerImage, animaI2IWorkflow, animaStyleI2IWorkflow, animaOotdI2IWorkflow, buildI2IWorkflow, detectI2ICapabilities, parseDenoise, sendNotices, sendImagesAsForward }
|
|
2564
|
+
return { couponConfirmFlow, buyCouponsAndConsume, normalizeConfirm, resolveCouponPrice, handleGenerateI2I, extractImageFromSession, uploadImageToComfyui, taggerImage, animaI2IWorkflow, animaStyleI2IWorkflow, animaOotdI2IWorkflow, buildI2IWorkflow, detectI2ICapabilities, parseDenoise, sendNotices, sendImagesAsForward, executeBatch }
|
|
2538
2565
|
}
|
package/lib/parse.js
CHANGED
|
@@ -186,6 +186,19 @@ function stripRawPrefix(prompt) {
|
|
|
186
186
|
return { raw: false, prompt: text }
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
// 将用户手写的正负面区块拆开。只识别独立行标记,避免把普通 tag(如 negative space)误判为负面段。
|
|
190
|
+
const NEGATIVE_SECTION_RE = /(?:^|\r?\n)\s*(?:negative(?:\s*prompt)?|negative_prompt|负面(?:提示词|词)?)\s*[::]\s*/i
|
|
191
|
+
|
|
192
|
+
function splitPositiveNegativePrompt(prompt) {
|
|
193
|
+
const text = String(prompt || '').trim()
|
|
194
|
+
const match = NEGATIVE_SECTION_RE.exec(text)
|
|
195
|
+
if (!match) return { positive: text, negative: '' }
|
|
196
|
+
return {
|
|
197
|
+
positive: text.slice(0, match.index).trim(),
|
|
198
|
+
negative: text.slice(match.index + match[0].length).trim(),
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
189
202
|
function mergeTagText(existing, addition) {
|
|
190
203
|
const tags = []
|
|
191
204
|
const seen = new Set()
|
|
@@ -259,6 +272,7 @@ module.exports = {
|
|
|
259
272
|
parseDenoise,
|
|
260
273
|
RAW_PREFIXES,
|
|
261
274
|
stripRawPrefix,
|
|
275
|
+
splitPositiveNegativePrompt,
|
|
262
276
|
mergeTagText,
|
|
263
277
|
parseNameTags,
|
|
264
278
|
parsePresetList,
|
package/lib/tags.js
CHANGED
|
@@ -236,6 +236,11 @@ function joinPromptParts(parts) {
|
|
|
236
236
|
return tags.join(', ')
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// 默认负面词始终保留,用户手写负面词只补充未出现的 tag。
|
|
240
|
+
function mergeNegativePrompts(defaultPrompt, userPrompt) {
|
|
241
|
+
return joinPromptParts([defaultPrompt, userPrompt])
|
|
242
|
+
}
|
|
243
|
+
|
|
239
244
|
module.exports = {
|
|
240
245
|
splitTags,
|
|
241
246
|
normalizeTagKey,
|
|
@@ -258,4 +263,5 @@ module.exports = {
|
|
|
258
263
|
isCharacterIdentityTag,
|
|
259
264
|
cleanContentTags,
|
|
260
265
|
joinPromptParts,
|
|
266
|
+
mergeNegativePrompts,
|
|
261
267
|
}
|