@rei-standard/amsg-server 2.1.1 → 2.3.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/README.md +32 -0
- package/dist/index.cjs +188 -21
- package/dist/index.d.cts +297 -24
- package/dist/index.d.ts +297 -24
- package/dist/index.mjs +185 -18
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -65,6 +65,38 @@ const rei = await createReiServer({
|
|
|
65
65
|
|
|
66
66
|
如果上游返回 `405 Method Not Allowed`,通常表示 `apiUrl` 指向了基础域名而非聊天端点,请优先检查配置值。
|
|
67
67
|
|
|
68
|
+
## 提示词字段(`completePrompt` vs `messages`,2.2.0+)
|
|
69
|
+
|
|
70
|
+
AI 配置消息的提示词可以用两种形态之一,**互斥二选一**:
|
|
71
|
+
|
|
72
|
+
- `completePrompt: string` —— 简单场景:内部包成单条 `{role:'user', content}` 后发给 LLM。
|
|
73
|
+
- `messages: Array<{ role: 'system'|'user'|'assistant'|'tool', content: string | unknown[] }>` —— 多轮 / 带 system role:**原样**转发给 LLM,不做任何 role 注入或重排。和上游主聊天路径调用 LLM 的 body 字节级一致。
|
|
74
|
+
|
|
75
|
+
两个字段同时给 → `400 INVALID_PARAMETERS`;都不给且没 `userMessage`(仅 instant 类型允许 fallback)也是 400。`messages` 数组必须非空,role 必须是上面四种之一。
|
|
76
|
+
|
|
77
|
+
可选 `temperature?: number` 透传给 LLM:`completePrompt` 路径未传时默认 0.8(保持旧行为);`messages` 路径未传时**不发**,让上游主路径自己决定。
|
|
78
|
+
|
|
79
|
+
## 自定义分句正则 `splitPattern`(2.3.0+)
|
|
80
|
+
|
|
81
|
+
`processSingleMessage` 默认按 `/([。!?!?]+)/` 把 LLM 返回的整段文本切成多条推送(每条间隔 1.5s)。`splitPattern` 让调用方覆盖这个正则:
|
|
82
|
+
|
|
83
|
+
- `splitPattern: string` —— 单个正则 source(不带 flags)。例:`"([\\n]+)"` 按换行切。
|
|
84
|
+
- `splitPattern: string[]` —— **级联**应用:第一个正则切完,每段再用第二个切。例:`["(\\n\\n+)", "([。!?!?]+)"]` 先按段落、再按句号。
|
|
85
|
+
- 不传 / `null` / `[]` → 走默认正则,行为字节级与 2.2.x 一致;老库存任务(无此字段)零迁移。
|
|
86
|
+
|
|
87
|
+
**约定**:
|
|
88
|
+
|
|
89
|
+
- 传**正则 source**,不要带两边的 `/.../` 也不要带尾部 flag(`/foo/i` 会被当字面量斜杠 + 字面量 `i` 匹配)。需要大小写不敏感请用 `[Aa]` 这种字符类替代。
|
|
90
|
+
- 想让分隔符回贴到前一段(与默认行为一致),把分隔符包进 `(...)` 捕获组。库不会自动包——传 `"\\n+"` 而不是 `"(\\n+)"` 会得到首尾相连、分隔符丢失的奇怪结果。
|
|
91
|
+
- 数组语义是级联,不是"任一匹配就切"。后者请自己用 `|` 合一条正则。
|
|
92
|
+
- 限制:每项 ≤ 200 字符,数组 ≤ 10 项;非法或无法 `new RegExp(...)` 通过 → `400 INVALID_PARAMETERS`(schedule)/ `400 INVALID_UPDATE_DATA`(update)。
|
|
93
|
+
- `update-message` 显式传 `splitPattern: null` 可重置回默认;不传则保留原值。
|
|
94
|
+
|
|
95
|
+
## 导出(新增)
|
|
96
|
+
|
|
97
|
+
- `validateLlmMessagesArray(messages)` — 同步预校验 messages 数组,返回 `string | null`(错误信息 / 通过)。和 `@rei-standard/amsg-instant` 的校验规则字节级一致。
|
|
98
|
+
- `validateSplitPattern(value)` — 同步预校验 splitPattern(string / string[] / null),返回 `string | null`。
|
|
99
|
+
|
|
68
100
|
## 一体化初始化流程
|
|
69
101
|
|
|
70
102
|
1. 管理员配置环境变量(VAPID + tenant secrets)
|
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,71 @@ function isValidUUIDv4(uuid) {
|
|
|
47
47
|
const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
48
48
|
return uuidV4Regex.test(uuid);
|
|
49
49
|
}
|
|
50
|
+
var VALID_LLM_MESSAGE_ROLES = /* @__PURE__ */ new Set(["system", "user", "assistant", "tool"]);
|
|
51
|
+
var AVATAR_URL_MAX_LENGTH = 2048;
|
|
52
|
+
function validateAvatarUrl(value) {
|
|
53
|
+
if (value === void 0 || value === null) return null;
|
|
54
|
+
if (typeof value !== "string") {
|
|
55
|
+
return "avatarUrl \u5FC5\u987B\u662F\u5B57\u7B26\u4E32";
|
|
56
|
+
}
|
|
57
|
+
if (/^data:/i.test(value)) {
|
|
58
|
+
return "\u5934\u50CF\u4E0D\u652F\u6301\u4F20\u5165 data: URI\uFF0C\u8BF7\u6539\u4E3A\u516C\u7F51\u53EF\u8BBF\u95EE\u7684 https:// \u56FE\u7247 URL";
|
|
59
|
+
}
|
|
60
|
+
if (value.length > AVATAR_URL_MAX_LENGTH) {
|
|
61
|
+
return `\u5934\u50CF URL \u957F\u5EA6 ${value.length} \u5B57\u7B26\u8D85\u8FC7 ${AVATAR_URL_MAX_LENGTH} \u4E0A\u9650\uFF0C\u8BF7\u6539\u4E3A\u66F4\u77ED\u7684\u56FE\u7247 URL`;
|
|
62
|
+
}
|
|
63
|
+
if (!isValidUrl(value)) {
|
|
64
|
+
return "avatarUrl \u4E0D\u662F\u5408\u6CD5 URL";
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
var SPLIT_PATTERN_MAX_LENGTH = 200;
|
|
69
|
+
var SPLIT_PATTERN_MAX_ITEMS = 10;
|
|
70
|
+
function validateSplitPattern(value) {
|
|
71
|
+
if (value === void 0 || value === null) return null;
|
|
72
|
+
const isArray = Array.isArray(value);
|
|
73
|
+
const items = isArray ? value : [value];
|
|
74
|
+
if (isArray && items.length === 0) return null;
|
|
75
|
+
if (items.length > SPLIT_PATTERN_MAX_ITEMS) {
|
|
76
|
+
return `splitPattern \u6570\u7EC4\u6700\u591A ${SPLIT_PATTERN_MAX_ITEMS} \u9879`;
|
|
77
|
+
}
|
|
78
|
+
for (let i = 0; i < items.length; i++) {
|
|
79
|
+
const s = items[i];
|
|
80
|
+
const label = isArray ? `splitPattern[${i}]` : "splitPattern";
|
|
81
|
+
if (typeof s !== "string") return `${label} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`;
|
|
82
|
+
if (s.length > SPLIT_PATTERN_MAX_LENGTH) {
|
|
83
|
+
return `${label} \u4E0D\u80FD\u8D85\u8FC7 ${SPLIT_PATTERN_MAX_LENGTH} \u5B57\u7B26`;
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
new RegExp(s);
|
|
87
|
+
} catch (_) {
|
|
88
|
+
return `${label} \u4E0D\u662F\u6709\u6548\u6B63\u5219\u8868\u8FBE\u5F0F`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function validateLlmMessagesArray(messages) {
|
|
94
|
+
if (!Array.isArray(messages) || messages.length === 0) {
|
|
95
|
+
return "messages must be a non-empty array";
|
|
96
|
+
}
|
|
97
|
+
for (let i = 0; i < messages.length; i++) {
|
|
98
|
+
const m = messages[i];
|
|
99
|
+
if (!m || typeof m !== "object" || Array.isArray(m)) {
|
|
100
|
+
return `messages[${i}] must be an object`;
|
|
101
|
+
}
|
|
102
|
+
if (!VALID_LLM_MESSAGE_ROLES.has(m.role)) {
|
|
103
|
+
return `messages[${i}].role must be one of system / user / assistant / tool`;
|
|
104
|
+
}
|
|
105
|
+
if (typeof m.content === "string") {
|
|
106
|
+
if (!m.content) return `messages[${i}].content must be a non-empty string`;
|
|
107
|
+
} else if (Array.isArray(m.content)) {
|
|
108
|
+
if (m.content.length === 0) return `messages[${i}].content array must be non-empty`;
|
|
109
|
+
} else {
|
|
110
|
+
return `messages[${i}].content must be a non-empty string or a non-empty array`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
50
115
|
function validateScheduleMessagePayload(payload) {
|
|
51
116
|
if (!payload.contactName || typeof payload.contactName !== "string") {
|
|
52
117
|
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["contactName"] } };
|
|
@@ -74,9 +139,35 @@ function validateScheduleMessagePayload(payload) {
|
|
|
74
139
|
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { missingFields: ["userMessage (required for fixed type)"] } };
|
|
75
140
|
}
|
|
76
141
|
}
|
|
142
|
+
const promptCheck = (() => {
|
|
143
|
+
const hasCompletePrompt = payload.completePrompt !== void 0 && payload.completePrompt !== null && payload.completePrompt !== "";
|
|
144
|
+
const hasMessages = payload.messages !== void 0 && payload.messages !== null;
|
|
145
|
+
if (hasCompletePrompt && hasMessages) {
|
|
146
|
+
return {
|
|
147
|
+
error: { code: "INVALID_PARAMETERS", message: "exactly one of `completePrompt` or `messages` must be provided\uFF08\u4E24\u8005\u4E0D\u80FD\u540C\u65F6\u51FA\u73B0\uFF09", details: { invalidFields: ["completePrompt", "messages"] } },
|
|
148
|
+
hasCompletePrompt: true,
|
|
149
|
+
hasMessages: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
if (hasMessages) {
|
|
153
|
+
const err = validateLlmMessagesArray(payload.messages);
|
|
154
|
+
if (err) {
|
|
155
|
+
return {
|
|
156
|
+
error: { code: "INVALID_PARAMETERS", message: err, details: { invalidFields: ["messages"] } },
|
|
157
|
+
hasCompletePrompt: false,
|
|
158
|
+
hasMessages: true
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { error: null, hasCompletePrompt, hasMessages };
|
|
163
|
+
})();
|
|
164
|
+
if (promptCheck.error) {
|
|
165
|
+
return { valid: false, errorCode: promptCheck.error.code, errorMessage: promptCheck.error.message, details: promptCheck.error.details };
|
|
166
|
+
}
|
|
167
|
+
const hasPrompt = promptCheck.hasCompletePrompt || promptCheck.hasMessages;
|
|
77
168
|
if (payload.messageType === "prompted" || payload.messageType === "auto") {
|
|
78
169
|
const missingAiFields = [];
|
|
79
|
-
if (!
|
|
170
|
+
if (!hasPrompt) missingAiFields.push("completePrompt or messages");
|
|
80
171
|
if (!payload.apiUrl) missingAiFields.push("apiUrl");
|
|
81
172
|
if (!payload.apiKey) missingAiFields.push("apiKey");
|
|
82
173
|
if (!payload.primaryModel) missingAiFields.push("primaryModel");
|
|
@@ -88,14 +179,18 @@ function validateScheduleMessagePayload(payload) {
|
|
|
88
179
|
if (payload.recurrenceType && payload.recurrenceType !== "none") {
|
|
89
180
|
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u7684 recurrenceType \u5FC5\u987B\u4E3A none", details: { invalidFields: ['recurrenceType (must be "none" for instant type)'] } };
|
|
90
181
|
}
|
|
91
|
-
const hasAiConfig =
|
|
182
|
+
const hasAiConfig = hasPrompt && payload.apiUrl && payload.apiKey && payload.primaryModel;
|
|
92
183
|
const hasUserMessage = payload.userMessage;
|
|
93
184
|
if (!hasAiConfig && !hasUserMessage) {
|
|
94
|
-
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u5FC5\u987B\u63D0\u4F9B userMessage \u6216\u5B8C\u6574\u7684 AI \u914D\u7F6E", details: { missingFields: ["userMessage or (completePrompt + apiUrl + apiKey + primaryModel)"] } };
|
|
185
|
+
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "instant \u7C7B\u578B\u5FC5\u987B\u63D0\u4F9B userMessage \u6216\u5B8C\u6574\u7684 AI \u914D\u7F6E", details: { missingFields: ["userMessage or ((completePrompt | messages) + apiUrl + apiKey + primaryModel)"] } };
|
|
95
186
|
}
|
|
96
187
|
}
|
|
97
|
-
if (payload.
|
|
98
|
-
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["
|
|
188
|
+
if (payload.temperature !== void 0 && payload.temperature !== null && (typeof payload.temperature !== "number" || !Number.isFinite(payload.temperature))) {
|
|
189
|
+
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["temperature (must be a finite number)"] } };
|
|
190
|
+
}
|
|
191
|
+
const avatarErr = validateAvatarUrl(payload.avatarUrl);
|
|
192
|
+
if (avatarErr) {
|
|
193
|
+
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: avatarErr, details: { invalidFields: ["avatarUrl"] } };
|
|
99
194
|
}
|
|
100
195
|
if (payload.uuid && !isValidUUID(payload.uuid)) {
|
|
101
196
|
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["uuid (invalid UUID format)"] } };
|
|
@@ -103,6 +198,10 @@ function validateScheduleMessagePayload(payload) {
|
|
|
103
198
|
if (payload.messageSubtype !== void 0 && payload.messageSubtype !== null && typeof payload.messageSubtype !== "string") {
|
|
104
199
|
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: "\u7F3A\u5C11\u5FC5\u9700\u53C2\u6570\u6216\u53C2\u6570\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["messageSubtype"] } };
|
|
105
200
|
}
|
|
201
|
+
const splitErr = validateSplitPattern(payload.splitPattern);
|
|
202
|
+
if (splitErr) {
|
|
203
|
+
return { valid: false, errorCode: "INVALID_PARAMETERS", errorMessage: splitErr, details: { invalidFields: ["splitPattern"] } };
|
|
204
|
+
}
|
|
106
205
|
return { valid: true };
|
|
107
206
|
}
|
|
108
207
|
|
|
@@ -383,6 +482,26 @@ function isUniqueViolation(error) {
|
|
|
383
482
|
|
|
384
483
|
// src/server/lib/message-processor.js
|
|
385
484
|
|
|
485
|
+
var DEFAULT_SPLIT_REGEX = /([。!?!?]+)/;
|
|
486
|
+
function splitOnceByRegex(chunk, regex) {
|
|
487
|
+
const out = chunk.split(regex).reduce((acc, part, i, arr) => {
|
|
488
|
+
if (i % 2 === 0 && part.trim()) {
|
|
489
|
+
const punctuation = arr[i + 1] || "";
|
|
490
|
+
acc.push(part.trim() + punctuation);
|
|
491
|
+
}
|
|
492
|
+
return acc;
|
|
493
|
+
}, []).filter((s) => s.length > 0);
|
|
494
|
+
return out.length > 0 ? out : [chunk];
|
|
495
|
+
}
|
|
496
|
+
function splitMessageIntoSentences(messageContent, splitPattern = null) {
|
|
497
|
+
const sources = splitPattern == null ? null : Array.isArray(splitPattern) ? splitPattern : [splitPattern];
|
|
498
|
+
const regexes = sources && sources.length > 0 ? sources.map((s) => new RegExp(s)) : [DEFAULT_SPLIT_REGEX];
|
|
499
|
+
let chunks = [messageContent];
|
|
500
|
+
for (const regex of regexes) {
|
|
501
|
+
chunks = chunks.flatMap((c) => splitOnceByRegex(c, regex));
|
|
502
|
+
}
|
|
503
|
+
return chunks.length > 0 ? chunks : [messageContent];
|
|
504
|
+
}
|
|
386
505
|
async function processSingleMessage(task, ctx, providedMasterKey) {
|
|
387
506
|
try {
|
|
388
507
|
const masterKey = providedMasterKey || ctx.masterKey;
|
|
@@ -395,7 +514,8 @@ async function processSingleMessage(task, ctx, providedMasterKey) {
|
|
|
395
514
|
if (decryptedPayload.messageType === "fixed") {
|
|
396
515
|
messageContent = decryptedPayload.userMessage;
|
|
397
516
|
} else if (decryptedPayload.messageType === "instant") {
|
|
398
|
-
|
|
517
|
+
const hasPrompt = !!decryptedPayload.completePrompt || Array.isArray(decryptedPayload.messages) && decryptedPayload.messages.length > 0;
|
|
518
|
+
if (hasPrompt && decryptedPayload.apiUrl && decryptedPayload.apiKey && decryptedPayload.primaryModel) {
|
|
399
519
|
messageContent = await _callAI(decryptedPayload);
|
|
400
520
|
} else if (decryptedPayload.userMessage) {
|
|
401
521
|
messageContent = decryptedPayload.userMessage;
|
|
@@ -407,14 +527,7 @@ async function processSingleMessage(task, ctx, providedMasterKey) {
|
|
|
407
527
|
} else {
|
|
408
528
|
throw new Error("Invalid message configuration: no content source available");
|
|
409
529
|
}
|
|
410
|
-
const
|
|
411
|
-
if (i % 2 === 0 && part.trim()) {
|
|
412
|
-
const punctuation = arr[i + 1] || "";
|
|
413
|
-
acc.push(part.trim() + punctuation);
|
|
414
|
-
}
|
|
415
|
-
return acc;
|
|
416
|
-
}, []).filter((s) => s.length > 0);
|
|
417
|
-
const messages = sentences.length > 0 ? sentences : [messageContent];
|
|
530
|
+
const messages = splitMessageIntoSentences(messageContent, _nullishCoalesce(decryptedPayload.splitPattern, () => ( null)));
|
|
418
531
|
if (!ctx.vapid.email || !ctx.vapid.publicKey || !ctx.vapid.privateKey) {
|
|
419
532
|
throw new Error("VAPID configuration missing - push notifications cannot be sent");
|
|
420
533
|
}
|
|
@@ -530,18 +643,23 @@ async function _callAI(payload) {
|
|
|
530
643
|
);
|
|
531
644
|
}
|
|
532
645
|
const aiData = await aiResponse.json();
|
|
533
|
-
const content = _optionalChain([aiData, 'optionalAccess',
|
|
646
|
+
const content = _optionalChain([aiData, 'optionalAccess', _2 => _2.choices, 'optionalAccess', _3 => _3[0], 'optionalAccess', _4 => _4.message, 'optionalAccess', _5 => _5.content]);
|
|
534
647
|
if (typeof content !== "string" || !content.trim()) {
|
|
535
648
|
throw new Error("AI API error: response missing choices[0].message.content");
|
|
536
649
|
}
|
|
537
650
|
return content.trim();
|
|
538
651
|
}
|
|
539
652
|
function buildAiRequestBody(payload) {
|
|
653
|
+
const llmMessages = Array.isArray(payload.messages) && payload.messages.length > 0 ? payload.messages : [{ role: "user", content: payload.completePrompt }];
|
|
540
654
|
const requestBody = {
|
|
541
655
|
model: payload.primaryModel,
|
|
542
|
-
messages:
|
|
543
|
-
temperature: 0.8
|
|
656
|
+
messages: llmMessages
|
|
544
657
|
};
|
|
658
|
+
if (payload.temperature !== void 0 && payload.temperature !== null) {
|
|
659
|
+
requestBody.temperature = payload.temperature;
|
|
660
|
+
} else if (!Array.isArray(payload.messages)) {
|
|
661
|
+
requestBody.temperature = 0.8;
|
|
662
|
+
}
|
|
545
663
|
if (payload.maxTokens === void 0 || payload.maxTokens === null) {
|
|
546
664
|
return requestBody;
|
|
547
665
|
}
|
|
@@ -641,8 +759,17 @@ function createScheduleMessageHandler(ctx) {
|
|
|
641
759
|
apiUrl: payload.apiUrl || null,
|
|
642
760
|
apiKey: payload.apiKey || null,
|
|
643
761
|
primaryModel: payload.primaryModel || null,
|
|
762
|
+
// Prompt is one-of: legacy completePrompt (string) OR messages (OpenAI-
|
|
763
|
+
// style array). Validation has already enforced exactly-one-of, so
|
|
764
|
+
// exactly one of these will be non-null when an AI config is provided.
|
|
644
765
|
completePrompt: payload.completePrompt || null,
|
|
766
|
+
messages: Array.isArray(payload.messages) ? payload.messages : null,
|
|
645
767
|
maxTokens: _nullishCoalesce(payload.maxTokens, () => ( null)),
|
|
768
|
+
temperature: _nullishCoalesce(payload.temperature, () => ( null)),
|
|
769
|
+
// 0.6.0+: optional caller-provided regex (string or string[]) used by
|
|
770
|
+
// the message processor to chunk LLM output into individual pushes.
|
|
771
|
+
// null → processor falls back to the default /([。!?!?]+)/ regex.
|
|
772
|
+
splitPattern: _nullishCoalesce(payload.splitPattern, () => ( null)),
|
|
646
773
|
pushSubscription: payload.pushSubscription,
|
|
647
774
|
metadata: payload.metadata || {}
|
|
648
775
|
};
|
|
@@ -954,6 +1081,30 @@ function createUpdateMessageHandler(ctx) {
|
|
|
954
1081
|
if (Object.prototype.hasOwnProperty.call(updates, "maxTokens") && updates.maxTokens !== null && (!Number.isInteger(updates.maxTokens) || updates.maxTokens <= 0)) {
|
|
955
1082
|
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["maxTokens"] } } } };
|
|
956
1083
|
}
|
|
1084
|
+
if (updates.completePrompt && updates.messages !== void 0 && updates.messages !== null) {
|
|
1085
|
+
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "completePrompt \u4E0E messages \u4E0D\u80FD\u540C\u65F6\u66F4\u65B0\uFF08\u4E8C\u9009\u4E00\uFF09", details: { invalidFields: ["completePrompt", "messages"] } } } };
|
|
1086
|
+
}
|
|
1087
|
+
if (updates.messages !== void 0 && updates.messages !== null) {
|
|
1088
|
+
const msgErr = validateLlmMessagesArray(updates.messages);
|
|
1089
|
+
if (msgErr) {
|
|
1090
|
+
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: msgErr, details: { invalidFields: ["messages"] } } } };
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
if (Object.prototype.hasOwnProperty.call(updates, "temperature") && updates.temperature !== null && (typeof updates.temperature !== "number" || !Number.isFinite(updates.temperature))) {
|
|
1094
|
+
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: "\u66F4\u65B0\u6570\u636E\u683C\u5F0F\u9519\u8BEF", details: { invalidFields: ["temperature"] } } } };
|
|
1095
|
+
}
|
|
1096
|
+
if (Object.prototype.hasOwnProperty.call(updates, "splitPattern")) {
|
|
1097
|
+
const splitErr = validateSplitPattern(updates.splitPattern);
|
|
1098
|
+
if (splitErr) {
|
|
1099
|
+
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: splitErr, details: { invalidFields: ["splitPattern"] } } } };
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
if (Object.prototype.hasOwnProperty.call(updates, "avatarUrl")) {
|
|
1103
|
+
const avatarErr = validateAvatarUrl(updates.avatarUrl);
|
|
1104
|
+
if (avatarErr) {
|
|
1105
|
+
return { status: 400, body: { success: false, error: { code: "INVALID_UPDATE_DATA", message: avatarErr, details: { invalidFields: ["avatarUrl"] } } } };
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
957
1108
|
const existingTask = await db.getTaskByUuid(taskUuid, userId);
|
|
958
1109
|
if (!existingTask) {
|
|
959
1110
|
const taskStatus = await db.getTaskStatus(taskUuid, userId);
|
|
@@ -963,14 +1114,27 @@ function createUpdateMessageHandler(ctx) {
|
|
|
963
1114
|
return { status: 409, body: { success: false, error: { code: "TASK_ALREADY_COMPLETED", message: "\u4EFB\u52A1\u5DF2\u5B8C\u6210\u6216\u5DF2\u5931\u8D25\uFF0C\u65E0\u6CD5\u66F4\u65B0" } } };
|
|
964
1115
|
}
|
|
965
1116
|
const existingData = JSON.parse(decryptFromStorage(existingTask.encrypted_payload, userKey));
|
|
1117
|
+
const promptUpdates = {};
|
|
1118
|
+
if (updates.completePrompt) {
|
|
1119
|
+
promptUpdates.completePrompt = updates.completePrompt;
|
|
1120
|
+
promptUpdates.messages = null;
|
|
1121
|
+
} else if (updates.messages !== void 0 && updates.messages !== null) {
|
|
1122
|
+
promptUpdates.messages = updates.messages;
|
|
1123
|
+
promptUpdates.completePrompt = null;
|
|
1124
|
+
}
|
|
966
1125
|
const updatedData = {
|
|
967
1126
|
...existingData,
|
|
968
|
-
...
|
|
1127
|
+
...promptUpdates,
|
|
969
1128
|
...updates.userMessage && { userMessage: updates.userMessage },
|
|
970
1129
|
...updates.recurrenceType && { recurrenceType: updates.recurrenceType },
|
|
971
1130
|
...updates.avatarUrl && { avatarUrl: updates.avatarUrl },
|
|
972
1131
|
...updates.metadata && { metadata: updates.metadata },
|
|
973
|
-
...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: _nullishCoalesce(updates.maxTokens, () => ( null)) }
|
|
1132
|
+
...Object.prototype.hasOwnProperty.call(updates, "maxTokens") && { maxTokens: _nullishCoalesce(updates.maxTokens, () => ( null)) },
|
|
1133
|
+
...Object.prototype.hasOwnProperty.call(updates, "temperature") && { temperature: _nullishCoalesce(updates.temperature, () => ( null)) },
|
|
1134
|
+
// splitPattern: hasOwnProperty so that explicit `null` (= revert to
|
|
1135
|
+
// default) doesn't get swallowed by truthy-spread the way the optional
|
|
1136
|
+
// string fields above are.
|
|
1137
|
+
...Object.prototype.hasOwnProperty.call(updates, "splitPattern") && { splitPattern: _nullishCoalesce(updates.splitPattern, () => ( null)) }
|
|
974
1138
|
};
|
|
975
1139
|
const encryptedPayload = encryptForStorage(JSON.stringify(updatedData), userKey);
|
|
976
1140
|
const extraFields = updates.nextSendAt ? { next_send_at: updates.nextSendAt } : void 0;
|
|
@@ -1162,7 +1326,7 @@ async function resolveBlobStore(namespace) {
|
|
|
1162
1326
|
}
|
|
1163
1327
|
try {
|
|
1164
1328
|
const blobModule = await Promise.resolve().then(() => _interopRequireWildcard(require("@netlify/blobs")));
|
|
1165
|
-
const getStore = blobModule.getStore || _optionalChain([blobModule, 'access',
|
|
1329
|
+
const getStore = blobModule.getStore || _optionalChain([blobModule, 'access', _6 => _6.default, 'optionalAccess', _7 => _7.getStore]);
|
|
1166
1330
|
if (typeof getStore === "function") {
|
|
1167
1331
|
return getStore({ name: namespace });
|
|
1168
1332
|
}
|
|
@@ -1493,4 +1657,7 @@ async function createReiServer(config) {
|
|
|
1493
1657
|
|
|
1494
1658
|
|
|
1495
1659
|
|
|
1496
|
-
|
|
1660
|
+
|
|
1661
|
+
|
|
1662
|
+
|
|
1663
|
+
exports.createAdapter = createAdapter; exports.createReiServer = createReiServer; exports.createTenantToken = createTenantToken; exports.decryptFromStorage = decryptFromStorage; exports.decryptPayload = decryptPayload; exports.deriveUserEncryptionKey = deriveUserEncryptionKey; exports.encryptForStorage = encryptForStorage; exports.isValidISO8601 = isValidISO8601; exports.isValidUUID = isValidUUID; exports.isValidUUIDv4 = isValidUUIDv4; exports.isValidUrl = isValidUrl; exports.validateAvatarUrl = validateAvatarUrl; exports.validateLlmMessagesArray = validateLlmMessagesArray; exports.validateScheduleMessagePayload = validateScheduleMessagePayload; exports.validateSplitPattern = validateSplitPattern; exports.verifyTenantToken = verifyTenantToken;
|