Ver Fonte

refactor(interaction): split draft and book selection handlers

Ma há 7 meses atrás
pai
commit
5410b95452
1 ficheiros alterados com 167 adições e 99 exclusões
  1. 167 99
      packages/core/src/interaction/runtime.ts

+ 167 - 99
packages/core/src/interaction/runtime.ts

@@ -337,57 +337,29 @@ function appendToolEvents(
   }), session);
 }
 
-export async function runInteractionRequest(params: {
-  readonly session: InteractionSession;
-  readonly request: InteractionRequest;
-  readonly tools: InteractionRuntimeTools;
-}): Promise<InteractionRuntimeResult> {
-  const request = routeInteractionRequest(params.request);
-  const language = resolveRuntimeLanguage(request);
-  let session = params.session;
-  const addEvent = (
+interface RuntimeRequestHelpers {
+  readonly language: RuntimeLanguage;
+  readonly addEvent: (
     nextSession: InteractionSession,
     kind: string,
     status: InteractionEvent["status"],
     detail: string,
-  ): InteractionSession => appendInteractionEvent(nextSession, {
-    kind,
-    timestamp: Date.now(),
-    status,
-    bookId: nextSession.activeBookId,
-    chapterNumber: nextSession.activeChapterNumber,
-    detail,
-  });
-
-  if (request.mode) {
-    session = updateAutomationMode(session, request.mode as AutomationMode);
-  }
-
-  session = clearPendingDecision({
-    ...session,
-    currentExecution: buildTaskStartedState(session, request, language),
-  });
-  session = addEvent(session, "task.started", session.currentExecution!.status, localize(language, {
-    zh: `开始执行 ${request.intent}。`,
-    en: `Started ${request.intent}.`,
-  }));
+  ) => InteractionSession;
+  readonly markCompleted: (nextSession: InteractionSession) => InteractionSession;
+}
 
-  const markCompleted = (nextSession: InteractionSession): InteractionSession => ({
-    ...nextSession,
-    currentExecution: {
-      status: "completed",
-      bookId: nextSession.activeBookId,
-      chapterNumber: nextSession.activeChapterNumber,
-      stageLabel: localize(language, {
-        zh: "已完成",
-        en: "completed",
-      }),
-    },
-  });
+async function handleDraftLifecycleRequest(params: {
+  readonly session: InteractionSession;
+  readonly request: InteractionRequest;
+  readonly tools: InteractionRuntimeTools;
+  readonly helpers: RuntimeRequestHelpers;
+}): Promise<InteractionRuntimeResult | undefined> {
+  const { session, request, tools, helpers } = params;
+  const { language, addEvent, markCompleted } = helpers;
 
   switch (request.intent) {
     case "develop_book": {
-      if (!params.tools.developBookDraft) {
+      if (!tools.developBookDraft) {
         throw new Error(localize(language, {
           zh: "创作草案会话暂未实现。",
           en: "Book-draft ideation is not implemented yet.",
@@ -399,7 +371,7 @@ export async function runInteractionRequest(params: {
           en: "Book-draft ideation requires user input.",
         }));
       }
-      const toolResult = await params.tools.developBookDraft(request.instruction, session.creationDraft);
+      const toolResult = await tools.developBookDraft(request.instruction, session.creationDraft);
       const metadata = extractToolMetadata(toolResult);
       const draft = metadata.details?.creationDraft as InteractionSession["creationDraft"] | undefined;
       if (!draft) {
@@ -408,11 +380,10 @@ export async function runInteractionRequest(params: {
           en: "Book-draft tool did not return draft data.",
         }));
       }
-      session = updateCreationDraft(session, draft);
-      session = appendToolEvents(session, metadata.events);
+      const nextSession = appendToolEvents(updateCreationDraft(session, draft), metadata.events);
       const completed = {
-        ...markCompleted(session),
-        currentExecution: metadata.currentExecution ?? markCompleted(session).currentExecution,
+        ...markCompleted(nextSession),
+        currentExecution: metadata.currentExecution ?? markCompleted(nextSession).currentExecution,
       };
       return {
         session: addEvent(completed, "task.completed", "completed", localize(language, {
@@ -442,7 +413,7 @@ export async function runInteractionRequest(params: {
       };
     }
     case "create_book": {
-      if (!params.tools.createBook) {
+      if (!tools.createBook) {
         throw new Error(localize(language, {
           zh: "交互运行时暂未实现创建作品。",
           en: "Book creation is not implemented in the interaction runtime yet.",
@@ -456,7 +427,7 @@ export async function runInteractionRequest(params: {
           en: "Book creation requires a title.",
         }));
       }
-      const toolResult = await params.tools.createBook({
+      const toolResult = await tools.createBook({
         title,
         genre: request.genre ?? effectiveDraft?.genre,
         platform: request.platform ?? effectiveDraft?.platform,
@@ -485,11 +456,13 @@ export async function runInteractionRequest(params: {
           en: "Create-book tool did not return a book id.",
         }));
       }
-      session = clearCreationDraft(bindActiveBook(session, createdBookId));
-      session = appendToolEvents(session, metadata.events);
+      const nextSession = appendToolEvents(
+        clearCreationDraft(bindActiveBook(session, createdBookId)),
+        metadata.events,
+      );
       const completed = {
-        ...markCompleted(session),
-        currentExecution: metadata.currentExecution ?? markCompleted(session).currentExecution,
+        ...markCompleted(nextSession),
+        currentExecution: metadata.currentExecution ?? markCompleted(nextSession).currentExecution,
       };
       return {
         session: addEvent(completed, "task.completed", "completed", localize(language, {
@@ -516,6 +489,146 @@ export async function runInteractionRequest(params: {
         }),
       };
     }
+    default:
+      return undefined;
+  }
+}
+
+async function handleBookSelectionRequest(params: {
+  readonly session: InteractionSession;
+  readonly request: InteractionRequest;
+  readonly tools: InteractionRuntimeTools;
+  readonly helpers: RuntimeRequestHelpers;
+}): Promise<InteractionRuntimeResult | undefined> {
+  const { session, request, tools, helpers } = params;
+  const { language, addEvent, markCompleted } = helpers;
+
+  switch (request.intent) {
+    case "list_books": {
+      const books = await tools.listBooks();
+      const completed = markCompleted(session);
+      return {
+        session: addEvent(completed, "task.completed", "completed", localize(language, {
+          zh: `已列出 ${books.length} 本作品。`,
+          en: `Listed ${books.length} book(s).`,
+        })),
+        responseText: books.length > 0
+          ? localize(language, {
+              zh: `作品列表:${books.join("、")}`,
+              en: `Books: ${books.join(", ")}`,
+            })
+          : localize(language, {
+              zh: "当前项目下没有作品。",
+              en: "No books found in this project.",
+            }),
+      };
+    }
+    case "select_book": {
+      if (!request.bookId) {
+        throw new Error(localize(language, {
+          zh: "切换作品需要提供作品 ID。",
+          en: "Book selection requires a book id.",
+        }));
+      }
+      const books = await tools.listBooks();
+      if (!books.includes(request.bookId)) {
+        throw new Error(localize(language, {
+          zh: `当前项目中找不到作品「${request.bookId}」。`,
+          en: `Book "${request.bookId}" not found in this project.`,
+        }));
+      }
+      const completed = markCompleted(bindActiveBook(session, request.bookId));
+      return {
+        session: addEvent(completed, "task.completed", "completed", localize(language, {
+          zh: `已切换当前作品到 ${request.bookId}。`,
+          en: `Bound active book to ${request.bookId}.`,
+        })),
+        responseText: localize(language, {
+          zh: `当前作品:${request.bookId}`,
+          en: `Active book: ${request.bookId}`,
+        }),
+      };
+    }
+    default:
+      return undefined;
+  }
+}
+
+export async function runInteractionRequest(params: {
+  readonly session: InteractionSession;
+  readonly request: InteractionRequest;
+  readonly tools: InteractionRuntimeTools;
+}): Promise<InteractionRuntimeResult> {
+  const request = routeInteractionRequest(params.request);
+  const language = resolveRuntimeLanguage(request);
+  let session = params.session;
+  const addEvent = (
+    nextSession: InteractionSession,
+    kind: string,
+    status: InteractionEvent["status"],
+    detail: string,
+  ): InteractionSession => appendInteractionEvent(nextSession, {
+    kind,
+    timestamp: Date.now(),
+    status,
+    bookId: nextSession.activeBookId,
+    chapterNumber: nextSession.activeChapterNumber,
+    detail,
+  });
+
+  if (request.mode) {
+    session = updateAutomationMode(session, request.mode as AutomationMode);
+  }
+
+  session = clearPendingDecision({
+    ...session,
+    currentExecution: buildTaskStartedState(session, request, language),
+  });
+  session = addEvent(session, "task.started", session.currentExecution!.status, localize(language, {
+    zh: `开始执行 ${request.intent}。`,
+    en: `Started ${request.intent}.`,
+  }));
+
+  const markCompleted = (nextSession: InteractionSession): InteractionSession => ({
+    ...nextSession,
+    currentExecution: {
+      status: "completed",
+      bookId: nextSession.activeBookId,
+      chapterNumber: nextSession.activeChapterNumber,
+      stageLabel: localize(language, {
+        zh: "已完成",
+        en: "completed",
+      }),
+    },
+  });
+
+  const helperContext: RuntimeRequestHelpers = {
+    language,
+    addEvent,
+    markCompleted,
+  };
+
+  const draftLifecycleResult = await handleDraftLifecycleRequest({
+    session,
+    request,
+    tools: params.tools,
+    helpers: helperContext,
+  });
+  if (draftLifecycleResult) {
+    return draftLifecycleResult;
+  }
+
+  const bookSelectionResult = await handleBookSelectionRequest({
+    session,
+    request,
+    tools: params.tools,
+    helpers: helperContext,
+  });
+  if (bookSelectionResult) {
+    return bookSelectionResult;
+  }
+
+  switch (request.intent) {
     case "write_next":
     case "continue_book": {
       const bookId = request.bookId ?? session.activeBookId;
@@ -563,51 +676,6 @@ export async function runInteractionRequest(params: {
         ),
       };
     }
-    case "list_books": {
-      const books = await params.tools.listBooks();
-      const completed = markCompleted(session);
-      return {
-        session: addEvent(completed, "task.completed", "completed", localize(language, {
-          zh: `已列出 ${books.length} 本作品。`,
-          en: `Listed ${books.length} book(s).`,
-        })),
-        responseText: books.length > 0
-          ? localize(language, {
-              zh: `作品列表:${books.join("、")}`,
-              en: `Books: ${books.join(", ")}`,
-            })
-          : localize(language, {
-              zh: "当前项目下没有作品。",
-              en: "No books found in this project.",
-            }),
-      };
-    }
-    case "select_book": {
-      if (!request.bookId) {
-        throw new Error(localize(language, {
-          zh: "切换作品需要提供作品 ID。",
-          en: "Book selection requires a book id.",
-        }));
-      }
-      const books = await params.tools.listBooks();
-      if (!books.includes(request.bookId)) {
-        throw new Error(localize(language, {
-          zh: `当前项目中找不到作品「${request.bookId}」。`,
-          en: `Book "${request.bookId}" not found in this project.`,
-        }));
-      }
-      const completed = markCompleted(bindActiveBook(session, request.bookId));
-      return {
-        session: addEvent(completed, "task.completed", "completed", localize(language, {
-          zh: `已切换当前作品到 ${request.bookId}。`,
-          en: `Bound active book to ${request.bookId}.`,
-        })),
-        responseText: localize(language, {
-          zh: `当前作品:${request.bookId}`,
-          en: `Active book: ${request.bookId}`,
-        }),
-      };
-    }
     case "revise_chapter":
     case "rewrite_chapter": {
       const bookId = request.bookId ?? session.activeBookId;