@musnows/scriverse 1.0.2 → 1.0.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/dist/ai.js +21 -4
- package/dist/ai.js.map +1 -1
- package/dist/app.js +92 -11
- package/dist/app.js.map +1 -1
- package/dist/public/ai-interactive.js +59 -6
- package/dist/public/ai-request-manager.js +10 -2
- package/dist/public/app.js +178 -61
- package/dist/public/index.html +2 -2
- package/dist/public/stream-typewriter.d.ts +1 -0
- package/dist/public/stream-typewriter.js +22 -3
- package/dist/public/styles.css +3 -0
- package/dist/public/text-count.d.ts +1 -0
- package/dist/public/text-count.js +7 -0
- package/dist/store.js.map +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -1056,7 +1056,7 @@ function redactAiCallContext(record, permissions) {
|
|
|
1056
1056
|
const redactedScope = { ...scope };
|
|
1057
1057
|
let restricted = false;
|
|
1058
1058
|
if (permissions.prose === "none") {
|
|
1059
|
-
for (const field of ["selection", "selectionStart", "selectionEnd", "writingChapterVersion", "chapterId", "volumeId", "chapterIds", "includeBookSummary"]) {
|
|
1059
|
+
for (const field of ["selection", "selectionStart", "selectionEnd", "writingChapterVersion", "chapterId", "volumeId", "chapterIds", "volumeIds", "includeBookSummary"]) {
|
|
1060
1060
|
if (field in redactedScope) {
|
|
1061
1061
|
delete redactedScope[field];
|
|
1062
1062
|
restricted = true;
|
|
@@ -1126,6 +1126,10 @@ function redactAiConversationMessage(item, permissions) {
|
|
|
1126
1126
|
delete readableMetadata.mentionRaceIds;
|
|
1127
1127
|
if (permissions.organizations === "none")
|
|
1128
1128
|
delete readableMetadata.mentionOrganizationIds;
|
|
1129
|
+
if (permissions.settings === "none")
|
|
1130
|
+
delete readableMetadata.mentionSettingIds;
|
|
1131
|
+
if (permissions.settings === "none")
|
|
1132
|
+
delete readableMetadata.mentionContextSettingIds;
|
|
1129
1133
|
if (Object.keys(readableMetadata).length === Object.keys(metadata).length)
|
|
1130
1134
|
return item;
|
|
1131
1135
|
return { ...message, metadata: readableMetadata };
|
|
@@ -1207,11 +1211,14 @@ export function publicAiStreamError(error) {
|
|
|
1207
1211
|
"monthStartedAt"
|
|
1208
1212
|
].filter((key) => details[key] !== undefined).map((key) => [key, details[key]]))
|
|
1209
1213
|
: undefined;
|
|
1214
|
+
const publicPendingQuestionDetails = error.code === "AI_QUESTION_PENDING" && typeof details?.questionId === "string"
|
|
1215
|
+
? { questionId: details.questionId }
|
|
1216
|
+
: undefined;
|
|
1210
1217
|
return {
|
|
1211
1218
|
code: error.code,
|
|
1212
1219
|
message: error.message,
|
|
1213
1220
|
status: error.status,
|
|
1214
|
-
...(publicQuotaDetails ? { details: publicQuotaDetails } : {}),
|
|
1221
|
+
...(publicQuotaDetails || publicPendingQuestionDetails ? { details: publicQuotaDetails ?? publicPendingQuestionDetails } : {}),
|
|
1215
1222
|
...((error.status < 500 || error.code === "AI_CALL_FAILED") && typeof details?.failure === "string" ? { failure: details.failure } : {}),
|
|
1216
1223
|
...(typeof details?.callId === "string" ? { callId: details.callId } : {}),
|
|
1217
1224
|
...(typeof details?.providerName === "string" ? { providerName: details.providerName } : {}),
|
|
@@ -3434,6 +3441,9 @@ export function createRuntime(options) {
|
|
|
3434
3441
|
interrupted: z.boolean().optional(),
|
|
3435
3442
|
interruptionCode: z.string().max(100).optional(),
|
|
3436
3443
|
interruptionMessage: z.string().max(500).optional(),
|
|
3444
|
+
errorCode: z.string().max(100).optional(),
|
|
3445
|
+
errorStatus: z.number().int().min(100).max(599).optional(),
|
|
3446
|
+
pendingQuestionId: identifier.optional(),
|
|
3437
3447
|
toolCalls: z.array(aiToolCallResultSchema).max(12).optional(),
|
|
3438
3448
|
processSteps: z.array(aiProcessStepSchema).max(50).optional()
|
|
3439
3449
|
}).optional()
|
|
@@ -3568,7 +3578,7 @@ export function createRuntime(options) {
|
|
|
3568
3578
|
app.get("/api/works/:workId/ai/questions/:questionId", (request, response) => {
|
|
3569
3579
|
data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, planViewer()));
|
|
3570
3580
|
});
|
|
3571
|
-
const resumeQuestionWorkflow = async (questionId, workId, viewer) => {
|
|
3581
|
+
const resumeQuestionWorkflow = async (questionId, workId, viewer, stream = {}) => {
|
|
3572
3582
|
const continuation = aiWritePlanManager.claimQuestionContinuation(questionId, workId, viewer);
|
|
3573
3583
|
if (!continuation)
|
|
3574
3584
|
return;
|
|
@@ -3599,26 +3609,88 @@ export function createRuntime(options) {
|
|
|
3599
3609
|
: {}),
|
|
3600
3610
|
...(typeof continuation.round === "number" ? { round: continuation.round } : {}),
|
|
3601
3611
|
...(Array.isArray(continuation.toolMessages) ? { toolMessages: continuation.toolMessages } : {})
|
|
3602
|
-
});
|
|
3612
|
+
}, stream);
|
|
3603
3613
|
aiWritePlanManager.finishQuestionContinuation(questionId, { callId: resumed.callId ?? null, completed: true });
|
|
3614
|
+
return resumed;
|
|
3604
3615
|
}
|
|
3605
3616
|
catch (error) {
|
|
3606
3617
|
aiWritePlanManager.finishQuestionContinuation(questionId, { message: error instanceof Error ? error.message : "恢复失败" }, true);
|
|
3607
3618
|
throw error;
|
|
3608
3619
|
}
|
|
3609
3620
|
};
|
|
3621
|
+
const respondWithQuestionContinuation = async (request, response, viewer) => {
|
|
3622
|
+
const { questionId, workId } = request.params;
|
|
3623
|
+
if (!request.get("Accept")?.includes("text/event-stream")) {
|
|
3624
|
+
await resumeQuestionWorkflow(questionId, workId, viewer);
|
|
3625
|
+
data(response, aiWritePlanManager.getQuestion(questionId, workId, viewer));
|
|
3626
|
+
return;
|
|
3627
|
+
}
|
|
3628
|
+
const controller = new AbortController();
|
|
3629
|
+
response.status(200);
|
|
3630
|
+
response.setHeader("Content-Type", "text/event-stream; charset=utf-8");
|
|
3631
|
+
response.setHeader("Cache-Control", "no-cache, no-transform");
|
|
3632
|
+
response.setHeader("Connection", "keep-alive");
|
|
3633
|
+
response.setHeader("X-Accel-Buffering", "no");
|
|
3634
|
+
response.flushHeaders();
|
|
3635
|
+
const sendEvent = (event, payload) => {
|
|
3636
|
+
if (!response.writableEnded && !response.destroyed)
|
|
3637
|
+
response.write(`event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`);
|
|
3638
|
+
};
|
|
3639
|
+
const heartbeat = setInterval(() => {
|
|
3640
|
+
if (!response.writableEnded && !response.destroyed)
|
|
3641
|
+
response.write(": heartbeat\n\n");
|
|
3642
|
+
}, aiStreamHeartbeatIntervalMs);
|
|
3643
|
+
heartbeat.unref();
|
|
3644
|
+
response.on("close", () => {
|
|
3645
|
+
clearInterval(heartbeat);
|
|
3646
|
+
if (!response.writableEnded)
|
|
3647
|
+
controller.abort(new Error("浏览器已中断流式请求"));
|
|
3648
|
+
});
|
|
3649
|
+
try {
|
|
3650
|
+
const resumed = await resumeQuestionWorkflow(questionId, workId, viewer, {
|
|
3651
|
+
signal: controller.signal,
|
|
3652
|
+
onStart: (message) => sendEvent("continuation", message),
|
|
3653
|
+
onDelta: (delta) => sendEvent("delta", { delta }),
|
|
3654
|
+
onToolCall: (toolCall, round) => sendEvent("tool_call", { ...toolCall, round }),
|
|
3655
|
+
onProcessStep: (step) => sendEvent("process_step", step),
|
|
3656
|
+
onContextCompacted: (event) => sendEvent("context_compacted", event)
|
|
3657
|
+
});
|
|
3658
|
+
const message = resumed?.conversationMessage;
|
|
3659
|
+
const metadata = message?.metadata;
|
|
3660
|
+
sendEvent("complete", {
|
|
3661
|
+
warningOnly: !resumed,
|
|
3662
|
+
model: resumed?.model,
|
|
3663
|
+
outputTokens: metadata?.outputTokens ?? resumed?.outputTokens,
|
|
3664
|
+
cacheHitPercent: metadata?.cacheHitPercent ?? resumed?.cacheHitPercent,
|
|
3665
|
+
processDurationMs: metadata?.processDurationMs ?? resumed?.processDurationMs,
|
|
3666
|
+
toolCalls: metadata?.toolCalls ?? resumed?.toolCalls,
|
|
3667
|
+
processSteps: metadata?.processSteps ?? resumed?.processSteps,
|
|
3668
|
+
contextUsage: resumed?.contextUsage,
|
|
3669
|
+
messageId: message?.id,
|
|
3670
|
+
messageCreatedAt: message?.createdAt,
|
|
3671
|
+
question: aiWritePlanManager.getQuestion(questionId, workId, viewer)
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
catch (error) {
|
|
3675
|
+
if (!controller.signal.aborted)
|
|
3676
|
+
sendEvent("error", publicAiStreamError(error));
|
|
3677
|
+
}
|
|
3678
|
+
finally {
|
|
3679
|
+
clearInterval(heartbeat);
|
|
3680
|
+
if (!response.writableEnded && !response.destroyed)
|
|
3681
|
+
response.end();
|
|
3682
|
+
}
|
|
3683
|
+
};
|
|
3610
3684
|
app.post("/api/works/:workId/ai/questions/:questionId/answer", async (request, response) => {
|
|
3611
3685
|
const input = parse(answerAiUserQuestionSchema, request.body ?? {});
|
|
3612
3686
|
const viewer = planViewer();
|
|
3613
3687
|
aiWritePlanManager.answerQuestion(request.params.questionId, request.params.workId, viewer, input);
|
|
3614
|
-
await
|
|
3615
|
-
data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, viewer));
|
|
3688
|
+
await respondWithQuestionContinuation(request, response, viewer);
|
|
3616
3689
|
});
|
|
3617
3690
|
app.post("/api/works/:workId/ai/questions/:questionId/reject", async (request, response) => {
|
|
3618
3691
|
const viewer = planViewer();
|
|
3619
3692
|
aiWritePlanManager.rejectQuestion(request.params.questionId, request.params.workId, viewer);
|
|
3620
|
-
await
|
|
3621
|
-
data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, viewer));
|
|
3693
|
+
await respondWithQuestionContinuation(request, response, viewer);
|
|
3622
3694
|
});
|
|
3623
3695
|
app.get("/api/works/:workId/providers", (request, response) => {
|
|
3624
3696
|
store.getWork(request.params.workId);
|
|
@@ -3923,6 +3995,9 @@ export function createRuntime(options) {
|
|
|
3923
3995
|
])];
|
|
3924
3996
|
const mentionRaceIds = [...new Set(resolvedScope.raceIds ?? [])];
|
|
3925
3997
|
const mentionOrganizationIds = [...new Set(resolvedScope.organizationIds ?? [])];
|
|
3998
|
+
const mentionSettingIds = [...new Set(resolvedScope.settingIds ?? [])];
|
|
3999
|
+
const mentionChapterIds = [...new Set(resolvedScope.chapterIds ?? [])];
|
|
4000
|
+
const mentionContextSettingIds = resolvedScope.includeSettingInfo === true ? ["include-setting-info"] : [];
|
|
3926
4001
|
const begun = store.beginAiConversationStreamRequest({
|
|
3927
4002
|
workId: request.params.workId,
|
|
3928
4003
|
conversationId,
|
|
@@ -3933,11 +4008,14 @@ export function createRuntime(options) {
|
|
|
3933
4008
|
content: storedUserContent,
|
|
3934
4009
|
citations,
|
|
3935
4010
|
...(input.currentMessageId ? { existingMessageId: input.currentMessageId } : {}),
|
|
3936
|
-
...((modelId || mentionCharacterIds.length || mentionRaceIds.length || mentionOrganizationIds.length || input.imageAttachmentIds?.length || input.scope.semanticSnapshotId) ? { metadata: {
|
|
4011
|
+
...((modelId || mentionCharacterIds.length || mentionRaceIds.length || mentionOrganizationIds.length || mentionSettingIds.length || mentionChapterIds.length || mentionContextSettingIds.length || input.imageAttachmentIds?.length || input.scope.semanticSnapshotId) ? { metadata: {
|
|
3937
4012
|
...(modelId ? { modelId } : {}),
|
|
3938
4013
|
...(mentionCharacterIds.length ? { mentionCharacterIds } : {}),
|
|
3939
4014
|
...(mentionRaceIds.length ? { mentionRaceIds } : {}),
|
|
3940
4015
|
...(mentionOrganizationIds.length ? { mentionOrganizationIds } : {}),
|
|
4016
|
+
...(mentionSettingIds.length ? { mentionSettingIds } : {}),
|
|
4017
|
+
...(mentionChapterIds.length ? { mentionChapterIds } : {}),
|
|
4018
|
+
...(mentionContextSettingIds.length ? { mentionContextSettingIds } : {}),
|
|
3941
4019
|
...(input.scope.semanticSnapshotId ? { semanticSnapshotId: input.scope.semanticSnapshotId } : {}),
|
|
3942
4020
|
...(input.imageAttachmentIds?.length ? { chatImageAttachmentIds: [...new Set(input.imageAttachmentIds)] } : {})
|
|
3943
4021
|
} } : {})
|
|
@@ -3996,8 +4074,11 @@ export function createRuntime(options) {
|
|
|
3996
4074
|
const currentMessageId = String(begun.userMessage?.id ?? input.currentMessageId ?? "");
|
|
3997
4075
|
if (begun.userMessage)
|
|
3998
4076
|
sendEvent("user_message", { message: redactAiConversationMessage(begun.userMessage, permissions) });
|
|
3999
|
-
|
|
4000
|
-
|
|
4077
|
+
const pendingQuestion = aiWritePlanManager.latestPendingQuestion(conversationId);
|
|
4078
|
+
if (pendingQuestion) {
|
|
4079
|
+
throw new AppError(409, "AI_QUESTION_PENDING", "当前对话仍有待回答问题,请先回答或拒绝后再继续", {
|
|
4080
|
+
questionId: pendingQuestion.id
|
|
4081
|
+
});
|
|
4001
4082
|
}
|
|
4002
4083
|
const suggestion = await ai.createStreamingChat({
|
|
4003
4084
|
workId: request.params.workId,
|