koishi-plugin-p-draw 1.3.5 → 1.3.6

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.
Files changed (2) hide show
  1. package/index.js +31 -28
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -488,21 +488,21 @@ exports.apply = async function apply(ctx, cfg) {
488
488
  }
489
489
  }
490
490
 
491
- // 生成图片使用合并转发;每张图后紧跟实际使用的最终提示词,不附原消息引用。
491
+ // 生成图片使用合并转发;每张图后紧跟实际使用的正负面提示词,不附原消息引用。
492
492
  async function sendImagesAsForward(session, outputs) {
493
493
  const nodes = []
494
494
  const fallback = []
495
495
  for (const output of outputs) {
496
496
  const src = typeof output === 'string' ? output : output.src
497
497
  const prompt = typeof output === 'string' ? '' : String(output.prompt || '')
498
+ const negativePrompt = typeof output === 'string' ? '' : String(output.negativePrompt || '')
498
499
  const materialized = await materializeImageSource(src)
499
500
  const image = Buffer.isBuffer(materialized) ? h.image(materialized) : h.image(src)
500
501
  nodes.push(h('message', image))
501
502
  fallback.push(image)
502
- if (prompt) {
503
- nodes.push(h('message', prompt))
504
- fallback.push(prompt)
505
- }
503
+ const promptText = `Positive:\n${prompt}\n\nNegative:\n${negativePrompt}`
504
+ nodes.push(h('message', promptText))
505
+ fallback.push(promptText)
506
506
  }
507
507
  try {
508
508
  await session.send(h('figure', nodes))
@@ -664,9 +664,10 @@ exports.apply = async function apply(ctx, cfg) {
664
664
  width,
665
665
  height,
666
666
  steps,
667
- cfg: cfgVal,
668
- prompt_id: promptId,
669
- }
667
+ cfg: cfgVal,
668
+ prompt_id: promptId,
669
+ negativePrompt,
670
+ }
670
671
  }
671
672
 
672
673
  // ---------------- LLM 提示词优化 ----------------
