@musnows/scriverse 1.0.2 → 1.0.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/app.js CHANGED
@@ -3568,7 +3568,7 @@ export function createRuntime(options) {
3568
3568
  app.get("/api/works/:workId/ai/questions/:questionId", (request, response) => {
3569
3569
  data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, planViewer()));
3570
3570
  });
3571
- const resumeQuestionWorkflow = async (questionId, workId, viewer) => {
3571
+ const resumeQuestionWorkflow = async (questionId, workId, viewer, stream = {}) => {
3572
3572
  const continuation = aiWritePlanManager.claimQuestionContinuation(questionId, workId, viewer);
3573
3573
  if (!continuation)
3574
3574
  return;
@@ -3599,26 +3599,88 @@ export function createRuntime(options) {
3599
3599
  : {}),
3600
3600
  ...(typeof continuation.round === "number" ? { round: continuation.round } : {}),
3601
3601
  ...(Array.isArray(continuation.toolMessages) ? { toolMessages: continuation.toolMessages } : {})
3602
- });
3602
+ }, stream);
3603
3603
  aiWritePlanManager.finishQuestionContinuation(questionId, { callId: resumed.callId ?? null, completed: true });
3604
+ return resumed;
3604
3605
  }
3605
3606
  catch (error) {
3606
3607
  aiWritePlanManager.finishQuestionContinuation(questionId, { message: error instanceof Error ? error.message : "恢复失败" }, true);
3607
3608
  throw error;
3608
3609
  }
3609
3610
  };
3611
+ const respondWithQuestionContinuation = async (request, response, viewer) => {
3612
+ const { questionId, workId } = request.params;
3613
+ if (!request.get("Accept")?.includes("text/event-stream")) {
3614
+ await resumeQuestionWorkflow(questionId, workId, viewer);
3615
+ data(response, aiWritePlanManager.getQuestion(questionId, workId, viewer));
3616
+ return;
3617
+ }
3618
+ const controller = new AbortController();
3619
+ response.status(200);
3620
+ response.setHeader("Content-Type", "text/event-stream; charset=utf-8");
3621
+ response.setHeader("Cache-Control", "no-cache, no-transform");
3622
+ response.setHeader("Connection", "keep-alive");
3623
+ response.setHeader("X-Accel-Buffering", "no");
3624
+ response.flushHeaders();
3625
+ const sendEvent = (event, payload) => {
3626
+ if (!response.writableEnded && !response.destroyed)
3627
+ response.write(`event: ${event}\ndata: ${JSON.stringify(payload)}\n\n`);
3628
+ };
3629
+ const heartbeat = setInterval(() => {
3630
+ if (!response.writableEnded && !response.destroyed)
3631
+ response.write(": heartbeat\n\n");
3632
+ }, aiStreamHeartbeatIntervalMs);
3633
+ heartbeat.unref();
3634
+ response.on("close", () => {
3635
+ clearInterval(heartbeat);
3636
+ if (!response.writableEnded)
3637
+ controller.abort(new Error("浏览器已中断流式请求"));
3638
+ });
3639
+ try {
3640
+ const resumed = await resumeQuestionWorkflow(questionId, workId, viewer, {
3641
+ signal: controller.signal,
3642
+ onStart: (message) => sendEvent("continuation", message),
3643
+ onDelta: (delta) => sendEvent("delta", { delta }),
3644
+ onToolCall: (toolCall, round) => sendEvent("tool_call", { ...toolCall, round }),
3645
+ onProcessStep: (step) => sendEvent("process_step", step),
3646
+ onContextCompacted: (event) => sendEvent("context_compacted", event)
3647
+ });
3648
+ const message = resumed?.conversationMessage;
3649
+ const metadata = message?.metadata;
3650
+ sendEvent("complete", {
3651
+ warningOnly: !resumed,
3652
+ model: resumed?.model,
3653
+ outputTokens: metadata?.outputTokens ?? resumed?.outputTokens,
3654
+ cacheHitPercent: metadata?.cacheHitPercent ?? resumed?.cacheHitPercent,
3655
+ processDurationMs: metadata?.processDurationMs ?? resumed?.processDurationMs,
3656
+ toolCalls: metadata?.toolCalls ?? resumed?.toolCalls,
3657
+ processSteps: metadata?.processSteps ?? resumed?.processSteps,
3658
+ contextUsage: resumed?.contextUsage,
3659
+ messageId: message?.id,
3660
+ messageCreatedAt: message?.createdAt,
3661
+ question: aiWritePlanManager.getQuestion(questionId, workId, viewer)
3662
+ });
3663
+ }
3664
+ catch (error) {
3665
+ if (!controller.signal.aborted)
3666
+ sendEvent("error", publicAiStreamError(error));
3667
+ }
3668
+ finally {
3669
+ clearInterval(heartbeat);
3670
+ if (!response.writableEnded && !response.destroyed)
3671
+ response.end();
3672
+ }
3673
+ };
3610
3674
  app.post("/api/works/:workId/ai/questions/:questionId/answer", async (request, response) => {
3611
3675
  const input = parse(answerAiUserQuestionSchema, request.body ?? {});
3612
3676
  const viewer = planViewer();
3613
3677
  aiWritePlanManager.answerQuestion(request.params.questionId, request.params.workId, viewer, input);
3614
- await resumeQuestionWorkflow(request.params.questionId, request.params.workId, viewer);
3615
- data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, viewer));
3678
+ await respondWithQuestionContinuation(request, response, viewer);
3616
3679
  });
3617
3680
  app.post("/api/works/:workId/ai/questions/:questionId/reject", async (request, response) => {
3618
3681
  const viewer = planViewer();
3619
3682
  aiWritePlanManager.rejectQuestion(request.params.questionId, request.params.workId, viewer);
3620
- await resumeQuestionWorkflow(request.params.questionId, request.params.workId, viewer);
3621
- data(response, aiWritePlanManager.getQuestion(request.params.questionId, request.params.workId, viewer));
3683
+ await respondWithQuestionContinuation(request, response, viewer);
3622
3684
  });
3623
3685
  app.get("/api/works/:workId/providers", (request, response) => {
3624
3686
  store.getWork(request.params.workId);