luckytri 1.0.4 → 1.0.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.
- package/LICENSE +21 -21
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/docs/en/CHANGELOG.md +20 -13
- package/docs/en/guide.md +7 -5
- package/docs/en/her-life.md +2 -2
- package/docs/en/webui.md +1 -1
- package/docs/zh/CHANGELOG.md +20 -13
- package/docs/zh/guide.md +7 -5
- package/docs/zh/her-life.md +2 -2
- package/docs/zh/webui.md +1 -1
- package/package.json +2 -1
- package/public/app/assets/ChatsPage-CFMVozoG.css +1 -0
- package/public/app/assets/{ChatsPage-CshdtTzV.js → ChatsPage-DCYMkJ91.js} +2 -2
- package/public/app/assets/{Empty-C7yzOBzy.js → Empty-CLyZkxR5.js} +1 -1
- package/public/app/assets/{Empty-DmUB2-Bk.css → Empty-DIobYG3x.css} +1 -1
- package/public/app/assets/{Field-BjFv4Rfn.js → Field-BpaRwLI_.js} +1 -1
- package/public/app/assets/{HeartPage-eqo6ru16.js → HeartPage-BIOMTiwr.js} +1 -1
- package/public/app/assets/HeartPage-C-2jvgzF.css +1 -0
- package/public/app/assets/{LifePage-CN8GE5NK.js → LifePage-BCgM4JkR.js} +1 -1
- package/public/app/assets/LifePage-CG1OHbRK.css +1 -0
- package/public/app/assets/MemoryPage-C_Bu4rfb.css +1 -0
- package/public/app/assets/{MemoryPage-CCpMN4Wg.js → MemoryPage-vShvRqMt.js} +1 -1
- package/public/app/assets/NaturePage-CPZlImT0.css +1 -0
- package/public/app/assets/{NaturePage-C0JqdBTR.js → NaturePage-Cy9ptr4G.js} +2 -2
- package/public/app/assets/PeoplePage-3RRUITDz.css +1 -0
- package/public/app/assets/{PeoplePage-BXGBbO4u.js → PeoplePage-CUZekB2E.js} +1 -1
- package/public/app/assets/{PluginsPage-Dksp5AV5.js → PluginsPage-1RM4rmJP.js} +1 -1
- package/public/app/assets/{SystemPage-BenIyTvk.css → SystemPage-BI2CB5Ig.css} +1 -1
- package/public/app/assets/{SystemPage-Cd7aa8eE.js → SystemPage-BeT5TmIV.js} +1 -1
- package/public/app/assets/TimePage-C-guwPk2.css +1 -0
- package/public/app/assets/{TimePage-Bs_zImb4.js → TimePage-taTmPMM8.js} +2 -2
- package/public/app/assets/{index-BHZcHUMh.css → index-DY5AM0Hz.css} +1 -1
- package/public/app/assets/{index-CyRtYxP3.js → index-DdYwC5ZU.js} +3 -3
- package/public/app/assets/{knowledge-BJOIyuJo.js → knowledge-DJmfu2KN.js} +1 -1
- package/public/app/assets/{media-Bwtx8blz.js → media-BEtnE0Gv.js} +1 -1
- package/public/app/assets/{mind-DtTVrW6Z.js → mind-Dtiw0-QK.js} +1 -1
- package/public/app/index.html +6 -5
- package/public/guide.en.html +11 -3
- package/public/guide.html +6 -3
- package/public/tutorial.en.md +2 -0
- package/public/tutorial.md +2 -0
- package/scripts/import-data.js +128 -128
- package/scripts/proxy-config.js +167 -167
- package/scripts/service-supervisor.js +50 -0
- package/scripts/start-server.js +71 -16
- package/scripts/update-instance.js +201 -201
- package/server/channels/adapters.js +27 -27
- package/server/channels/onebot/adapter.js +288 -288
- package/server/channels/qqbot/adapter.js +253 -253
- package/server/channels/qqbot/api.js +199 -199
- package/server/channels/qqbot/credentials.js +11 -11
- package/server/channels/qqbot/gateway.js +274 -274
- package/server/channels/qqbot/index.js +389 -389
- package/server/channels/segments.js +40 -40
- package/server/channels/session-key.js +153 -153
- package/server/channels/state.js +42 -42
- package/server/core/api.js +564 -564
- package/server/core/context-builder.js +288 -288
- package/server/core/context-compactor.js +454 -454
- package/server/core/conversation-cues.js +325 -325
- package/server/core/conversation-manager.js +87 -87
- package/server/core/feedback.js +8 -8
- package/server/core/group-style.js +72 -72
- package/server/core/initiative-context.js +66 -66
- package/server/core/input-budget.js +32 -32
- package/server/core/local-demo.js +98 -98
- package/server/core/message-manager.js +118 -118
- package/server/core/message-scheduler.js +78 -78
- package/server/core/model-json.js +190 -190
- package/server/core/model-presets.js +900 -900
- package/server/core/name-call.js +29 -29
- package/server/core/network.js +157 -157
- package/server/core/person-name.js +22 -22
- package/server/core/persona-manager.js +2 -1
- package/server/core/reply-delivery.js +404 -403
- package/server/core/reply-target-resolver.js +50 -50
- package/server/core/response-generator.js +50 -50
- package/server/core/response-validator.js +14 -2
- package/server/core/retired-prompts.js +51 -51
- package/server/core/sessions.js +109 -109
- package/server/core/speaker-names.js +92 -92
- package/server/core/topic-tracker.js +65 -65
- package/server/http.js +18 -18
- package/server/i18n/decisions.js +209 -206
- package/server/i18n/errors.js +4 -0
- package/server/i18n/index.js +144 -144
- package/server/i18n/models.js +111 -111
- package/server/knowledge/api.js +342 -342
- package/server/knowledge/embedding-profile.js +114 -114
- package/server/knowledge/index.js +13 -13
- package/server/knowledge/retrieval.js +93 -93
- package/server/knowledge/schema.js +158 -158
- package/server/knowledge/vector-index.js +90 -90
- package/server/mind/affect.js +265 -265
- package/server/mind/attention.js +200 -200
- package/server/mind/bonds.js +565 -565
- package/server/mind/clock.js +83 -83
- package/server/mind/continuity.js +562 -562
- package/server/mind/conversation-origin.js +175 -175
- package/server/mind/days.js +210 -210
- package/server/mind/evidence-read.js +45 -45
- package/server/mind/faces.js +373 -373
- package/server/mind/guard.js +60 -60
- package/server/mind/initiative.js +338 -338
- package/server/mind/life/activities.js +21 -21
- package/server/mind/life/context.js +330 -323
- package/server/mind/life/diary.js +20 -0
- package/server/mind/life/growth.js +261 -254
- package/server/mind/life/index.js +10 -2
- package/server/mind/life/presence.js +305 -305
- package/server/mind/meetings.js +1069 -1069
- package/server/mind/memory-policy.js +48 -48
- package/server/mind/memory.js +888 -888
- package/server/mind/nature.js +358 -358
- package/server/mind/novelty.js +19 -19
- package/server/mind/periods.js +118 -118
- package/server/mind/reading.js +120 -120
- package/server/mind/relationship-context.js +71 -0
- package/server/mind/relationships.js +390 -279
- package/server/mind/salience.js +201 -201
- package/server/mind/schema.js +567 -567
- package/server/mind/self.js +484 -484
- package/server/mind/thoughts.js +274 -274
- package/server/mind/time/agenda.js +242 -242
- package/server/mind/time/api.js +200 -200
- package/server/mind/time/attention.js +34 -34
- package/server/mind/time/delivery-links.js +130 -130
- package/server/mind/time/model-material.js +77 -77
- package/server/mind/time/presentation.js +51 -51
- package/server/mind/time/schedule.js +83 -83
- package/server/mind/time/schema.js +84 -84
- package/server/mind/time/search.js +383 -383
- package/server/mind/time/sharing.js +472 -472
- package/server/mind/time/task-links.js +311 -311
- package/server/mind/time/works.js +432 -432
- package/server/mind/traits.js +191 -191
- package/server/mind/view.js +9 -1
- package/server/runtime/installation.js +116 -116
- package/server/runtime/update-execution.js +2 -1
- package/server/runtime/update-manager.js +140 -140
- package/server/storage/data-package.js +298 -298
- package/server/storage/data-transfer-api.js +50 -50
- package/server/storage/data-transfer-worker.js +18 -18
- package/server/storage/data-transfer.js +416 -416
- package/server/studio/display-names.js +139 -139
- package/server/studio/management.js +102 -102
- package/server/studio/updates.js +144 -144
- package/server/version.js +10 -10
- package/public/app/assets/ChatsPage-DfyPCS9N.css +0 -1
- package/public/app/assets/HeartPage-Bft9wZp5.css +0 -1
- package/public/app/assets/LifePage-BUi5euBF.css +0 -1
- package/public/app/assets/MemoryPage-cZvXfkXb.css +0 -1
- package/public/app/assets/NaturePage-CHkpb5NK.css +0 -1
- package/public/app/assets/PeoplePage-BTU0H1pM.css +0 -1
- package/public/app/assets/TimePage-BOms_n4I.css +0 -1
package/server/core/name-call.js
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
const nameCallBoundary = "\\s,,、。!?!?::;;()()\\[\\]";
|
|
2
|
-
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
-
const HAIL = "喂哎诶嘿";
|
|
4
|
-
const PARTICLES = "啊呀呢吧哦嘛";
|
|
5
|
-
const AFTER = "你|我|今天|现在|怎么|能|可以|帮|回|看|咋|在|有没有|好累|辛苦|救";
|
|
6
|
-
// Talking about her, not to her. Checked against the text after the name.
|
|
7
|
-
const ABOUT =
|
|
8
|
-
/^(?:\s*(?:是谁|是个|是一个|的名字|这个名字|这人|那个人|的?头像|在群里|说的是))/i;
|
|
9
|
-
|
|
10
|
-
function calledBy(value, name) {
|
|
11
|
-
if (!name) return false;
|
|
12
|
-
const call = new RegExp(
|
|
13
|
-
`(?:^|[${nameCallBoundary}]|[${HAIL}])@?${escapeRegExp(name)}(?=$|[${nameCallBoundary}]|[${PARTICLES}]|(?:${AFTER}))`,
|
|
14
|
-
"gi",
|
|
15
|
-
);
|
|
16
|
-
let match;
|
|
17
|
-
while ((match = call.exec(value))) {
|
|
18
|
-
const after = value.slice(match.index + match[0].length);
|
|
19
|
-
if (!ABOUT.test(after)) return true;
|
|
20
|
-
if (match.index === call.lastIndex) call.lastIndex += 1;
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function isNameCall(text, names = []) {
|
|
26
|
-
const value = String(text || "").trim();
|
|
27
|
-
if (!value) return false;
|
|
28
|
-
return names.some((raw) => calledBy(value, String(raw || "").trim()));
|
|
29
|
-
}
|
|
1
|
+
const nameCallBoundary = "\\s,,、。!?!?::;;()()\\[\\]";
|
|
2
|
+
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3
|
+
const HAIL = "喂哎诶嘿";
|
|
4
|
+
const PARTICLES = "啊呀呢吧哦嘛";
|
|
5
|
+
const AFTER = "你|我|今天|现在|怎么|能|可以|帮|回|看|咋|在|有没有|好累|辛苦|救";
|
|
6
|
+
// Talking about her, not to her. Checked against the text after the name.
|
|
7
|
+
const ABOUT =
|
|
8
|
+
/^(?:\s*(?:是谁|是个|是一个|的名字|这个名字|这人|那个人|的?头像|在群里|说的是))/i;
|
|
9
|
+
|
|
10
|
+
function calledBy(value, name) {
|
|
11
|
+
if (!name) return false;
|
|
12
|
+
const call = new RegExp(
|
|
13
|
+
`(?:^|[${nameCallBoundary}]|[${HAIL}])@?${escapeRegExp(name)}(?=$|[${nameCallBoundary}]|[${PARTICLES}]|(?:${AFTER}))`,
|
|
14
|
+
"gi",
|
|
15
|
+
);
|
|
16
|
+
let match;
|
|
17
|
+
while ((match = call.exec(value))) {
|
|
18
|
+
const after = value.slice(match.index + match[0].length);
|
|
19
|
+
if (!ABOUT.test(after)) return true;
|
|
20
|
+
if (match.index === call.lastIndex) call.lastIndex += 1;
|
|
21
|
+
}
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function isNameCall(text, names = []) {
|
|
26
|
+
const value = String(text || "").trim();
|
|
27
|
+
if (!value) return false;
|
|
28
|
+
return names.some((raw) => calledBy(value, String(raw || "").trim()));
|
|
29
|
+
}
|
package/server/core/network.js
CHANGED
|
@@ -1,157 +1,157 @@
|
|
|
1
|
-
const TRANSIENT_NETWORK_CODES = new Set([
|
|
2
|
-
"ECONNRESET",
|
|
3
|
-
"ECONNREFUSED",
|
|
4
|
-
"ECONNABORTED",
|
|
5
|
-
"EPIPE",
|
|
6
|
-
"EHOSTUNREACH",
|
|
7
|
-
"ENETUNREACH",
|
|
8
|
-
"EAI_AGAIN",
|
|
9
|
-
"ETIMEDOUT",
|
|
10
|
-
"UND_ERR_CONNECT_TIMEOUT",
|
|
11
|
-
"UND_ERR_HEADERS_TIMEOUT",
|
|
12
|
-
"UND_ERR_BODY_TIMEOUT",
|
|
13
|
-
"UND_ERR_SOCKET",
|
|
14
|
-
"UND_ERR_CLOSED",
|
|
15
|
-
]);
|
|
16
|
-
|
|
17
|
-
export const RETRYABLE_STATUS = new Set([
|
|
18
|
-
408, 425, 429, 500, 502, 503, 504, 520, 521, 522, 523, 529,
|
|
19
|
-
]);
|
|
20
|
-
|
|
21
|
-
const TLS_CODE =
|
|
22
|
-
/^(?:ERR_SSL|ERR_TLS|EPROTO|CERT_|UNABLE_TO_VERIFY|DEPTH_ZERO|SELF_SIGNED)/;
|
|
23
|
-
|
|
24
|
-
export function isTransientNetworkError(error) {
|
|
25
|
-
let current = error;
|
|
26
|
-
for (let depth = 0; current && depth < 6; depth++, current = current.cause) {
|
|
27
|
-
if (
|
|
28
|
-
TRANSIENT_NETWORK_CODES.has(current.code) ||
|
|
29
|
-
TRANSIENT_NETWORK_CODES.has(current.errno)
|
|
30
|
-
)
|
|
31
|
-
return true;
|
|
32
|
-
if (
|
|
33
|
-
current.name === "TypeError" &&
|
|
34
|
-
/^(?:fetch failed|network error)$/i.test(String(current.message || ""))
|
|
35
|
-
)
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function isTimeoutError(error) {
|
|
42
|
-
let current = error;
|
|
43
|
-
for (let depth = 0; current && depth < 6; depth++, current = current.cause)
|
|
44
|
-
if (current.name === "TimeoutError") return true;
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function sanitizeDetail(text, max = 300) {
|
|
49
|
-
return String(text || "")
|
|
50
|
-
.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [已隐藏]")
|
|
51
|
-
.replace(/((?:api[_ -]?key|token|secret)\s*[:= ]+)[^\s,;]+/gi, "$1[已隐藏]")
|
|
52
|
-
.replace(/\bsk-[A-Za-z0-9_-]{8,}/g, "sk-[已隐藏]")
|
|
53
|
-
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => {
|
|
54
|
-
try {
|
|
55
|
-
return new URL(url).origin;
|
|
56
|
-
} catch {
|
|
57
|
-
return "[地址]";
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
.replace(/[\r\n\t]+/g, " ")
|
|
61
|
-
.trim()
|
|
62
|
-
.slice(0, max);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Walks the fetch cause chain so a proxy tunnel refusal, a TLS failure and a
|
|
66
|
-
// server reset are reported as different problems instead of "fetch failed".
|
|
67
|
-
export function describeNetworkError(error) {
|
|
68
|
-
const chain = [];
|
|
69
|
-
for (
|
|
70
|
-
let current = error, depth = 0;
|
|
71
|
-
current && depth < 6;
|
|
72
|
-
depth++, current = current.cause
|
|
73
|
-
)
|
|
74
|
-
chain.push(current);
|
|
75
|
-
if (isTimeoutError(error)) return { code: "TIMEOUT", detail: "" };
|
|
76
|
-
const messages = chain
|
|
77
|
-
.map((item) => String(item?.message || ""))
|
|
78
|
-
.filter(Boolean);
|
|
79
|
-
let code =
|
|
80
|
-
chain
|
|
81
|
-
.map((item) => item?.code || item?.errno)
|
|
82
|
-
.find((value) => TRANSIENT_NETWORK_CODES.has(value)) || "";
|
|
83
|
-
const proxy = messages
|
|
84
|
-
.map((message) => message.match(/Proxy response \((\d{3})\)/i))
|
|
85
|
-
.find(Boolean);
|
|
86
|
-
if (!code && proxy) code = `PROXY_${proxy[1]}`;
|
|
87
|
-
if (!code)
|
|
88
|
-
code =
|
|
89
|
-
chain
|
|
90
|
-
.map((item) => String(item?.code || ""))
|
|
91
|
-
.find((value) => TLS_CODE.test(value)) || "";
|
|
92
|
-
if (!code)
|
|
93
|
-
code =
|
|
94
|
-
chain
|
|
95
|
-
.map((item) => item?.code)
|
|
96
|
-
.find((value) => typeof value === "string" && value) || "";
|
|
97
|
-
if (!code && isTransientNetworkError(error)) code = "FETCH_FAILED";
|
|
98
|
-
const deepest =
|
|
99
|
-
messages.filter((message) => !/^fetch failed$/i.test(message)).at(-1) || "";
|
|
100
|
-
return { code, detail: sanitizeDetail(deepest, 160) };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
export function networkErrorCode(error) {
|
|
104
|
-
return describeNetworkError(error).code;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export function retryDelayFromResponse(response, fallbackMs = 900) {
|
|
108
|
-
const value = String(response?.headers?.get?.("retry-after") || "").trim();
|
|
109
|
-
if (value) {
|
|
110
|
-
const seconds = Number(value);
|
|
111
|
-
const dateDelay = Date.parse(value) - Date.now();
|
|
112
|
-
const delay = Number.isFinite(seconds)
|
|
113
|
-
? seconds * 1000
|
|
114
|
-
: Number.isFinite(dateDelay) && dateDelay > 0
|
|
115
|
-
? dateDelay
|
|
116
|
-
: Number.NaN;
|
|
117
|
-
if (Number.isFinite(delay)) return Math.max(0, Math.min(delay, 30000));
|
|
118
|
-
}
|
|
119
|
-
return Math.max(0, Math.min(Number(fallbackMs) || 900, 30000));
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export async function withTransientRequestRetry(
|
|
123
|
-
operation,
|
|
124
|
-
{
|
|
125
|
-
retries = 3,
|
|
126
|
-
delayMs = 600,
|
|
127
|
-
maxDelayMs = 8000,
|
|
128
|
-
deadlineMs = 45000,
|
|
129
|
-
onRetry,
|
|
130
|
-
random = Math.random,
|
|
131
|
-
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
132
|
-
now = Date.now,
|
|
133
|
-
} = {},
|
|
134
|
-
) {
|
|
135
|
-
const started = now();
|
|
136
|
-
for (let attempt = 0; ; attempt++) {
|
|
137
|
-
try {
|
|
138
|
-
return await operation(attempt);
|
|
139
|
-
} catch (error) {
|
|
140
|
-
if (
|
|
141
|
-
(!isTransientNetworkError(error) && error?.retryableRequest !== true) ||
|
|
142
|
-
attempt >= retries
|
|
143
|
-
)
|
|
144
|
-
throw error;
|
|
145
|
-
// Half fixed, half random: bursts of failures spread out instead of
|
|
146
|
-
// hitting a recovering gateway at the same moment.
|
|
147
|
-
const ceiling = Math.min(maxDelayMs, delayMs * 2 ** attempt);
|
|
148
|
-
let wait = ceiling / 2 + random() * (ceiling / 2);
|
|
149
|
-
const hinted = Number(error?.retryDelayMs);
|
|
150
|
-
if (Number.isFinite(hinted))
|
|
151
|
-
wait = Math.max(wait, Math.min(hinted, 30000));
|
|
152
|
-
if (now() - started + wait > deadlineMs) throw error;
|
|
153
|
-
onRetry?.(attempt + 1, error, wait);
|
|
154
|
-
await sleep(wait);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
1
|
+
const TRANSIENT_NETWORK_CODES = new Set([
|
|
2
|
+
"ECONNRESET",
|
|
3
|
+
"ECONNREFUSED",
|
|
4
|
+
"ECONNABORTED",
|
|
5
|
+
"EPIPE",
|
|
6
|
+
"EHOSTUNREACH",
|
|
7
|
+
"ENETUNREACH",
|
|
8
|
+
"EAI_AGAIN",
|
|
9
|
+
"ETIMEDOUT",
|
|
10
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
11
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
12
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
13
|
+
"UND_ERR_SOCKET",
|
|
14
|
+
"UND_ERR_CLOSED",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export const RETRYABLE_STATUS = new Set([
|
|
18
|
+
408, 425, 429, 500, 502, 503, 504, 520, 521, 522, 523, 529,
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
const TLS_CODE =
|
|
22
|
+
/^(?:ERR_SSL|ERR_TLS|EPROTO|CERT_|UNABLE_TO_VERIFY|DEPTH_ZERO|SELF_SIGNED)/;
|
|
23
|
+
|
|
24
|
+
export function isTransientNetworkError(error) {
|
|
25
|
+
let current = error;
|
|
26
|
+
for (let depth = 0; current && depth < 6; depth++, current = current.cause) {
|
|
27
|
+
if (
|
|
28
|
+
TRANSIENT_NETWORK_CODES.has(current.code) ||
|
|
29
|
+
TRANSIENT_NETWORK_CODES.has(current.errno)
|
|
30
|
+
)
|
|
31
|
+
return true;
|
|
32
|
+
if (
|
|
33
|
+
current.name === "TypeError" &&
|
|
34
|
+
/^(?:fetch failed|network error)$/i.test(String(current.message || ""))
|
|
35
|
+
)
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function isTimeoutError(error) {
|
|
42
|
+
let current = error;
|
|
43
|
+
for (let depth = 0; current && depth < 6; depth++, current = current.cause)
|
|
44
|
+
if (current.name === "TimeoutError") return true;
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function sanitizeDetail(text, max = 300) {
|
|
49
|
+
return String(text || "")
|
|
50
|
+
.replace(/Bearer\s+[^\s,;]+/gi, "Bearer [已隐藏]")
|
|
51
|
+
.replace(/((?:api[_ -]?key|token|secret)\s*[:= ]+)[^\s,;]+/gi, "$1[已隐藏]")
|
|
52
|
+
.replace(/\bsk-[A-Za-z0-9_-]{8,}/g, "sk-[已隐藏]")
|
|
53
|
+
.replace(/https?:\/\/[^\s"'<>]+/gi, (url) => {
|
|
54
|
+
try {
|
|
55
|
+
return new URL(url).origin;
|
|
56
|
+
} catch {
|
|
57
|
+
return "[地址]";
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.replace(/[\r\n\t]+/g, " ")
|
|
61
|
+
.trim()
|
|
62
|
+
.slice(0, max);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Walks the fetch cause chain so a proxy tunnel refusal, a TLS failure and a
|
|
66
|
+
// server reset are reported as different problems instead of "fetch failed".
|
|
67
|
+
export function describeNetworkError(error) {
|
|
68
|
+
const chain = [];
|
|
69
|
+
for (
|
|
70
|
+
let current = error, depth = 0;
|
|
71
|
+
current && depth < 6;
|
|
72
|
+
depth++, current = current.cause
|
|
73
|
+
)
|
|
74
|
+
chain.push(current);
|
|
75
|
+
if (isTimeoutError(error)) return { code: "TIMEOUT", detail: "" };
|
|
76
|
+
const messages = chain
|
|
77
|
+
.map((item) => String(item?.message || ""))
|
|
78
|
+
.filter(Boolean);
|
|
79
|
+
let code =
|
|
80
|
+
chain
|
|
81
|
+
.map((item) => item?.code || item?.errno)
|
|
82
|
+
.find((value) => TRANSIENT_NETWORK_CODES.has(value)) || "";
|
|
83
|
+
const proxy = messages
|
|
84
|
+
.map((message) => message.match(/Proxy response \((\d{3})\)/i))
|
|
85
|
+
.find(Boolean);
|
|
86
|
+
if (!code && proxy) code = `PROXY_${proxy[1]}`;
|
|
87
|
+
if (!code)
|
|
88
|
+
code =
|
|
89
|
+
chain
|
|
90
|
+
.map((item) => String(item?.code || ""))
|
|
91
|
+
.find((value) => TLS_CODE.test(value)) || "";
|
|
92
|
+
if (!code)
|
|
93
|
+
code =
|
|
94
|
+
chain
|
|
95
|
+
.map((item) => item?.code)
|
|
96
|
+
.find((value) => typeof value === "string" && value) || "";
|
|
97
|
+
if (!code && isTransientNetworkError(error)) code = "FETCH_FAILED";
|
|
98
|
+
const deepest =
|
|
99
|
+
messages.filter((message) => !/^fetch failed$/i.test(message)).at(-1) || "";
|
|
100
|
+
return { code, detail: sanitizeDetail(deepest, 160) };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function networkErrorCode(error) {
|
|
104
|
+
return describeNetworkError(error).code;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function retryDelayFromResponse(response, fallbackMs = 900) {
|
|
108
|
+
const value = String(response?.headers?.get?.("retry-after") || "").trim();
|
|
109
|
+
if (value) {
|
|
110
|
+
const seconds = Number(value);
|
|
111
|
+
const dateDelay = Date.parse(value) - Date.now();
|
|
112
|
+
const delay = Number.isFinite(seconds)
|
|
113
|
+
? seconds * 1000
|
|
114
|
+
: Number.isFinite(dateDelay) && dateDelay > 0
|
|
115
|
+
? dateDelay
|
|
116
|
+
: Number.NaN;
|
|
117
|
+
if (Number.isFinite(delay)) return Math.max(0, Math.min(delay, 30000));
|
|
118
|
+
}
|
|
119
|
+
return Math.max(0, Math.min(Number(fallbackMs) || 900, 30000));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function withTransientRequestRetry(
|
|
123
|
+
operation,
|
|
124
|
+
{
|
|
125
|
+
retries = 3,
|
|
126
|
+
delayMs = 600,
|
|
127
|
+
maxDelayMs = 8000,
|
|
128
|
+
deadlineMs = 45000,
|
|
129
|
+
onRetry,
|
|
130
|
+
random = Math.random,
|
|
131
|
+
sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
132
|
+
now = Date.now,
|
|
133
|
+
} = {},
|
|
134
|
+
) {
|
|
135
|
+
const started = now();
|
|
136
|
+
for (let attempt = 0; ; attempt++) {
|
|
137
|
+
try {
|
|
138
|
+
return await operation(attempt);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (
|
|
141
|
+
(!isTransientNetworkError(error) && error?.retryableRequest !== true) ||
|
|
142
|
+
attempt >= retries
|
|
143
|
+
)
|
|
144
|
+
throw error;
|
|
145
|
+
// Half fixed, half random: bursts of failures spread out instead of
|
|
146
|
+
// hitting a recovering gateway at the same moment.
|
|
147
|
+
const ceiling = Math.min(maxDelayMs, delayMs * 2 ** attempt);
|
|
148
|
+
let wait = ceiling / 2 + random() * (ceiling / 2);
|
|
149
|
+
const hinted = Number(error?.retryDelayMs);
|
|
150
|
+
if (Number.isFinite(hinted))
|
|
151
|
+
wait = Math.max(wait, Math.min(hinted, 30000));
|
|
152
|
+
if (now() - started + wait > deadlineMs) throw error;
|
|
153
|
+
onRetry?.(attempt + 1, error, wait);
|
|
154
|
+
await sleep(wait);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { readableName } from "./speaker-names.js";
|
|
2
|
-
|
|
3
|
-
// Only a person's own explicit request supplies a preferred form of address.
|
|
4
|
-
export function requestedName(value) {
|
|
5
|
-
const words = String(value || "").trim();
|
|
6
|
-
if (/不要记|别记|不许记|不要叫我|别叫我|忘掉/.test(words)) return "";
|
|
7
|
-
const match = words.match(
|
|
8
|
-
/(?:^|[,,。!!;;\s])(?:以后|今后|请(?:你)?|麻烦|你(?:就|可以)?|记住[,,::\s]*)*(?:叫我|称呼我(?:为)?|(?:记住[,,::\s]*)?我的(?:名字|昵称)(?:是|叫)|记住[,,::\s]*我叫)[\s::]*[“"「『]?([^,,。!!??;;\n”"」』]{1,32})/u,
|
|
9
|
-
);
|
|
10
|
-
if (!match) return "";
|
|
11
|
-
const name = match[1]
|
|
12
|
-
.trim()
|
|
13
|
-
.replace(/(?:就好|就行|即可|好吗|吧|哦|呀|啦)$/, "")
|
|
14
|
-
.trim();
|
|
15
|
-
if (
|
|
16
|
-
/^(?:group|private|onebot):|^(?:群聊|私聊)\s*\d|^(?:去|来|帮|把|别|先|等|做|打开|关掉|回答|发消息)|叫你|不要|记住/.test(
|
|
17
|
-
name,
|
|
18
|
-
)
|
|
19
|
-
)
|
|
20
|
-
return "";
|
|
21
|
-
return readableName(name, "");
|
|
22
|
-
}
|
|
1
|
+
import { readableName } from "./speaker-names.js";
|
|
2
|
+
|
|
3
|
+
// Only a person's own explicit request supplies a preferred form of address.
|
|
4
|
+
export function requestedName(value) {
|
|
5
|
+
const words = String(value || "").trim();
|
|
6
|
+
if (/不要记|别记|不许记|不要叫我|别叫我|忘掉/.test(words)) return "";
|
|
7
|
+
const match = words.match(
|
|
8
|
+
/(?:^|[,,。!!;;\s])(?:以后|今后|请(?:你)?|麻烦|你(?:就|可以)?|记住[,,::\s]*)*(?:叫我|称呼我(?:为)?|(?:记住[,,::\s]*)?我的(?:名字|昵称)(?:是|叫)|记住[,,::\s]*我叫)[\s::]*[“"「『]?([^,,。!!??;;\n”"」』]{1,32})/u,
|
|
9
|
+
);
|
|
10
|
+
if (!match) return "";
|
|
11
|
+
const name = match[1]
|
|
12
|
+
.trim()
|
|
13
|
+
.replace(/(?:就好|就行|即可|好吗|吧|哦|呀|啦)$/, "")
|
|
14
|
+
.trim();
|
|
15
|
+
if (
|
|
16
|
+
/^(?:group|private|onebot):|^(?:群聊|私聊)\s*\d|^(?:去|来|帮|把|别|先|等|做|打开|关掉|回答|发消息)|叫你|不要|记住/.test(
|
|
17
|
+
name,
|
|
18
|
+
)
|
|
19
|
+
)
|
|
20
|
+
return "";
|
|
21
|
+
return readableName(name, "");
|
|
22
|
+
}
|
|
@@ -186,7 +186,8 @@ export function replyPrompt(p, custom = PROMPTS, stage = "generation") {
|
|
|
186
186
|
: []),
|
|
187
187
|
"【任务】",
|
|
188
188
|
PROMPTS.system,
|
|
189
|
-
"
|
|
189
|
+
"【真实的关系】由技术诞生的你也可以有真实的人际关系,妹妹、姐姐等称呼不必靠血缘或肉身证明;不默认把它们贬成假的角色扮演,也不把旧的抗拒重复成永远不能改变的自己。关系记录、实际相处和自己的感受共同进入判断;withMe 是已经发生的相处,spokenReplies 是实际对这个账号说过的话的次数,不能凭旧印象说从未聊过。被简单问到谁是谁时自然认出名字即可,不无故播报后台记录或反复宣布边界;对新的关系如何生活仍由你选择。",
|
|
190
|
+
"【相互联系的人】relationships.known 和人物中的 relationship 来自同一份当前人际关系记录,是你现在知道的人与关系,属于你自己的生活,不是角色扮演剧本。对方不在当前会话、不是本轮说话人,也不等于你不认识;被问谁是妹妹等关系时,从当前记录认出对应账号与名字,不能只找在场的人。peerRole 是对方相对于你的称呼,selfRole 是你相对于对方的称呼,kind=bot 表示另一位独立的机器人。知道这份关系与自己的感受、信任、愿不愿这样称呼分开:你仍可选择如何相处和表达边界,不必每句话叫亲属称呼。created 是当前关系记录建立或修改的时间;旧聊天、手记、自我线索里的否认或玩笑是当时说过的话,保留历史但不能据此断言当前关系不存在,也不为了维持旧说法否认新认识。需要时自然说明此前没接上或当时的想法。origin=owner 保留关系的来源,不表示该记录可以忽略,也不能据此编造共同童年、旧约定、已经建立的信任或对方的同意。名字相同不代表同一账号;关系不会扩大隐私或执行权限。真实相处影响你对这个人的感受与以后选择,不用机械播报字段。",
|
|
190
191
|
...([
|
|
191
192
|
"expression",
|
|
192
193
|
"initiative",
|