@@ -1195,11 +1196,11 @@ exports.apply = async function apply(ctx, cfg) {
1195
1196
 
1196
1197
  // 视觉校验(anima_verify + generation_verifier 移植):对生成的图片跑视觉 LLM,
1197
1198
  // 不合格则用相同提示词重试(最多 multiCandidateCount 张),按多候选规则挑选并返回结果。
1198
- async function verifyGeneratedImages(session, images, userRequest, prompt, size, planCount, unet) {
1199
+ async function verifyGeneratedImages(session, images, userRequest, prompt, size, planCount, unet, negativePrompt) {
1199
1200
  const verifyBaseUrl = String(cfg.verifyLlmBaseUrl || '').trim()
1200
1201
  const verifyModel = String(cfg.verifyLlmModel || '').trim()
1201
1202
  if (!verifyBaseUrl || !verifyModel) {
1202
- return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt }
1203
+ return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt, negativePrompt }
1203
1204
  }
1204
1205
  const passScore = Math.max(0, Math.min(10, parseInt(cfg.multiVerifyPassScore) || 6))
1205
1206
  const candidateCount = Math.max(1, Math.min(3, parseInt(cfg.multiCandidateCount) || 2))
@@ -1207,9 +1208,10 @@ exports.apply = async function apply(ctx, cfg) {
1207
1208
  const systemPrompt = buildVerifySystemPrompt(true, planCount)
1208
1209
  const candidates = []
1209
1210
  let lastVerdict = null
1210
- let retries = 0
1211
- let currentImages = images
1212
- let currentPrompt = prompt
1211
+ let retries = 0
1212
+ let currentImages = images
1213
+ let currentPrompt = prompt
1214
+ let currentNegativePrompt = negativePrompt
1213
1215
 
1214
1216
  async function verifyOnce(imgs, userReq) {
1215
1217
  const controller = new AbortController()
@@ -1307,15 +1309,15 @@ exports.apply = async function apply(ctx, cfg) {
1307
1309
  data = extractVerifyJson(reply)
1308
1310
  } catch (e) {
1309
1311
  logger.warn(`多人视觉校验失败:${e.message}`)
1310
- return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images, prompt: currentPrompt }
1312
+ return { ok: true, degraded: true, message: session.text('.multi-verify-error', [String(e && e.message || e)]), verdict: null, outputs: images, prompt: currentPrompt, negativePrompt: currentNegativePrompt }
1311
1313
  }
1312
1314
  if (!data) {
1313
1315
  logger.warn(`多人视觉校验返回无法解析:${reply.slice(0, 200)}`)
1314
- return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt: currentPrompt }
1316
+ return { ok: true, degraded: true, message: '', verdict: null, outputs: images, prompt: currentPrompt, negativePrompt: currentNegativePrompt }
1315
1317
  }
1316
1318
  const verdict = verdictFromData(data)
1317
1319
  verdict.skipped = false
1318
- candidates.push({ outputs: currentImages, verdict, prompt: currentPrompt })
1320
+ candidates.push({ outputs: currentImages, verdict, prompt: currentPrompt, negativePrompt: currentNegativePrompt })
1319
1321
  selectedOutputs = currentImages
1320
1322
  selectedVerdict = verdict
1321
1323
 
@@ -1329,12 +1331,13 @@ exports.apply = async function apply(ctx, cfg) {
1329
1331
  if (hint) {
1330
1332
  currentPrompt = `${userRequest}\n【上次问题,请修正】${hint}`
1331
1333
  }
1332
- const regen = await runComfyGenerate(currentPrompt, size, { unet })
1334
+ const regen = await runComfyGenerate(currentPrompt, size, { unet, negativePrompt: currentNegativePrompt })
1333
1335
  if (!regen.ok) {
1334
1336
  logger.warn(`多人校验重试生成失败:${regen.message}`)
1335
1337
  break
1336
1338
  }
1337
- currentImages = regen.outputs
1339
+ currentImages = regen.outputs
1340
+ currentNegativePrompt = regen.negativePrompt || currentNegativePrompt
1338
1341
  }
1339
1342
 
1340
1343
  // 多候选挑选
@@ -1345,7 +1348,7 @@ exports.apply = async function apply(ctx, cfg) {
1345
1348
  selectedOutputs = best.outputs
1346
1349
  selectedVerdict = best.verdict
1347
1350
  if (!multiAccepted && !cfg.multiSendDegradedCandidate) {
1348
- return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [], prompt: best.prompt }
1351
+ return { ok: false, discarded: true, message: session.text('.multi-verify-discarded'), verdict: selectedVerdict, outputs: [], prompt: best.prompt, negativePrompt: best.negativePrompt }
1349
1352
  }
1350
1353
  const noteParts = []
1351
1354
  if (multiAccepted) {
@@ -1354,7 +1357,7 @@ exports.apply = async function apply(ctx, cfg) {
1354
1357
  noteParts.push(session.text('.multi-verify-degraded', selectedVerdict.issues.length ? '(' + selectedVerdict.issues.join(';').slice(0, 80) + ')' : ''))
1355
1358
  }
1356
1359
  if (retries) noteParts.push(session.text('.multi-verify-failed', [selectedVerdict.issues.length ? ':' + selectedVerdict.issues.join(';').slice(0, 80) : '', retries]))
1357
- return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs, prompt: best.prompt }
1360
+ return { ok: true, degraded: false, message: noteParts.join('\n'), verdict: selectedVerdict, outputs: selectedOutputs, prompt: best.prompt, negativePrompt: best.negativePrompt }
1358
1361
  }
1359
1362
 
