# All files / modules/book-generator/strategies base.ts

0% Statements 0/54
0% Branches 0/1
0% Functions 0/1
0% Lines 0/54

Press n or j to go to the next uncovered block, b, p or k for the previous block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81

     /**
     * 基础策略 - 共享的 graph 执行、进度监控、错误处理逻辑
     */

    import { StateGraph, END } from '@langchain/langgraph';
    import { GraphState } from '../graph';
    import { bookStore } from '../book-generator.store';
    import { startProgressMonitor } from '../fault-tolerance';

    /** 基础策略选项 */
    export interface BaseStrategyOptions {
      name: string;
      description: string;
      buildGraph: () => ReturnType<typeof buildAndCompile>;
    }

    function buildAndCompile() {
      const workflow = new StateGraph(GraphState);
      return { workflow, addEdge: workflow.addEdge.bind(workflow) };
    }

    /**
     * 运行一个 LangGraph 工作流并处理结果
     * 所有策略的 graph 执行逻辑相同，只是 graph 结构不同
     */
    export async function runGraphWorkflow(
      bookId: string,
      initialState: typeof GraphState.State,
      workflow: StateGraph<typeof GraphState.State, any, any, any, any>
    ): Promise<void> {
      await bookStore.update(bookId, { genStage: 'outlining', progress: 0 });

      const stopMonitor = startProgressMonitor(bookId);

      try {
        const graph = workflow.compile();
        const stream = await graph.stream(initialState);
        for await (const step of stream) {
          const stepName = Object.keys(step).join(',');
          if (stepName !== 'progress') {
            console.log(`[Graph] Step:`, stepName);
          }
        }

        // 检查最终状态
        const finalBook = await bookStore.getById(bookId);
        if (finalBook && finalBook.genStage !== 'failed') {
          const finalStage = finalBook.genStage || 'content_completed';
          const stageUpdate: any = { progress: 100 };
          if (['content_completed', 'audio_generating', 'audio_completed'].includes(finalStage)) {
            stageUpdate.genStage = finalStage;
          }
          await bookStore.update(bookId, stageUpdate);
          // 只有最终态 video_completed 才自动发布
          if (finalStage === 'video_completed') {
            await bookStore.publishAlbum(bookId);
          }
          console.log(`[Strategy] 生成完成，genStage: ${finalStage}`);
        } else {
          console.log('[Strategy] 生成失败，保持 failed 状态');
        }
      } catch (error) {
        console.error('[Strategy] 生成失败:', error);
        // 尝试从数据库获取当前真实阶段，作为 failedStage
        let failedStage = 'outlining';
        try {
          const currentBook = await bookStore.getById(bookId);
          if (currentBook?.genStage) {
            failedStage = currentBook.genStage;
          }
        } catch { /* 取不到就用默认值 */ }
        await bookStore.update(bookId, {
          genStage: 'failed',
          failedStage,
          errorMsg: error instanceof Error ? error.message : '生成失败',
        });
      } finally {
        stopMonitor();
      }
    }