@musnows/scriverse 0.9.2 → 0.9.3
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/dist/ai.js +34 -11
- package/dist/ai.js.map +1 -1
- package/dist/app.js +31 -8
- package/dist/app.js.map +1 -1
- package/dist/public/ai-stream-protocol.js +9 -0
- package/dist/public/index.html +2 -2
- package/dist/relationship-search.js +30 -1
- package/dist/relationship-search.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -835,6 +835,7 @@ const characterExtractionApplySchema = z.object({
|
|
|
835
835
|
}
|
|
836
836
|
});
|
|
837
837
|
export const RUNTIME_BACKUP_IDLE_TIMEOUT_MS = 9_000;
|
|
838
|
+
export const AI_CLIENT_STREAM_HEARTBEAT_INTERVAL_MS = 15_000;
|
|
838
839
|
function data(response, value, status = 200) {
|
|
839
840
|
response.status(status).json({ data: value });
|
|
840
841
|
}
|
|
@@ -1183,6 +1184,10 @@ function redactVersionSnapshots(value, mapper) {
|
|
|
1183
1184
|
export function createRuntime(options) {
|
|
1184
1185
|
const uploadLimits = options.uploadLimits ?? DEFAULT_IMAGE_UPLOAD_LIMITS;
|
|
1185
1186
|
const aiChatTabLimit = options.aiChatTabLimit ?? DEFAULT_AI_CHAT_TAB_LIMIT;
|
|
1187
|
+
const requestedAiStreamHeartbeatIntervalMs = Number(options.aiStreamHeartbeatIntervalMs);
|
|
1188
|
+
const aiStreamHeartbeatIntervalMs = Number.isFinite(requestedAiStreamHeartbeatIntervalMs)
|
|
1189
|
+
? Math.max(1, Math.trunc(requestedAiStreamHeartbeatIntervalMs))
|
|
1190
|
+
: AI_CLIENT_STREAM_HEARTBEAT_INTERVAL_MS;
|
|
1186
1191
|
logger.info("runtime.initializing", {
|
|
1187
1192
|
databasePath: options.databasePath,
|
|
1188
1193
|
serveUi: options.serveUi ?? true,
|
|
@@ -3200,16 +3205,30 @@ export function createRuntime(options) {
|
|
|
3200
3205
|
const modelId = resolveConversationModelId(request.params.workId, conversationId, input.modelId);
|
|
3201
3206
|
const permissions = requestPermissions(request, request.params.workId);
|
|
3202
3207
|
const controller = new AbortController();
|
|
3203
|
-
response.on("close", () => {
|
|
3204
|
-
if (!response.writableEnded)
|
|
3205
|
-
controller.abort(new Error("浏览器已中断流式请求"));
|
|
3206
|
-
});
|
|
3207
3208
|
let streamRequestId = null;
|
|
3208
3209
|
let streamRequestFinished = false;
|
|
3209
3210
|
let lastStreamLeaseTouchAt = Date.now();
|
|
3211
|
+
let streamHeartbeatTimer = null;
|
|
3210
3212
|
let preparedContext = null;
|
|
3211
3213
|
let preparedConversation = null;
|
|
3212
3214
|
let preparedChatImageAttachments = [];
|
|
3215
|
+
const touchStreamLease = () => {
|
|
3216
|
+
if (streamRequestId && Date.now() - lastStreamLeaseTouchAt >= 30_000) {
|
|
3217
|
+
store.touchAiConversationStreamRequest(streamRequestId);
|
|
3218
|
+
lastStreamLeaseTouchAt = Date.now();
|
|
3219
|
+
}
|
|
3220
|
+
};
|
|
3221
|
+
const stopStreamHeartbeat = () => {
|
|
3222
|
+
if (!streamHeartbeatTimer)
|
|
3223
|
+
return;
|
|
3224
|
+
clearInterval(streamHeartbeatTimer);
|
|
3225
|
+
streamHeartbeatTimer = null;
|
|
3226
|
+
};
|
|
3227
|
+
response.on("close", () => {
|
|
3228
|
+
stopStreamHeartbeat();
|
|
3229
|
+
if (!response.writableEnded)
|
|
3230
|
+
controller.abort(new Error("浏览器已中断流式请求"));
|
|
3231
|
+
});
|
|
3213
3232
|
const startStream = () => {
|
|
3214
3233
|
if (response.headersSent)
|
|
3215
3234
|
return;
|
|
@@ -3219,12 +3238,15 @@ export function createRuntime(options) {
|
|
|
3219
3238
|
response.setHeader("Connection", "keep-alive");
|
|
3220
3239
|
response.setHeader("X-Accel-Buffering", "no");
|
|
3221
3240
|
response.flushHeaders();
|
|
3241
|
+
streamHeartbeatTimer = setInterval(() => {
|
|
3242
|
+
touchStreamLease();
|
|
3243
|
+
if (!response.writableEnded && !response.destroyed)
|
|
3244
|
+
response.write(": heartbeat\n\n");
|
|
3245
|
+
}, aiStreamHeartbeatIntervalMs);
|
|
3246
|
+
streamHeartbeatTimer.unref();
|
|
3222
3247
|
};
|
|
3223
3248
|
const sendEvent = (event, payload) => {
|
|
3224
|
-
|
|
3225
|
-
store.touchAiConversationStreamRequest(streamRequestId);
|
|
3226
|
-
lastStreamLeaseTouchAt = Date.now();
|
|
3227
|
-
}
|
|
3249
|
+
touchStreamLease();
|
|
3228
3250
|
if (!response.writableEnded && !response.destroyed)
|
|
3229
3251
|
response.write(`event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`);
|
|
3230
3252
|
};
|
|
@@ -3402,6 +3424,7 @@ export function createRuntime(options) {
|
|
|
3402
3424
|
}
|
|
3403
3425
|
}
|
|
3404
3426
|
finally {
|
|
3427
|
+
stopStreamHeartbeat();
|
|
3405
3428
|
if (streamRequestId && !streamRequestFinished && !stopping) {
|
|
3406
3429
|
store.finishAiConversationStreamRequest(streamRequestId, "cancelled", "stream_closed");
|
|
3407
3430
|
streamRequestFinished = true;
|