mioku-plugin-chat 2.9.11 → 2.10.1
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/configs/base.ts +1 -1
- package/core/chat-engine.ts +9 -26
- package/core/chat-turn.ts +1 -1
- package/handlers/idle-debug.ts +1 -1
- package/handlers/message.ts +2 -0
- package/handlers/poke.ts +1 -1
- package/index.ts +1 -1
- package/manage/cooldown.ts +2 -2
- package/manage/idle-check.ts +1 -1
- package/manage/queue-processor.ts +2 -2
- package/manage/rate-limit-guard.ts +47 -31
- package/manage/types.ts +6 -1
- package/package.json +1 -1
package/configs/base.ts
CHANGED
package/core/chat-engine.ts
CHANGED
|
@@ -721,7 +721,6 @@ function cleanMarkers(text: string): string {
|
|
|
721
721
|
let cleaned = stripThinkBlocks(text).trim();
|
|
722
722
|
|
|
723
723
|
cleaned = cleaned
|
|
724
|
-
.replace(/<Ai>\s*<think>[\s\S]*?<\/Ai>/gi, "")
|
|
725
724
|
.replace(/<||DSML||tool_calls>[\s\S]*?<\/||DSML||tool_calls>/gi, "")
|
|
726
725
|
.replace(/<||DSML||invoke[^>]*>[\s\S]*?<\/||DSML||invoke>/gi, "")
|
|
727
726
|
.replace(
|
|
@@ -824,7 +823,7 @@ function stripThinkBlocks(text: string): string {
|
|
|
824
823
|
source = afterOpen.slice(close.end);
|
|
825
824
|
}
|
|
826
825
|
|
|
827
|
-
return output.replace(
|
|
826
|
+
return output.replace(/<\/?(?:think|thinking)\s*>/gi, "");
|
|
828
827
|
}
|
|
829
828
|
|
|
830
829
|
function createThinkTagStreamFilter() {
|
|
@@ -840,7 +839,7 @@ function createThinkTagStreamFilter() {
|
|
|
840
839
|
if (insideThink) {
|
|
841
840
|
const close = findThinkCloseTag(buffer);
|
|
842
841
|
if (!close) {
|
|
843
|
-
buffer = force ? "" : keepTagPrefixSuffix(buffer, "</
|
|
842
|
+
buffer = force ? "" : keepTagPrefixSuffix(buffer, "</thinking>");
|
|
844
843
|
break;
|
|
845
844
|
}
|
|
846
845
|
|
|
@@ -851,7 +850,7 @@ function createThinkTagStreamFilter() {
|
|
|
851
850
|
|
|
852
851
|
const open = findThinkOpenTag(buffer);
|
|
853
852
|
if (!open) {
|
|
854
|
-
const keep = force ? "" : keepTagPrefixSuffix(buffer, "<
|
|
853
|
+
const keep = force ? "" : keepTagPrefixSuffix(buffer, "<thinking>");
|
|
855
854
|
output += buffer.slice(0, buffer.length - keep.length);
|
|
856
855
|
buffer = keep;
|
|
857
856
|
break;
|
|
@@ -862,38 +861,22 @@ function createThinkTagStreamFilter() {
|
|
|
862
861
|
insideThink = true;
|
|
863
862
|
}
|
|
864
863
|
|
|
865
|
-
return output.replace(
|
|
864
|
+
return output.replace(/<\/?(?:think|thinking)\s*>/gi, "");
|
|
866
865
|
},
|
|
867
866
|
};
|
|
868
867
|
}
|
|
869
868
|
|
|
870
869
|
function findThinkOpenTag(text: string): { index: number; end: number } | null {
|
|
871
|
-
const
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
}
|
|
876
|
-
|
|
877
|
-
const afterName = text[index + "<think".length];
|
|
878
|
-
if (afterName && !/[\s>]/.test(afterName)) {
|
|
879
|
-
const next = findThinkOpenTag(text.slice(index + 1));
|
|
880
|
-
return next
|
|
881
|
-
? { index: index + 1 + next.index, end: index + 1 + next.end }
|
|
882
|
-
: null;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
const closeIndex = text.indexOf(">", index + "<think".length);
|
|
886
|
-
if (closeIndex < 0) {
|
|
887
|
-
return { index, end: text.length };
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
return { index, end: closeIndex + 1 };
|
|
870
|
+
const match = /<(think|thinking)\b[^>]*>/i.exec(text);
|
|
871
|
+
return match
|
|
872
|
+
? { index: match.index, end: match.index + match[0].length }
|
|
873
|
+
: null;
|
|
891
874
|
}
|
|
892
875
|
|
|
893
876
|
function findThinkCloseTag(
|
|
894
877
|
text: string,
|
|
895
878
|
): { index: number; end: number } | null {
|
|
896
|
-
const match = /<\/think\s*>/i.exec(text);
|
|
879
|
+
const match = /<\/(think|thinking)\s*>/i.exec(text);
|
|
897
880
|
return match
|
|
898
881
|
? { index: match.index, end: match.index + match[0].length }
|
|
899
882
|
: null;
|
package/core/chat-turn.ts
CHANGED
|
@@ -351,7 +351,7 @@ export async function processChat(
|
|
|
351
351
|
}
|
|
352
352
|
: undefined,
|
|
353
353
|
),
|
|
354
|
-
{ userId, groupId, label: isGroup ? "group-chat" : "private-chat" },
|
|
354
|
+
{ userId, groupId, label: isGroup ? "group-chat" : "private-chat", skipRetryOnRateLimit: true },
|
|
355
355
|
);
|
|
356
356
|
|
|
357
357
|
if (!result) {
|
package/handlers/idle-debug.ts
CHANGED
|
@@ -104,7 +104,7 @@ export async function handleIdleCheckDebug(
|
|
|
104
104
|
pluginCtx.humanize,
|
|
105
105
|
pluginCtx.skillManager,
|
|
106
106
|
),
|
|
107
|
-
{ groupId: targetGroupId, label: "idle-check" },
|
|
107
|
+
{ groupId: targetGroupId, label: "idle-check", skipRetryOnRateLimit: true },
|
|
108
108
|
);
|
|
109
109
|
if (!result) {
|
|
110
110
|
await e.reply(`[空闲检测] 群 ${targetGroupId} 因限流被跳过`);
|
package/handlers/message.ts
CHANGED
|
@@ -98,6 +98,7 @@ export function createMessageHandler(
|
|
|
98
98
|
userId,
|
|
99
99
|
groupId,
|
|
100
100
|
label: "history-media",
|
|
101
|
+
skipRetryOnRateLimit: true,
|
|
101
102
|
}),
|
|
102
103
|
)
|
|
103
104
|
: undefined;
|
|
@@ -114,6 +115,7 @@ export function createMessageHandler(
|
|
|
114
115
|
userId,
|
|
115
116
|
groupId,
|
|
116
117
|
label: "image-analysis",
|
|
118
|
+
skipRetryOnRateLimit: true,
|
|
117
119
|
}),
|
|
118
120
|
}).catch((err) =>
|
|
119
121
|
ctx.logger.error(`[image-analyzer] Failed: ${err}`),
|
package/handlers/poke.ts
CHANGED
package/index.ts
CHANGED
package/manage/cooldown.ts
CHANGED
|
@@ -277,7 +277,7 @@ export class CooldownManager {
|
|
|
277
277
|
),
|
|
278
278
|
},
|
|
279
279
|
),
|
|
280
|
-
{ userId: targetMessage.userId, groupId, label: "cooldown" },
|
|
280
|
+
{ userId: targetMessage.userId, groupId, label: "cooldown", skipRetryOnRateLimit: true },
|
|
281
281
|
);
|
|
282
282
|
if (!result) return;
|
|
283
283
|
|
|
@@ -414,7 +414,7 @@ export class CooldownManager {
|
|
|
414
414
|
),
|
|
415
415
|
},
|
|
416
416
|
),
|
|
417
|
-
{ userId: targetMessage.userId, groupId, label: "cooldown-planner" },
|
|
417
|
+
{ userId: targetMessage.userId, groupId, label: "cooldown-planner", skipRetryOnRateLimit: true },
|
|
418
418
|
);
|
|
419
419
|
if (!result) {
|
|
420
420
|
this.ctx.logger.warn(
|
package/manage/idle-check.ts
CHANGED
|
@@ -277,7 +277,7 @@ export class IdleCheckManager {
|
|
|
277
277
|
this.humanize,
|
|
278
278
|
this.skillManager,
|
|
279
279
|
),
|
|
280
|
-
{ groupId, label: "idle-check" },
|
|
280
|
+
{ groupId, label: "idle-check", skipRetryOnRateLimit: true },
|
|
281
281
|
);
|
|
282
282
|
if (!result) {
|
|
283
283
|
this.groupMessageCount.set(groupSessionId, 0);
|
|
@@ -199,7 +199,7 @@ export class QueueProcessor {
|
|
|
199
199
|
}, this.humanize, this.skillManager, {
|
|
200
200
|
manager: this.groupStructuredHistory, ttlMs: cfg.groupStructuredHistoryTtlMs, currentUserInputs: structuredUserInputs,
|
|
201
201
|
}),
|
|
202
|
-
{ userId: targetMessage.userId, groupId, label: "dynamic-delay" },
|
|
202
|
+
{ userId: targetMessage.userId, groupId, label: "dynamic-delay", skipRetryOnRateLimit: true },
|
|
203
203
|
);
|
|
204
204
|
if (!result) return;
|
|
205
205
|
|
|
@@ -280,7 +280,7 @@ export class QueueProcessor {
|
|
|
280
280
|
}, this.humanize, this.skillManager, {
|
|
281
281
|
manager: this.groupStructuredHistory, ttlMs: cfg.groupStructuredHistoryTtlMs, currentUserInputs: structuredUserInputs,
|
|
282
282
|
}),
|
|
283
|
-
{ userId: targetMessage.userId, groupId, label: "queue" },
|
|
283
|
+
{ userId: targetMessage.userId, groupId, label: "queue", skipRetryOnRateLimit: true },
|
|
284
284
|
);
|
|
285
285
|
if (!result) return;
|
|
286
286
|
|
|
@@ -1,12 +1,38 @@
|
|
|
1
1
|
import type { RateLimiter } from "./rate-limiter";
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const RATE_LIMIT_MAX_RETRIES = 2;
|
|
3
|
+
const RATE_LIMIT_RETRY_DELAYS_MS = [5_000, 10_000, 30_000, 60_000];
|
|
5
4
|
|
|
6
5
|
type Logger = { warn: (...args: unknown[]) => void };
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
function isRateLimitError(err: unknown): boolean {
|
|
8
|
+
if (err === null || err === undefined) return false;
|
|
9
|
+
const s = String(err).toLowerCase();
|
|
10
|
+
return s.includes("429") || s.includes("rate limit") || s.includes("rate_limit");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function retryOn429<T>(
|
|
14
|
+
fn: () => Promise<T>,
|
|
15
|
+
log: Logger,
|
|
16
|
+
label?: string,
|
|
17
|
+
): Promise<T> {
|
|
18
|
+
let attempt = 0;
|
|
19
|
+
while (true) {
|
|
20
|
+
try {
|
|
21
|
+
return await fn();
|
|
22
|
+
} catch (err) {
|
|
23
|
+
if (!isRateLimitError(err)) throw err;
|
|
24
|
+
if (attempt >= RATE_LIMIT_RETRY_DELAYS_MS.length) throw err;
|
|
25
|
+
const delay = RATE_LIMIT_RETRY_DELAYS_MS[attempt];
|
|
26
|
+
const attemptLabel = `${attempt + 1}/${RATE_LIMIT_RETRY_DELAYS_MS.length}`;
|
|
27
|
+
log.warn(
|
|
28
|
+
`[Chat]${label ? ` ${label}` : ""} 命中 429,等待 ${delay / 1000}s 后第 ${attemptLabel} 次重试: ${err}`,
|
|
29
|
+
);
|
|
30
|
+
attempt += 1;
|
|
31
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
10
36
|
export class RateLimitGuard {
|
|
11
37
|
private blockedUntil = 0;
|
|
12
38
|
|
|
@@ -20,17 +46,17 @@ export class RateLimitGuard {
|
|
|
20
46
|
}
|
|
21
47
|
|
|
22
48
|
private markBlocked(): void {
|
|
23
|
-
this.blockedUntil = Date.now() +
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
private isRateLimitError(err: unknown): boolean {
|
|
27
|
-
const s = String(err).toLowerCase();
|
|
28
|
-
return s.includes("429") || s.includes("rate limit");
|
|
49
|
+
this.blockedUntil = Date.now() + RATE_LIMIT_RETRY_DELAYS_MS[0];
|
|
29
50
|
}
|
|
30
51
|
|
|
31
52
|
async run<T>(
|
|
32
53
|
request: () => Promise<T>,
|
|
33
|
-
opts?: {
|
|
54
|
+
opts?: {
|
|
55
|
+
userId?: number;
|
|
56
|
+
groupId?: number;
|
|
57
|
+
label?: string;
|
|
58
|
+
skipRetryOnRateLimit?: boolean;
|
|
59
|
+
},
|
|
34
60
|
): Promise<T | null> {
|
|
35
61
|
if (this.isBlocked()) {
|
|
36
62
|
this.log.warn(
|
|
@@ -46,25 +72,15 @@ export class RateLimitGuard {
|
|
|
46
72
|
}
|
|
47
73
|
this.rateLimiter.recordAIRequest(opts?.userId, opts?.groupId);
|
|
48
74
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (retries >= RATE_LIMIT_MAX_RETRIES) throw err;
|
|
59
|
-
retries += 1;
|
|
60
|
-
this.log.warn(
|
|
61
|
-
`[Chat] Rate limit hit, waiting ${RATE_LIMIT_RETRY_DELAY_MS / 1000}s...`,
|
|
62
|
-
);
|
|
63
|
-
await new Promise((resolve) =>
|
|
64
|
-
setTimeout(resolve, RATE_LIMIT_RETRY_DELAY_MS),
|
|
65
|
-
);
|
|
66
|
-
if (this.isBlocked()) this.blockedUntil = 0;
|
|
67
|
-
}
|
|
75
|
+
if (opts?.skipRetryOnRateLimit) {
|
|
76
|
+
return await request();
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
return await retryOn429(request, this.log, opts?.label);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
this.markBlocked();
|
|
83
|
+
throw err;
|
|
68
84
|
}
|
|
69
85
|
}
|
|
70
|
-
}
|
|
86
|
+
}
|
package/manage/types.ts
CHANGED
|
@@ -21,7 +21,12 @@ export type HistoryMediaOptions = {
|
|
|
21
21
|
|
|
22
22
|
export type RunRateLimitGuard = <T>(
|
|
23
23
|
request: () => Promise<T>,
|
|
24
|
-
opts?: {
|
|
24
|
+
opts?: {
|
|
25
|
+
userId?: number;
|
|
26
|
+
groupId?: number;
|
|
27
|
+
label?: string;
|
|
28
|
+
skipRetryOnRateLimit?: boolean;
|
|
29
|
+
},
|
|
25
30
|
) => Promise<T | null>;
|
|
26
31
|
|
|
27
32
|
export type GetGroupHistoryMessages = (
|