koishi-plugin-hhs-huatu 1.2.2 → 1.2.4
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/commands/account.d.ts +0 -0
- package/lib/commands/director.d.ts +0 -0
- package/lib/commands/member.d.ts +0 -0
- package/lib/commands/novelai.d.ts +0 -0
- package/lib/commands/queue.d.ts +0 -0
- package/lib/commands/redraw.d.ts +0 -0
- package/lib/config.d.ts +1 -1
- package/lib/imageAudit.d.ts +0 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +102 -120
- package/lib/interactions/inpaint.d.ts +0 -0
- package/lib/interactions/preciseRef.d.ts +0 -0
- package/lib/membershipSystem.d.ts +9 -2
- package/lib/queueSystem.d.ts +2 -2
- package/lib/runtime.d.ts +0 -0
- package/lib/services/director.d.ts +0 -0
- package/lib/services/generateImage.d.ts +0 -0
- package/lib/services/points.d.ts +0 -0
- package/lib/types.d.ts +0 -0
- package/lib/utils.d.ts +0 -0
- package/package.json +1 -1
- package/readme.md +6 -5
- package/lib/NaiInpaintService.d.ts +0 -31
- package/lib/imageService.d.ts +0 -59
- package/lib/infillPayload.d.ts +0 -94
- package/lib/services/currency.d.ts +0 -46
|
File without changes
|
|
File without changes
|
package/lib/commands/member.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/lib/commands/queue.d.ts
CHANGED
|
File without changes
|
package/lib/commands/redraw.d.ts
CHANGED
|
File without changes
|
package/lib/config.d.ts
CHANGED
|
@@ -175,7 +175,7 @@ interface ParamConfig {
|
|
|
175
175
|
export interface Config extends PromptConfig, ParamConfig {
|
|
176
176
|
type: 'token' | 'login' | 'naifu' | 'sd-webui' | 'stable-horde' | 'comfyui';
|
|
177
177
|
token?: string | string[];
|
|
178
|
-
tokenStrategy?: 'round-robin' | 'random' | 'fallback' | 'parallel';
|
|
178
|
+
tokenStrategy?: 'round-robin' | 'first-available' | 'random' | 'fallback' | 'parallel';
|
|
179
179
|
email?: string;
|
|
180
180
|
password?: string;
|
|
181
181
|
authLv?: Computed<number>;
|
package/lib/imageAudit.d.ts
CHANGED
|
File without changes
|
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
2
|
import { Config } from './config';
|
|
3
|
-
export declare const usage = "\n# \uD83C\uDFA8 hhs-huatu \u63D2\u4EF6\n\n> **\u57FA\u4E8E [novelai-bot](https://bot.novelai.dev/) \u7684\u589E\u5F3A\u7248 AI \u7ED8\u56FE\u63D2\u4EF6\uFF0C\u63D0\u4F9B\u66F4\u667A\u80FD\u3001\u4FBF\u6377\u7684 NovelAI \u4F53\u9A8C\u3002**\n\n[](https://qm.qq.com/q/4nKKvckKbu) [](https://github.com/hhs2275/koishi-plugin-hhs-huatu)\n\n### \u2728 \u6838\u5FC3\u4EAE\u70B9\n\u65B0\u7248\u672C\u6539\u7528\u6570\u636E\u5E93\u3002\u4F60\u7684\u4F1A\u5458\u6570\u636E\u5728 \u4F60\u7684 Koishi \u6839\u76EE\u5F55/data/hhs-huatu-user-data.json\u3002\u628A\u5B83\u653E\u5728hhs-huatu-import\u6587\u4EF6\u5939\u4E0B\u9762\uFF0C\u7136\u540E\u53D1\u201C\u4F1A\u5458\u8C03\u8BD5--import\u201D\u6307\u4EE4\uFF0C\u5C31\u53EF\u4EE5\u628A\u6570\u636E\u5BFC\u5165\u6570\u636E\u5E93\u3002\n\n\u672C\u63D2\u4EF6\u9488\u5BF9 **NovelAI V4 / V4.5 / V5** \u6A21\u578B\u8FDB\u884C\u4E86\u6DF1\u5EA6\u9002\u914D\uFF0C\u9884\u8BBE\u5FEB\u6377\u6307\u4EE4\uFF0C\u52A9\u4F60\u5FEB\u901F\u5207\u6362\u6A21\u578B\uFF1A\n\n| \u6307\u4EE4 | \u5BF9\u5E94\u6A21\u578B (Model) | \u8BF4\u660E |\n| :--- | :--- | :--- |\n| `nai4` | `nai-diffusion-4-full` | V4 \u5168\u91CF\u6A21\u578B |\n| `nai4c` | `nai-diffusion-4-curated` | V4 \u7CBE\u9009\u6A21\u578B |\n| `nai4-5` | `nai-diffusion-4-5-full` | V4.5 \u5168\u91CF\u6A21\u578B |\n| `nai4-5c` | `nai-diffusion-4-5-curated` | V4.5 \u7CBE\u9009\u6A21\u578B |\n| `nai5` | `nai-diffusion-5-full` | V5 \u5168\u91CF\u6A21\u578B |\n| `nai5c` | `nai-diffusion-5-curated` | V5 \u7CBE\u9009\u6A21\u578B |\n\n### \uD83D\uDEE0\uFE0F \u529F\u80FD\u5217\u8868\n\n**\uD83C\uDFA8 \u7ED8\u56FE\u6838\u5FC3**\n* **\u5168\u9762\u652F\u6301**\uFF1A\u6587\u751F\u56FE\u3001\u56FE\u751F\u56FE\u57FA\u7840\u529F\u80FD\u5B8C\u6574\u3002\n* **\u5C40\u90E8\u91CD\u7ED8 (Inpaint)**\uFF1A\u652F\u6301\u5BF9\u56FE\u7247\u7279\u5B9A\u533A\u57DF\u8FDB\u884C\u91CD\u7ED8\u3002\n* **\u5BFC\u6F14\u5DE5\u5177**\uFF1Anovelai\u5B98\u7F51\u7684\u5BFC\u6F14\u5DE5\u5177\u529F\u80FD\u3002\n* **V4 \u89D2\u8272\u63D0\u793A\u8BCD**\uFF1Anovelai\u5B98\u7F51\u7684\u591A\u89D2\u8272\u63D0\u793A\u8BCD\u7CFB\u7EDF\u3002\n\n**\u2699\uFE0F \u7CFB\u7EDF\u4E0E\u7BA1\u7406**\n* **\u9AD8\u5E76\u53D1\u4F18\u5316**\uFF1A\u5185\u7F6E\u961F\u5217\u7CFB\u7EDF\u4E0E Token \u6C60\u8F6E\u8BE2\uFF0C\u591A\u8D26\u53F7\u8D1F\u8F7D\u5747\u8861\u3002\n* **\u4F1A\u5458\u7BA1\u7406**\uFF1A\u652F\u6301\u7528\u6237\u5206\u7EA7\u7BA1\u7406\uFF1B\u53EF\u914D\u7F6E\u4EC5\u4F1A\u5458\u4F7F\u7528\u4F1A\u6D88\u8017\u914D\u989D\u7684 nai5 / nai5c\u3002\n* **\u667A\u80FD\u5BA1\u6838**\uFF1A\u96C6\u6210\u817E\u8BAF AI \u6216 API4AI\uFF0C\u81EA\u52A8\u8FC7\u6EE4\u8FDD\u89C4\u5185\u5BB9\u3002\n\n### \uD83D\uDDD3\uFE0F \u5F00\u53D1\u8BA1\u5212\n- [\u221A] **\u70B9\u6570\u63A7\u5236\u7CFB\u7EDF**\uFF1A\
|
|
3
|
+
export declare const usage = "\n# \uD83C\uDFA8 hhs-huatu \u63D2\u4EF6\n\n> **\u57FA\u4E8E [novelai-bot](https://bot.novelai.dev/) \u7684\u589E\u5F3A\u7248 AI \u7ED8\u56FE\u63D2\u4EF6\uFF0C\u63D0\u4F9B\u66F4\u667A\u80FD\u3001\u4FBF\u6377\u7684 NovelAI \u4F53\u9A8C\u3002**\n\n[](https://qm.qq.com/q/4nKKvckKbu) [](https://github.com/hhs2275/koishi-plugin-hhs-huatu)\n\n### \u2728 \u6838\u5FC3\u4EAE\u70B9\n\u65B0\u7248\u672C\u6539\u7528\u6570\u636E\u5E93\u3002\u4F60\u7684\u4F1A\u5458\u6570\u636E\u5728 \u4F60\u7684 Koishi \u6839\u76EE\u5F55/data/hhs-huatu-user-data.json\u3002\u628A\u5B83\u653E\u5728hhs-huatu-import\u6587\u4EF6\u5939\u4E0B\u9762\uFF0C\u7136\u540E\u53D1\u201C\u4F1A\u5458\u8C03\u8BD5--import\u201D\u6307\u4EE4\uFF0C\u5C31\u53EF\u4EE5\u628A\u6570\u636E\u5BFC\u5165\u6570\u636E\u5E93\u3002\n\n\u672C\u63D2\u4EF6\u9488\u5BF9 **NovelAI V4 / V4.5 / V5** \u6A21\u578B\u8FDB\u884C\u4E86\u6DF1\u5EA6\u9002\u914D\uFF0C\u9884\u8BBE\u5FEB\u6377\u6307\u4EE4\uFF0C\u52A9\u4F60\u5FEB\u901F\u5207\u6362\u6A21\u578B\uFF1A\n\n| \u6307\u4EE4 | \u5BF9\u5E94\u6A21\u578B (Model) | \u8BF4\u660E |\n| :--- | :--- | :--- |\n| `nai4` | `nai-diffusion-4-full` | V4 \u5168\u91CF\u6A21\u578B |\n| `nai4c` | `nai-diffusion-4-curated` | V4 \u7CBE\u9009\u6A21\u578B |\n| `nai4-5` | `nai-diffusion-4-5-full` | V4.5 \u5168\u91CF\u6A21\u578B |\n| `nai4-5c` | `nai-diffusion-4-5-curated` | V4.5 \u7CBE\u9009\u6A21\u578B |\n| `nai5` | `nai-diffusion-5-full` | V5 \u5168\u91CF\u6A21\u578B |\n| `nai5c` | `nai-diffusion-5-curated` | V5 \u7CBE\u9009\u6A21\u578B |\n\n### \uD83D\uDEE0\uFE0F \u529F\u80FD\u5217\u8868\n\n**\uD83C\uDFA8 \u7ED8\u56FE\u6838\u5FC3**\n* **\u5168\u9762\u652F\u6301**\uFF1A\u6587\u751F\u56FE\u3001\u56FE\u751F\u56FE\u57FA\u7840\u529F\u80FD\u5B8C\u6574\u3002\n* **\u5C40\u90E8\u91CD\u7ED8 (Inpaint)**\uFF1A\u652F\u6301\u5BF9\u56FE\u7247\u7279\u5B9A\u533A\u57DF\u8FDB\u884C\u91CD\u7ED8\u3002\n* **\u5BFC\u6F14\u5DE5\u5177**\uFF1Anovelai\u5B98\u7F51\u7684\u5BFC\u6F14\u5DE5\u5177\u529F\u80FD\u3002\n* **V4 \u89D2\u8272\u63D0\u793A\u8BCD**\uFF1Anovelai\u5B98\u7F51\u7684\u591A\u89D2\u8272\u63D0\u793A\u8BCD\u7CFB\u7EDF\u3002\n\n**\u2699\uFE0F \u7CFB\u7EDF\u4E0E\u7BA1\u7406**\n* **\u9AD8\u5E76\u53D1\u4F18\u5316**\uFF1A\u5185\u7F6E\u961F\u5217\u7CFB\u7EDF\u4E0E Token \u6C60\u8F6E\u8BE2\uFF0C\u591A\u8D26\u53F7\u8D1F\u8F7D\u5747\u8861\u3002\n* **\u4F1A\u5458\u7BA1\u7406**\uFF1A\u652F\u6301\u7528\u6237\u5206\u7EA7\u7BA1\u7406\uFF1B\u53EF\u914D\u7F6E\u4EC5\u4F1A\u5458\u4F7F\u7528\u4F1A\u6D88\u8017\u914D\u989D\u7684 nai5 / nai5c\u3002\n* **\u667A\u80FD\u5BA1\u6838**\uFF1A\u96C6\u6210\u817E\u8BAF AI \u6216 API4AI\uFF0C\u81EA\u52A8\u8FC7\u6EE4\u8FDD\u89C4\u5185\u5BB9\u3002\n\n### \uD83D\uDDD3\uFE0F \u5F00\u53D1\u8BA1\u5212\n- [\u221A] **\u70B9\u6570\u63A7\u5236\u7CFB\u7EDF**\uFF1A\u6309 NovelAI \u5B98\u7F51\u524D\u7AEF\u516C\u5F0F\u9884\u6263\u672C\u5730\u70B9\u6570\uFF08V5 \u4E3A V4.5 \u7684 1.5 \u500D\uFF09\u3002\u5B9E\u9645\u6263\u8D39\u4EE5 NovelAI \u8D26\u6237\u4E3A\u51C6\u3002\n- [x] ~~**\u6C1B\u56F4\u4F20\u8F93\u529F\u80FD**\uFF1A\u5B9E\u73B0novelai\u5B98\u7F51\u7684\u6C1B\u56F4\u4F20\u8F93\u529F\u80FD\u3002v4\u7248\u672C\u7684\u98CE\u5473\u4F20\u8F93api\u4E0D\u5141\u8BB8\u76F4\u63A5\u53D1\u56FE\uFF0C\u9700\u8981\u5148\u53BB\u5B98\u7F51\u751F\u6210\u5BF9\u5E94\u6587\u4EF6\uFF0C\u6211\u505A\u4E0D\u5230\u3002~~\n- [\u221A] **\u89D2\u8272\u53C2\u8003\u529F\u80FD**\uFF1A\u5B9E\u73B0novelai\u5B98\u7F51\u7684\u89D2\u8272\u53C2\u8003\u529F\u80FD\u3002\uFF08\u5DF2\u88AB\u7CBE\u786E\u53C2\u8003\u66FF\u4EE3\uFF09\n---\n\n### \uD83D\uDCD6 \u66F4\u591A\u8D44\u6E90\n* \u8BE6\u7EC6\u6559\u7A0B\u8BF7\u79FB\u6B65 [GitHub \u4ED3\u5E93](https://github.com/hhs2275/koishi-plugin-hhs-huatu)\n* \u9047\u5230\u95EE\u9898\uFF1F\u6B22\u8FCE\u52A0\u5165\u4EA4\u6D41\u7FA4\uFF1A[112879548](https://qm.qq.com/q/4nKKvckKbu) (\u8054\u7CFB\u7FA4\u4E3B\u53CD\u9988\u95EE\u9898/\u63D0\u4EA4\u5EFA\u8BAE/\u6109\u5FEB\u73A9\u800D)\n";
|
|
4
4
|
export * from './config';
|
|
5
5
|
export declare const reactive = true;
|
|
6
6
|
export declare const name = "hhs-huatu";
|
package/lib/index.js
CHANGED
|
@@ -1083,9 +1083,18 @@ var Config = import_koishi2.Schema.intersect([
|
|
|
1083
1083
|
import_koishi2.Schema.object({
|
|
1084
1084
|
type: import_koishi2.Schema.const("token"),
|
|
1085
1085
|
token: import_koishi2.Schema.union([
|
|
1086
|
-
import_koishi2.Schema.string().description("
|
|
1087
|
-
import_koishi2.Schema.array(
|
|
1088
|
-
|
|
1086
|
+
import_koishi2.Schema.string().role("secret").description("单个授权令牌。"),
|
|
1087
|
+
import_koishi2.Schema.array(
|
|
1088
|
+
import_koishi2.Schema.string().role("secret").default("")
|
|
1089
|
+
).role("table").default([""]).description("多个授权令牌。每行填写一个令牌,可直接新增空白行,不需要手写 JSON 引号。")
|
|
1090
|
+
]).required(),
|
|
1091
|
+
tokenStrategy: import_koishi2.Schema.union([
|
|
1092
|
+
import_koishi2.Schema.const("round-robin").description("轮询(推荐:按顺序轮流使用;目标令牌忙时跳过,下一次从后续位置继续)"),
|
|
1093
|
+
import_koishi2.Schema.const("first-available").description("首个可用(总是优先前面的空闲令牌,可能让第一个令牌消耗更多)"),
|
|
1094
|
+
import_koishi2.Schema.const("random").description("随机(从空闲令牌中随机选择,长期大致均衡但不保证严格轮流)"),
|
|
1095
|
+
import_koishi2.Schema.const("fallback").description("回退(兼容旧配置:按首个可用分配;请求失败不会自动换令牌重试)"),
|
|
1096
|
+
import_koishi2.Schema.const("parallel").description("并行(兼容旧配置:按空闲令牌并发处理,并发上限受令牌数量限制)")
|
|
1097
|
+
]).description("多个授权令牌的使用策略(token 为数组时生效;单个令牌时无影响)").default("round-robin")
|
|
1089
1098
|
}),
|
|
1090
1099
|
import_koishi2.Schema.object({
|
|
1091
1100
|
type: import_koishi2.Schema.const("login"),
|
|
@@ -1512,12 +1521,15 @@ var QueueSystem = class {
|
|
|
1512
1521
|
this.generateImageFn = generateImageFn;
|
|
1513
1522
|
this.membershipSystem = membershipSystem || null;
|
|
1514
1523
|
if (Array.isArray(config.token) && config.token.length > 0) {
|
|
1515
|
-
this.
|
|
1524
|
+
this.tokenIndices = config.token.map((token, index) => typeof token === "string" && token.trim() ? index : -1).filter((index) => index >= 0);
|
|
1525
|
+
this.tokenPool = new Array(this.tokenIndices.length).fill(false);
|
|
1516
1526
|
this.maxConcurrentTasks = this.tokenPool.length;
|
|
1517
1527
|
} else if (typeof config.token === "string" && config.token) {
|
|
1528
|
+
this.tokenIndices = [0];
|
|
1518
1529
|
this.tokenPool = [false];
|
|
1519
1530
|
this.maxConcurrentTasks = 1;
|
|
1520
1531
|
} else {
|
|
1532
|
+
this.tokenIndices = [0];
|
|
1521
1533
|
this.tokenPool = [false];
|
|
1522
1534
|
this.maxConcurrentTasks = 1;
|
|
1523
1535
|
}
|
|
@@ -1537,7 +1549,6 @@ var QueueSystem = class {
|
|
|
1537
1549
|
userLastTask = /* @__PURE__ */ Object.create(null);
|
|
1538
1550
|
// 重画相关
|
|
1539
1551
|
lastRedrawTime = 0;
|
|
1540
|
-
usedTokenIndices = /* @__PURE__ */ new Set();
|
|
1541
1552
|
redrawLock = false;
|
|
1542
1553
|
redrawWaitQueue = [];
|
|
1543
1554
|
// 最大并发任务数
|
|
@@ -1546,6 +1557,11 @@ var QueueSystem = class {
|
|
|
1546
1557
|
membershipSystem = null;
|
|
1547
1558
|
// Token管理相关
|
|
1548
1559
|
tokenPool = [];
|
|
1560
|
+
// tokenPool 使用紧凑槽位,tokenIndices 保留配置数组中的真实下标,
|
|
1561
|
+
// 这样可以忽略配置中为了方便填写而保留的空白行。
|
|
1562
|
+
tokenIndices = [];
|
|
1563
|
+
// 轮询分配的起始索引。每次成功分配后指向下一个 token。
|
|
1564
|
+
nextTokenIndex = 0;
|
|
1549
1565
|
// 获取锁
|
|
1550
1566
|
async acquireRedrawLock() {
|
|
1551
1567
|
if (!this.redrawLock) {
|
|
@@ -1567,29 +1583,34 @@ var QueueSystem = class {
|
|
|
1567
1583
|
this.redrawLock = false;
|
|
1568
1584
|
}
|
|
1569
1585
|
}
|
|
1570
|
-
//
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
if (this.usedTokenIndices.size >= tokenCount) {
|
|
1574
|
-
this.usedTokenIndices.clear();
|
|
1575
|
-
}
|
|
1576
|
-
let newIndex = currentIndex;
|
|
1577
|
-
let attempts = 0;
|
|
1578
|
-
const maxAttempts = tokenCount * 2;
|
|
1579
|
-
while (this.usedTokenIndices.has(newIndex) && attempts < maxAttempts) {
|
|
1580
|
-
newIndex = (newIndex + 1) % tokenCount;
|
|
1581
|
-
attempts++;
|
|
1582
|
-
}
|
|
1583
|
-
this.usedTokenIndices.add(newIndex);
|
|
1584
|
-
return newIndex;
|
|
1585
|
-
}
|
|
1586
|
-
// 从 Token 池中获取一个空闲的 token 索引(并标记为占用)
|
|
1586
|
+
// 从 Token 池中获取一个空闲的 token 索引(并标记为占用)。
|
|
1587
|
+
// 轮询时从 nextTokenIndex 开始查找,因此即使任务是串行执行,
|
|
1588
|
+
// 也会在每次任务之间切换到下一个 token,而不是始终命中 token[0]。
|
|
1587
1589
|
acquireTokenIndex() {
|
|
1588
1590
|
if (!this.tokenPool.length) return null;
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1591
|
+
const strategy = this.config.tokenStrategy || "round-robin";
|
|
1592
|
+
if (strategy === "random") {
|
|
1593
|
+
const available = this.tokenPool.map((busy, slot2) => busy ? -1 : slot2).filter((slot2) => slot2 >= 0);
|
|
1594
|
+
if (!available.length) return null;
|
|
1595
|
+
const slot = available[Math.floor(Math.random() * available.length)];
|
|
1596
|
+
this.tokenPool[slot] = true;
|
|
1597
|
+
return this.tokenIndices[slot];
|
|
1598
|
+
}
|
|
1599
|
+
if (strategy === "round-robin") {
|
|
1600
|
+
for (let offset = 0; offset < this.tokenPool.length; offset++) {
|
|
1601
|
+
const slot = (this.nextTokenIndex + offset) % this.tokenPool.length;
|
|
1602
|
+
if (!this.tokenPool[slot]) {
|
|
1603
|
+
this.tokenPool[slot] = true;
|
|
1604
|
+
this.nextTokenIndex = (slot + 1) % this.tokenPool.length;
|
|
1605
|
+
return this.tokenIndices[slot];
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
return null;
|
|
1609
|
+
}
|
|
1610
|
+
for (let slot = 0; slot < this.tokenPool.length; slot++) {
|
|
1611
|
+
if (!this.tokenPool[slot]) {
|
|
1612
|
+
this.tokenPool[slot] = true;
|
|
1613
|
+
return this.tokenIndices[slot];
|
|
1593
1614
|
}
|
|
1594
1615
|
}
|
|
1595
1616
|
return null;
|
|
@@ -1598,8 +1619,9 @@ var QueueSystem = class {
|
|
|
1598
1619
|
releaseTokenIndex(index) {
|
|
1599
1620
|
if (index == null) return;
|
|
1600
1621
|
if (!this.tokenPool.length) return;
|
|
1601
|
-
|
|
1602
|
-
|
|
1622
|
+
const slot = this.tokenIndices.indexOf(index);
|
|
1623
|
+
if (slot >= 0) {
|
|
1624
|
+
this.tokenPool[slot] = false;
|
|
1603
1625
|
}
|
|
1604
1626
|
}
|
|
1605
1627
|
// 提供给外部任务借用/归还 token 索引
|
|
@@ -1938,8 +1960,14 @@ var MembershipSystem = class {
|
|
|
1938
1960
|
}
|
|
1939
1961
|
// ========== 点数计算相关 ==========
|
|
1940
1962
|
/**
|
|
1941
|
-
*
|
|
1942
|
-
*
|
|
1963
|
+
* 按 NovelAI 官网前端公式计算 Anlas。
|
|
1964
|
+
* 来源:novelai.net/_next/static/chunks/1052-*.js
|
|
1965
|
+
* SDXL / V4 / V4.5 / V5:
|
|
1966
|
+
* M = ceil(2.951823174884865e-6 * pixels + 5.753298233447344e-7 * pixels * steps)
|
|
1967
|
+
* M *= sm_dyn ? 1.4 : smea ? 1.2 : 1
|
|
1968
|
+
* V5 再乘 1.5
|
|
1969
|
+
* cost = max(ceil(M * strength), 2)
|
|
1970
|
+
* Opus 免费:无角色参考、像素 ≤ 1048576、步数 ≤ 28 时第一张为 0。
|
|
1943
1971
|
*/
|
|
1944
1972
|
calculatePointsCost(params) {
|
|
1945
1973
|
if (!this.config.pointsEnabled) return 0;
|
|
@@ -1952,18 +1980,19 @@ var MembershipSystem = class {
|
|
|
1952
1980
|
strength = 1,
|
|
1953
1981
|
isImg2Img = false,
|
|
1954
1982
|
preciseRefCount = 0,
|
|
1955
|
-
chargeOpusFreeRange = false
|
|
1983
|
+
chargeOpusFreeRange = false,
|
|
1984
|
+
model
|
|
1956
1985
|
} = params;
|
|
1957
|
-
|
|
1958
|
-
if (
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
let L = Math.ceil(
|
|
1962
|
-
if (smeaDyn) L
|
|
1963
|
-
else if (smea) L
|
|
1964
|
-
if (
|
|
1965
|
-
|
|
1966
|
-
|
|
1986
|
+
let pixels = width * height;
|
|
1987
|
+
if (pixels < 65536) pixels = 65536;
|
|
1988
|
+
const opusFree = !chargeOpusFreeRange && preciseRefCount <= 0 && pixels <= 1048576 && steps <= 28;
|
|
1989
|
+
if (opusFree) return 0;
|
|
1990
|
+
let L = Math.ceil(2951823174884865e-21 * pixels + 5753298233447344e-22 * pixels * steps);
|
|
1991
|
+
if (smeaDyn) L *= 1.4;
|
|
1992
|
+
else if (smea) L *= 1.2;
|
|
1993
|
+
if (this.isNai5Model(model)) L *= 1.5;
|
|
1994
|
+
const resolvedStrength = isImg2Img ? strength : 1;
|
|
1995
|
+
L = Math.max(Math.ceil(L * resolvedStrength), 2);
|
|
1967
1996
|
L += preciseRefCount * 5;
|
|
1968
1997
|
return L;
|
|
1969
1998
|
}
|
|
@@ -3622,7 +3651,7 @@ var import_koishi9 = require("koishi");
|
|
|
3622
3651
|
var import_koishi7 = require("koishi");
|
|
3623
3652
|
|
|
3624
3653
|
// src/services/points.ts
|
|
3625
|
-
function getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount, chargeOpusFreeRange = false) {
|
|
3654
|
+
function getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount, chargeOpusFreeRange = false, model) {
|
|
3626
3655
|
const steps = options2.steps ?? session.resolve(isImg2Img ? runtime.config.imageSteps : runtime.config.textSteps) ?? 23;
|
|
3627
3656
|
const resolvedSmeaDyn = options2.smeaDyn ?? runtime.config.smeaDyn ?? false;
|
|
3628
3657
|
const resolvedSmea = (options2.smea ?? runtime.config.smea) || resolvedSmeaDyn;
|
|
@@ -3635,7 +3664,8 @@ function getUnitPointsCost(runtime, session, options2, width, height, isImg2Img,
|
|
|
3635
3664
|
strength: options2.strength,
|
|
3636
3665
|
isImg2Img,
|
|
3637
3666
|
preciseRefCount,
|
|
3638
|
-
chargeOpusFreeRange
|
|
3667
|
+
chargeOpusFreeRange,
|
|
3668
|
+
model: model ?? options2.model
|
|
3639
3669
|
});
|
|
3640
3670
|
}
|
|
3641
3671
|
__name(getUnitPointsCost, "getUnitPointsCost");
|
|
@@ -3643,7 +3673,7 @@ function calculateTaskPointsCost(runtime, session, options2, width, height, isIm
|
|
|
3643
3673
|
const uid = userId || session.userId;
|
|
3644
3674
|
const resolvedModel = model ?? options2.model;
|
|
3645
3675
|
const count = Math.max(1, drawCount);
|
|
3646
|
-
const unitFree = getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount);
|
|
3676
|
+
const unitFree = getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount, false, resolvedModel);
|
|
3647
3677
|
const perImage = [];
|
|
3648
3678
|
if (!runtime.membershipSystem.isNai5Model(resolvedModel)) {
|
|
3649
3679
|
for (let i = 0; i < count; i++) perImage.push(unitFree);
|
|
@@ -3651,7 +3681,7 @@ function calculateTaskPointsCost(runtime, session, options2, width, height, isIm
|
|
|
3651
3681
|
}
|
|
3652
3682
|
const overageCount = runtime.membershipSystem.getNai5OverageCount(uid, count);
|
|
3653
3683
|
const freeCount = count - overageCount;
|
|
3654
|
-
const unitOverage = overageCount > 0 ? getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount, true) : unitFree;
|
|
3684
|
+
const unitOverage = overageCount > 0 ? getUnitPointsCost(runtime, session, options2, width, height, isImg2Img, preciseRefCount, true, resolvedModel) : unitFree;
|
|
3655
3685
|
for (let i = 0; i < freeCount; i++) perImage.push(unitFree);
|
|
3656
3686
|
for (let i = 0; i < overageCount; i++) perImage.push(unitOverage);
|
|
3657
3687
|
return {
|
|
@@ -3929,6 +3959,23 @@ function extractOptionsFromUndesired(undesired) {
|
|
|
3929
3959
|
__name(extractOptionsFromUndesired, "extractOptionsFromUndesired");
|
|
3930
3960
|
function registerNovelai(ctx, config, runtime) {
|
|
3931
3961
|
const { membershipSystem, queueSystem, useFilter, useBackend, thirdParty, noImage, some, step, resolution: resolution2 } = runtime;
|
|
3962
|
+
const recentCommandMessages = /* @__PURE__ */ new Map();
|
|
3963
|
+
const commandDedupWindow = 3e4;
|
|
3964
|
+
const isDuplicateCommand = /* @__PURE__ */ __name((session) => {
|
|
3965
|
+
const messageId = session.messageId;
|
|
3966
|
+
if (!messageId) return false;
|
|
3967
|
+
const now = Date.now();
|
|
3968
|
+
for (const [key2, timestamp] of recentCommandMessages) {
|
|
3969
|
+
if (now - timestamp > commandDedupWindow) recentCommandMessages.delete(key2);
|
|
3970
|
+
}
|
|
3971
|
+
const key = [session.platform, session.selfId, session.channelId, session.userId, messageId].join(":");
|
|
3972
|
+
if (recentCommandMessages.has(key)) {
|
|
3973
|
+
ctx.logger.warn(`检测到重复绘图消息,已忽略:messageId=${messageId}`);
|
|
3974
|
+
return true;
|
|
3975
|
+
}
|
|
3976
|
+
recentCommandMessages.set(key, now);
|
|
3977
|
+
return false;
|
|
3978
|
+
}, "isDuplicateCommand");
|
|
3932
3979
|
ctx.before("attach", (session) => {
|
|
3933
3980
|
session.stripped.content = session.stripped.content.replace(
|
|
3934
3981
|
/^(\S*?nai4)[\u2010-\u2015\u2212\uFE63\uFF0D](5c?)(?=\s|$)/i,
|
|
@@ -3939,6 +3986,7 @@ function registerNovelai(ctx, config, runtime) {
|
|
|
3939
3986
|
hidden: /* @__PURE__ */ __name(() => config.type === "naifu", "hidden"),
|
|
3940
3987
|
type: ["token", "login"].includes(config.type) ? scheduler.nai : config.type === "sd-webui" ? scheduler.sd : config.type === "stable-horde" ? scheduler.horde : []
|
|
3941
3988
|
}).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("inpaint", "-M", { hidden: thirdParty }).option("preciseRef", "-P", { hidden: thirdParty }).option("preciseRefParams", "-p <params:string>").action(async ({ session, options: options2, name: name2 }, ...prompts) => {
|
|
3989
|
+
if (isDuplicateCommand(session)) return;
|
|
3942
3990
|
let input = prompts.join(" ");
|
|
3943
3991
|
const imageUrls = import_koishi9.h.select(session.elements ?? [], "img").map((el) => el.attrs.src);
|
|
3944
3992
|
if (imageUrls.length) {
|
|
@@ -4289,26 +4337,9 @@ function registerRedraw(ctx, config, runtime) {
|
|
|
4289
4337
|
try {
|
|
4290
4338
|
const currentChannelId = session.channelId;
|
|
4291
4339
|
const commandId = Date.now() % 1e4;
|
|
4292
|
-
if (
|
|
4293
|
-
if (!ctx.runtime) {
|
|
4294
|
-
ctx.runtime = { currentTokenIndex: 0 };
|
|
4295
|
-
}
|
|
4296
|
-
const oldTokenIndex = ctx.runtime.currentTokenIndex;
|
|
4297
|
-
const lastRedrawTime = queueSystem.getLastRedrawTime();
|
|
4298
|
-
const timeSinceLastRedraw = now - lastRedrawTime;
|
|
4299
|
-
let newTokenIndex;
|
|
4300
|
-
if (timeSinceLastRedraw < 5e3) {
|
|
4301
|
-
const timeOffset = timeSinceLastRedraw % ctx.config.token.length;
|
|
4302
|
-
const randomOffset = Math.floor(Math.random() * ctx.config.token.length);
|
|
4303
|
-
const baseIndex = (oldTokenIndex + timeOffset + randomOffset + commandId) % ctx.config.token.length;
|
|
4304
|
-
newTokenIndex = queueSystem.getUniqueTokenIndex(baseIndex, ctx.config.token.length);
|
|
4305
|
-
} else {
|
|
4306
|
-
const randomOffset = Math.floor(Math.random() * ctx.config.token.length);
|
|
4307
|
-
newTokenIndex = (oldTokenIndex + randomOffset + 1) % ctx.config.token.length;
|
|
4308
|
-
}
|
|
4309
|
-
ctx.runtime.currentTokenIndex = newTokenIndex;
|
|
4340
|
+
if (Array.isArray(ctx.config.token)) {
|
|
4310
4341
|
queueSystem.setLastRedrawTime(now);
|
|
4311
|
-
ctx.logger.debug(`重画命令(${commandId})开始,token
|
|
4342
|
+
ctx.logger.debug(`重画命令(${commandId})开始,token 使用策略: ${config.tokenStrategy || "round-robin"}`);
|
|
4312
4343
|
}
|
|
4313
4344
|
const addRedrawTask = /* @__PURE__ */ __name(async (index, delay = 0) => {
|
|
4314
4345
|
if (delay > 0) {
|
|
@@ -4324,61 +4355,12 @@ function registerRedraw(ctx, config, runtime) {
|
|
|
4324
4355
|
taskSession.isRedraw = true;
|
|
4325
4356
|
const taskUniqueId = commandId * 100 + index;
|
|
4326
4357
|
if (Array.isArray(ctx.config.token)) {
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
_taskId: taskUniqueId
|
|
4334
|
-
};
|
|
4335
|
-
ctx.logger.debug(`为重画任务 ${taskUniqueId} 创建独立的session对象,确保能够独立获取token`);
|
|
4336
|
-
} else if (config.tokenStrategy === "round-robin") {
|
|
4337
|
-
if (ctx.runtime) {
|
|
4338
|
-
let forcedTokenIndex;
|
|
4339
|
-
if (ctx.config.token.length <= 1) {
|
|
4340
|
-
forcedTokenIndex = 0;
|
|
4341
|
-
} else {
|
|
4342
|
-
const baseIndex = ctx.runtime.currentTokenIndex;
|
|
4343
|
-
const taskOffset = (index * 3 + taskUniqueId) % ctx.config.token.length;
|
|
4344
|
-
const candidateIndex = (baseIndex + taskOffset) % ctx.config.token.length;
|
|
4345
|
-
forcedTokenIndex = queueSystem.getUniqueTokenIndex(candidateIndex, ctx.config.token.length);
|
|
4346
|
-
ctx.logger.debug(`重画任务 ${taskUniqueId} token索引计算: 基础=${baseIndex}, 任务偏移=${taskOffset}, 最终=${forcedTokenIndex}`);
|
|
4347
|
-
}
|
|
4348
|
-
taskSession.runtime = {
|
|
4349
|
-
_timeStamp: Date.now() + index,
|
|
4350
|
-
_redraw: true,
|
|
4351
|
-
_forcedTokenIndex: forcedTokenIndex,
|
|
4352
|
-
// 强制指定 token 索引
|
|
4353
|
-
_taskIndex: index,
|
|
4354
|
-
// 任务索引
|
|
4355
|
-
_taskId: taskUniqueId
|
|
4356
|
-
// 任务唯一ID
|
|
4357
|
-
};
|
|
4358
|
-
ctx.logger.debug(`为重画任务 ${taskUniqueId} 强制指定 token 索引: ${forcedTokenIndex}`);
|
|
4359
|
-
} else {
|
|
4360
|
-
taskSession.runtime = {
|
|
4361
|
-
_timeStamp: Date.now() + index,
|
|
4362
|
-
_redraw: true,
|
|
4363
|
-
_taskId: taskUniqueId
|
|
4364
|
-
};
|
|
4365
|
-
ctx.logger.debug(`为重画任务 ${taskUniqueId} 创建轮询session对象,将使用下一个可用token`);
|
|
4366
|
-
}
|
|
4367
|
-
} else if (config.tokenStrategy === "random") {
|
|
4368
|
-
taskSession.runtime = {
|
|
4369
|
-
_timeStamp: Date.now() + index,
|
|
4370
|
-
_redraw: true,
|
|
4371
|
-
_taskId: taskUniqueId
|
|
4372
|
-
};
|
|
4373
|
-
ctx.logger.debug(`为重画任务 ${taskUniqueId} 创建随机策略session对象,将随机选择token`);
|
|
4374
|
-
} else if (config.tokenStrategy === "fallback") {
|
|
4375
|
-
taskSession.runtime = {
|
|
4376
|
-
_timeStamp: Date.now() + index,
|
|
4377
|
-
_redraw: true,
|
|
4378
|
-
_taskId: taskUniqueId
|
|
4379
|
-
};
|
|
4380
|
-
ctx.logger.debug(`为重画任务 ${taskUniqueId} 创建备用策略session对象,将从第一个token开始尝试`);
|
|
4381
|
-
}
|
|
4358
|
+
taskSession.runtime = {
|
|
4359
|
+
_timeStamp: Date.now() + index,
|
|
4360
|
+
_redraw: true,
|
|
4361
|
+
_taskIndex: index,
|
|
4362
|
+
_taskId: taskUniqueId
|
|
4363
|
+
};
|
|
4382
4364
|
}
|
|
4383
4365
|
const taskOptions = { ...lastTask.options };
|
|
4384
4366
|
if (config.membershipEnabled && config.pointsEnabled) {
|
|
@@ -5709,7 +5691,7 @@ var usage = `
|
|
|
5709
5691
|
* **智能审核**:集成腾讯 AI 或 API4AI,自动过滤违规内容。
|
|
5710
5692
|
|
|
5711
5693
|
### 🗓️ 开发计划
|
|
5712
|
-
- [√]
|
|
5694
|
+
- [√] **点数控制系统**:按 NovelAI 官网前端公式预扣本地点数(V5 为 V4.5 的 1.5 倍)。实际扣费以 NovelAI 账户为准。
|
|
5713
5695
|
- [x] ~~**氛围传输功能**:实现novelai官网的氛围传输功能。v4版本的风味传输api不允许直接发图,需要先去官网生成对应文件,我做不到。~~
|
|
5714
5696
|
- [√] **角色参考功能**:实现novelai官网的角色参考功能。(已被精确参考替代)
|
|
5715
5697
|
---
|
|
File without changes
|
|
File without changes
|
|
@@ -18,8 +18,14 @@ export declare class MembershipSystem {
|
|
|
18
18
|
syncUserToDB(userId: string): Promise<void>;
|
|
19
19
|
saveUserData(): Promise<void>;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* 按 NovelAI 官网前端公式计算 Anlas。
|
|
22
|
+
* 来源:novelai.net/_next/static/chunks/1052-*.js
|
|
23
|
+
* SDXL / V4 / V4.5 / V5:
|
|
24
|
+
* M = ceil(2.951823174884865e-6 * pixels + 5.753298233447344e-7 * pixels * steps)
|
|
25
|
+
* M *= sm_dyn ? 1.4 : smea ? 1.2 : 1
|
|
26
|
+
* V5 再乘 1.5
|
|
27
|
+
* cost = max(ceil(M * strength), 2)
|
|
28
|
+
* Opus 免费:无角色参考、像素 ≤ 1048576、步数 ≤ 28 时第一张为 0。
|
|
23
29
|
*/
|
|
24
30
|
calculatePointsCost(params: {
|
|
25
31
|
width: number;
|
|
@@ -31,6 +37,7 @@ export declare class MembershipSystem {
|
|
|
31
37
|
isImg2Img?: boolean;
|
|
32
38
|
preciseRefCount?: number;
|
|
33
39
|
chargeOpusFreeRange?: boolean;
|
|
40
|
+
model?: string;
|
|
34
41
|
}): number;
|
|
35
42
|
/**
|
|
36
43
|
* 导演工具点数计算
|
package/lib/queueSystem.d.ts
CHANGED
|
@@ -24,16 +24,16 @@ export declare class QueueSystem {
|
|
|
24
24
|
pointsCost?: number;
|
|
25
25
|
}>;
|
|
26
26
|
private lastRedrawTime;
|
|
27
|
-
private usedTokenIndices;
|
|
28
27
|
private redrawLock;
|
|
29
28
|
private redrawWaitQueue;
|
|
30
29
|
maxConcurrentTasks: number;
|
|
31
30
|
private membershipSystem;
|
|
32
31
|
private tokenPool;
|
|
32
|
+
private tokenIndices;
|
|
33
|
+
private nextTokenIndex;
|
|
33
34
|
constructor(ctx: Context, config: Config, generateImageFn: (session: Session<'authority'>, options: any, input: string) => Promise<any>, membershipSystem?: MembershipSystem, initialTokenUsage?: Dict<boolean>);
|
|
34
35
|
acquireRedrawLock(): Promise<void>;
|
|
35
36
|
releaseRedrawLock(): void;
|
|
36
|
-
getUniqueTokenIndex(currentIndex: number, tokenCount: number): number;
|
|
37
37
|
private acquireTokenIndex;
|
|
38
38
|
private releaseTokenIndex;
|
|
39
39
|
borrowTokenIndex(): number | null;
|
package/lib/runtime.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/lib/services/points.d.ts
CHANGED
|
File without changes
|
package/lib/types.d.ts
CHANGED
|
File without changes
|
package/lib/utils.d.ts
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -139,13 +139,14 @@ nai4-5 <提示词> -P -p "cs,1,1;c,0.8,0.9"
|
|
|
139
139
|
|
|
140
140
|
### 💰 点数控制系统
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
按 NovelAI 官网前端公式模拟 Anlas 消耗并预扣费,确保消耗透明且防止恶意刷图。
|
|
143
143
|
|
|
144
144
|
**计费细则:**
|
|
145
|
-
-
|
|
146
|
-
- **
|
|
147
|
-
-
|
|
148
|
-
-
|
|
145
|
+
- **默认消耗**:`ceil(2.951823174884865e-6 × 像素 + 5.753298233447344e-7 × 像素 × 步数)`。
|
|
146
|
+
- **V5(nai5 / nai5c)**:在上述结果上再乘 **1.5**。例如 832×1216、28 步超额时,V4.5 为 20、V5 为 30。
|
|
147
|
+
- **SMEA / DYN 加成**:SMEA 单独开启 **1.2 倍**,同时开启 SMEA 和 DYN **1.4 倍**。
|
|
148
|
+
- **以图生图**:按去噪强度(`strength`)缩放后向上取整,最少 2 点。
|
|
149
|
+
- **精确参考(-P)**:有参考图时取消 Opus 免费档,并每张参考图额外扣除 5 点数。
|
|
149
150
|
- **导演工具(Director Tools)**:
|
|
150
151
|
- 普清免扣:分辨率在 `1024×1024`(或 1,048,576 像素)及以下时,处理**免收基础点数**。
|
|
151
152
|
- 高清计费:超过安全像素后根据公式 `像素数 ÷ 50000 - 1.8` 收取超额积分。
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
interface ProcessedImageResult {
|
|
2
|
-
cleanImage: Buffer;
|
|
3
|
-
dimmedImage: Buffer;
|
|
4
|
-
width: number;
|
|
5
|
-
height: number;
|
|
6
|
-
}
|
|
7
|
-
interface InpaintPayload {
|
|
8
|
-
input?: string;
|
|
9
|
-
model: string;
|
|
10
|
-
action: 'infill';
|
|
11
|
-
parameters: any;
|
|
12
|
-
}
|
|
13
|
-
export declare class NaiInpaintService {
|
|
14
|
-
/**
|
|
15
|
-
* 预处理输入图像
|
|
16
|
-
* 1. 移除 Alpha 通道 (强制 RGB)
|
|
17
|
-
* 2. 调整尺寸为 64 的倍数 (NovelAI 限制)
|
|
18
|
-
* 3. 生成一张变暗的引导图供用户涂抹
|
|
19
|
-
*/
|
|
20
|
-
processInputImage(inputBuffer: Buffer): Promise<ProcessedImageResult>;
|
|
21
|
-
/**
|
|
22
|
-
* 从用户涂抹的图中提取蒙版
|
|
23
|
-
* 用户会在暗图上用白色画笔涂抹
|
|
24
|
-
*/
|
|
25
|
-
extractMaskFromUserDrawing(cleanImageBuffer: Buffer, userDrawnBuffer: Buffer): Promise<Buffer>;
|
|
26
|
-
/**
|
|
27
|
-
* 构造 NovelAI API Payload
|
|
28
|
-
*/
|
|
29
|
-
constructInpaintPayload(model: string, imageBase64: string, maskBase64: string, prompt: string, negativePrompt: string, params: any): InpaintPayload;
|
|
30
|
-
}
|
|
31
|
-
export {};
|
package/lib/imageService.d.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 将数值向下或向上对齐到 64 的倍数
|
|
3
|
-
* @param value 原始数值
|
|
4
|
-
* @param direction 对齐方向:'floor' 向下,'ceil' 向上
|
|
5
|
-
*/
|
|
6
|
-
export declare function alignTo64Grid(value: number, direction: 'floor' | 'ceil'): number;
|
|
7
|
-
/**
|
|
8
|
-
* Buffer 转 Base64 字符串(不含 data URL 前缀)
|
|
9
|
-
*/
|
|
10
|
-
export declare function bufferToBase64(buffer: Buffer): string;
|
|
11
|
-
/**
|
|
12
|
-
* 将图片尺寸调整为 64 的倍数
|
|
13
|
-
* @param buffer 图片 ArrayBuffer
|
|
14
|
-
* @returns 调整后的图片 Buffer 和尺寸
|
|
15
|
-
*/
|
|
16
|
-
export declare function resizeToMultipleOf64(buffer: ArrayBuffer): Promise<{
|
|
17
|
-
buffer: Buffer;
|
|
18
|
-
width: number;
|
|
19
|
-
height: number;
|
|
20
|
-
}>;
|
|
21
|
-
/**
|
|
22
|
-
* 将图片亮度降低(变暗),方便用户在上面涂白
|
|
23
|
-
* @param buffer 原图 ArrayBuffer
|
|
24
|
-
* @param brightness 亮度系数,默认 0.4(即降低到 40%)
|
|
25
|
-
* @returns 变暗后的图片 Buffer
|
|
26
|
-
*/
|
|
27
|
-
export declare function darkenImage(buffer: ArrayBuffer, brightness?: number): Promise<Buffer>;
|
|
28
|
-
/**
|
|
29
|
-
* 从用户涂抹的图片中生成 Inpainting 蒙版
|
|
30
|
-
*
|
|
31
|
-
* 处理流程:
|
|
32
|
-
* 1. 灰度化 -> 阈值提取白色区域 -> 模糊 -> 再次阈值化
|
|
33
|
-
* 这个流程可以平滑蒙版边缘,避免 AI 重绘时产生明显边界
|
|
34
|
-
* 2. 计算白色区域的包围盒
|
|
35
|
-
* 3. 将包围盒扩展至 64 的倍数对齐
|
|
36
|
-
* 4. 生成最终蒙版
|
|
37
|
-
*
|
|
38
|
-
* @param buffer 用户发回的涂抹图片 ArrayBuffer
|
|
39
|
-
* @returns 蒙版 Buffer、宽度、高度
|
|
40
|
-
*/
|
|
41
|
-
export declare function createInpaintMask(buffer: ArrayBuffer): Promise<{
|
|
42
|
-
maskBuffer: Buffer;
|
|
43
|
-
width: number;
|
|
44
|
-
height: number;
|
|
45
|
-
}>;
|
|
46
|
-
/**
|
|
47
|
-
* 创建精确的 Inpainting 蒙版(保留原始涂抹形状)
|
|
48
|
-
*
|
|
49
|
-
* 与 createInpaintMask 不同,此函数保留用户涂抹的精确形状,
|
|
50
|
-
* 而不是扩展为矩形。适用于需要精确控制重绘区域的场景。
|
|
51
|
-
*
|
|
52
|
-
* @param buffer 用户发回的涂抹图片 ArrayBuffer
|
|
53
|
-
* @returns 蒙版 Buffer、宽度、高度
|
|
54
|
-
*/
|
|
55
|
-
export declare function createPreciseInpaintMask(buffer: ArrayBuffer): Promise<{
|
|
56
|
-
maskBuffer: Buffer;
|
|
57
|
-
width: number;
|
|
58
|
-
height: number;
|
|
59
|
-
}>;
|
package/lib/infillPayload.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* NovelAI Inpainting API Payload 构造器
|
|
3
|
-
* 用于构建 infill 请求的参数
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* Infill Payload 选项
|
|
7
|
-
*/
|
|
8
|
-
export interface InfillOptions {
|
|
9
|
-
/** 基础模型名称(如 'nai-diffusion-3',将自动添加 -inpainting 后缀) */
|
|
10
|
-
baseModel: string;
|
|
11
|
-
/** 正向提示词 */
|
|
12
|
-
prompt: string;
|
|
13
|
-
/** 负向提示词 */
|
|
14
|
-
negativePrompt?: string;
|
|
15
|
-
/** 原图 Base64(不含前缀) */
|
|
16
|
-
imageBase64: string;
|
|
17
|
-
/** 蒙版 Base64(不含前缀) */
|
|
18
|
-
maskBase64: string;
|
|
19
|
-
/** 重绘强度,默认 1.0 */
|
|
20
|
-
strength?: number;
|
|
21
|
-
/** 噪声强度,默认 0 */
|
|
22
|
-
noise?: number;
|
|
23
|
-
/** 生成种子 */
|
|
24
|
-
seed?: number;
|
|
25
|
-
/** CFG Scale */
|
|
26
|
-
scale?: number;
|
|
27
|
-
/** 采样器 */
|
|
28
|
-
sampler?: string;
|
|
29
|
-
/** 调度器 */
|
|
30
|
-
scheduler?: string;
|
|
31
|
-
/** 步数 */
|
|
32
|
-
steps?: number;
|
|
33
|
-
/** 图片宽度 */
|
|
34
|
-
width?: number;
|
|
35
|
-
/** 图片高度 */
|
|
36
|
-
height?: number;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Infill API 请求 Payload
|
|
40
|
-
*/
|
|
41
|
-
export interface InfillPayload {
|
|
42
|
-
model: string;
|
|
43
|
-
input: string;
|
|
44
|
-
action: 'infill';
|
|
45
|
-
parameters: {
|
|
46
|
-
image: string;
|
|
47
|
-
mask: string;
|
|
48
|
-
add_original_image: boolean;
|
|
49
|
-
strength: number;
|
|
50
|
-
noise: number;
|
|
51
|
-
seed: number;
|
|
52
|
-
prompt: string;
|
|
53
|
-
negative_prompt: string;
|
|
54
|
-
scale: number;
|
|
55
|
-
sampler: string;
|
|
56
|
-
noise_schedule?: string;
|
|
57
|
-
steps: number;
|
|
58
|
-
width: number;
|
|
59
|
-
height: number;
|
|
60
|
-
n_samples: number;
|
|
61
|
-
ucPreset: number;
|
|
62
|
-
qualityToggle: boolean;
|
|
63
|
-
params_version: number;
|
|
64
|
-
legacy: boolean;
|
|
65
|
-
[key: string]: any;
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* 获取 Inpainting 模型名称
|
|
70
|
-
* @param baseModel 基础模型名称
|
|
71
|
-
* @returns 带 -inpainting 后缀的模型名称
|
|
72
|
-
*/
|
|
73
|
-
export declare function getInpaintingModel(baseModel: string): string;
|
|
74
|
-
/**
|
|
75
|
-
* 检查模型是否支持 Inpainting
|
|
76
|
-
* @param model 模型名称
|
|
77
|
-
*/
|
|
78
|
-
export declare function supportsInpainting(model: string): boolean;
|
|
79
|
-
/**
|
|
80
|
-
* 构造 Infill 请求 Payload
|
|
81
|
-
*
|
|
82
|
-
* @param options 选项参数
|
|
83
|
-
* @returns 完整的 Infill API 请求 Payload
|
|
84
|
-
*/
|
|
85
|
-
export declare function constructInfillPayload(options: InfillOptions): InfillPayload;
|
|
86
|
-
/**
|
|
87
|
-
* 从 Buffer 创建 Infill Payload(便捷方法)
|
|
88
|
-
*
|
|
89
|
-
* @param options 选项参数(使用 Buffer 而非 Base64)
|
|
90
|
-
*/
|
|
91
|
-
export declare function constructInfillPayloadFromBuffers(options: Omit<InfillOptions, 'imageBase64' | 'maskBase64'> & {
|
|
92
|
-
imageBuffer: Buffer;
|
|
93
|
-
maskBuffer: Buffer;
|
|
94
|
-
}): InfillPayload;
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { Context, Session } from 'koishi';
|
|
2
|
-
import { Config } from '../config';
|
|
3
|
-
/**
|
|
4
|
-
* 组合支付结果。
|
|
5
|
-
* 当点数不足以支付费用时,缺口按兑换比例折算为外部货币补足(组合支付)。
|
|
6
|
-
*/
|
|
7
|
-
export interface PayResult {
|
|
8
|
-
success: boolean;
|
|
9
|
-
/** 实际支付的点数 */
|
|
10
|
-
pointsPaid: number;
|
|
11
|
-
/** 实际支付的外部货币数量 */
|
|
12
|
-
currencyPaid: number;
|
|
13
|
-
/** 失败时(success=false)附带以下提示数据 */
|
|
14
|
-
pointsBalance?: number;
|
|
15
|
-
requiredPoints?: number;
|
|
16
|
-
currencyBalance?: number;
|
|
17
|
-
requiredCurrency?: number;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* 外部货币适配器。
|
|
21
|
-
* 用于「点数不足时使用外部货币兜底支付」:直接读写外部数据库表(默认即
|
|
22
|
-
* xiaohua-monetary 插件的 monetary 表)。外部货币的发放/刷新由对应插件管理,
|
|
23
|
-
* 本插件仅负责消费(查询 / 扣减 / 退还)。
|
|
24
|
-
*/
|
|
25
|
-
export declare class ExternalCurrency {
|
|
26
|
-
private ctx;
|
|
27
|
-
private config;
|
|
28
|
-
constructor(ctx: Context, config: Config);
|
|
29
|
-
private get fc();
|
|
30
|
-
/**
|
|
31
|
-
* 解析用户的 uid(对应 Koishi binding 表的 aid)。
|
|
32
|
-
* 优先使用 session.user.id(Koishi 在挂载用户时总是会加载 id 字段);
|
|
33
|
-
* 无 session 时经 binding 表由 userId(platform:pid)反查。
|
|
34
|
-
*/
|
|
35
|
-
static resolveUid(session?: Session, userId?: string): number | null;
|
|
36
|
-
/** 经 binding 表由 userId(platform:pid)反查 uid,查不到返回 null */
|
|
37
|
-
static resolveUidByUserId(ctx: Context, userId: string): Promise<number | null>;
|
|
38
|
-
/** 构造查询条件:按 uid + (可选的)货币类型 */
|
|
39
|
-
private buildQuery;
|
|
40
|
-
/** 查询用户外部货币余额,无记录视为 0;表/字段配置错误时记日志并按 0 处理(不免费放行) */
|
|
41
|
-
getBalance(uid: number): Promise<number>;
|
|
42
|
-
/** 扣减外部货币(原子 $.sub),余额不足返回 false */
|
|
43
|
-
deduct(uid: number, amount: number): Promise<boolean>;
|
|
44
|
-
/** 退还外部货币(原子 $.add;无记录时创建新记录,不丢钱) */
|
|
45
|
-
refund(uid: number, amount: number): Promise<void>;
|
|
46
|
-
}
|