1360
1363
  function buildVerifySystemPrompt(multiPerson, planCount) {
@@ -1367,7 +1370,7 @@ exports.apply = async function apply(ctx, cfg) {
1367
1370
  }
1368
1371
 
1369
1372
  // 共享批量执行器:一次性扣除总价,逐张生成,单张失败只退该张单价。
1370
- // runOne(i) 需返回 { ok, outputs, seed, prompt, message? };返回数组为多张输出(如视觉校验候选)。
1373
+ // runOne(i) 需返回 { ok, outputs, seed, prompt, negativePrompt, message? };返回数组为多张输出(如视觉校验候选)。
1371
1374
  async function executeBatch(USERID, isAdmin, count, unitPrice, runOne) {
1372
1375
  const results = []
1373
1376
  let successCount = 0
@@ -1381,7 +1384,7 @@ exports.apply = async function apply(ctx, cfg) {
1381
1384
  const outputs = Array.isArray(item.outputs) ? item.outputs : (item.outputs ? [item.outputs] : [])
1382
1385
  if (item.ok && outputs.length) {
1383
1386
  successCount += 1
1384
- results.push({ i, ok: true, outputs, seed: item.seed, prompt: item.prompt || '', note: item.note || '' })
1387
+ results.push({ i, ok: true, outputs, seed: item.seed, prompt: item.prompt || '', negativePrompt: item.negativePrompt || '', note: item.note || '' })
1385
1388
  } else {
1386
1389
  if (!isAdmin) await refundP(USERID, unitPrice)
1387
1390
  if (cfg.outputLogs) logger.warn(`批量第 ${i + 1} 张生成失败(${USERID}):${item.message || '无输出'}`)
@@ -1477,11 +1480,11 @@ exports.apply = async function apply(ctx, cfg) {
1477
1480
  }
1478
1481
  if (!result.ok || !result.outputs || !result.outputs.length) return result
1479
1482
  if (!cfg.multiVerifyEnabled) {
1480
- return { ok: true, outputs: result.outputs, seed: result.seed, prompt: finalPrompt, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
1483
+ return { ok: true, outputs: result.outputs, seed: result.seed, prompt: finalPrompt, negativePrompt: result.negativePrompt, note: session.text('.multi-degraded', ['(未启用校验或未配置视觉模型)']) }
1481
1484
  }
1482
- const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet)
1485
+ const verified = await verifyGeneratedImages(session, result.outputs, text, finalPrompt, size, plan.characters.length, unet, result.negativePrompt || multiNegative)
1483
1486
  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 || '' }
1487
+ return { ok: true, outputs: verified.outputs, seed: result.seed, prompt: verified.prompt || finalPrompt, negativePrompt: verified.negativePrompt || result.negativePrompt || multiNegative, note: verified.message || '' }
1485
1488
  }
1486
1489
 
1487
1490
  const { results, successCount } = await executeBatch(USERID, isAdmin, count, price, runOne)
@@ -1495,7 +1498,7 @@ exports.apply = async function apply(ctx, cfg) {
1495
1498
  for (const item of results) {
1496
1499
  if (item.ok) {
1497
1500
  allOutputs.push(...item.outputs)
1498
- forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
1501
+ forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt, negativePrompt: item.negativePrompt })))
1499
1502
  if (item.seed != null) seeds.push(item.seed)
1500
1503
  if (item.note) notes.push(item.note)
1501
1504
  } else {
@@ -1669,7 +1672,7 @@ exports.apply = async function apply(ctx, cfg) {
1669
1672
  for (const item of results) {
1670
1673
  if (item.ok) {
1671
1674
  allOutputs.push(...item.outputs)
1672
- forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || stagePrompts[item.i] || '' })))
1675
+ forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || stagePrompts[item.i] || '', negativePrompt: item.negativePrompt })))
1673
1676
  if (item.seed != null) seeds.push(item.seed)
1674
1677
  } else {
1675
1678
  failures.push(`第 ${item.i + 1} 阶段:${item.message}`)
@@ -2393,7 +2396,7 @@ exports.apply = async function apply(ctx, cfg) {
2393
2396
  for (const item of results) {
2394
2397
  if (item.ok) {
2395
2398
  allOutputs.push(...item.outputs)
2396
- forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt })))
2399
+ forwardOutputs.push(...item.outputs.map(src => ({ src, prompt: item.prompt || finalPrompt, negativePrompt: item.negativePrompt })))
2397
2400
  if (item.seed != null) seeds.push(item.seed)
2398
2401
  } else {
2399
2402
  failures.push(`第 ${item.i + 1} 张:${item.message}`)
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-p-draw",
3
3
  "description": "p点-绘图,连接本地 ComfyUI 生图。支持自然语言优化、尺寸选择、画师组、固定角色、生成队列,需要配合 p-qiandao 使用",
4
- "version": "1.3.5",
4
+ "version": "1.3.6",
5
5
  "main": "index.js",
6
6
  "files": [
7
7
  "index.js",