All files / modules/book-generator index.ts

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

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 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                                                                                                                                                                                                                               
/**
 * LangGraph 书籍生成器 - 主入口(策略门面)
 *
 * 使用策略模式,通过 strategies/selector.ts 配置切换不同生成方案
 *
 * 当前支持策略:
 *   1. sequential          - 串行(原始方案)
 *   2. one-step-outline     - 一步大纲 + 并行内容
 *   3. per-chapter          - 逐章内聚
 *   4. deep-plan-parallel   - DeepPlan+RichOutline+Concurrent+Edit(当前推荐,默认策略)
 *
 * 大纲层级(genLevel):
 *   1 = 仅章(短文/小说/故事类)
 *   2 = 章→节(科普/商业/大众类)
 *   3 = 章→节→小节(教材/技术教程/专业类)
 */
 
import { bookStore } from './book-generator.store';
import { getScaleConfig } from './book-type-config';
import { getCurrentStrategy, setCurrentStrategy, getAllStrategies, getCurrentStrategyName } from './strategies/selector';
import type { StrategyName } from './strategies/selector';
 
// ============ 书籍类型 → 大纲层级映射 ============
 
const BOOK_TYPE_GEN_LEVEL: Record<string, number> = {
  '教材': 3,
  '技术教程': 3,
  '小说': 1,
  '传记': 1,
  '商业': 2,
  '科普': 2,
  '历史': 2,
  '学术': 3,
  '心理学': 2,
};
 
/**
 * 将书籍类型映射为大纲层级
 */
export function mapBookTypeToGenLevel(bookType: string): number {
  return BOOK_TYPE_GEN_LEVEL[bookType] ?? 2;
}
 
/**
 * 根据书籍规模和话题文本自动推荐大纲层级
 * 增强版:如未指定 genLevel,会从话题文本推断书籍类型→层级
 */
export function resolveGenLevel(bookScale: string, userGenLevel?: number | string, topic?: string): number {
  if (userGenLevel !== undefined && userGenLevel !== null) {
    const n = typeof userGenLevel === 'string' ? parseInt(userGenLevel) : userGenLevel;
    if ([1, 2, 3].includes(n)) return n;
  }
  const config = getScaleConfig(bookScale);
  if (!config) return 2;
  if (config.isShortArticle) return 1;
 
  // 从话题文本推断书籍类型,增强 genLevel 推荐准确度
  if (topic) {
    const text = topic.toLowerCase();
    if (/教材|教程|编程|开发|实战|教学|课程/i.test(text)) return 3;
    if (/小说|故事|传记|散文|诗歌/i.test(text)) return 1;
  }
 
  return 2;
}
 
// ============ 策略切换 API(外部可调用) ============
 
export { setCurrentStrategy, getCurrentStrategyName, getAllStrategies } from './strategies/selector';
export type { StrategyName } from './strategies/selector';
 
// ============ 主类(策略门面) ============
 
export class LangGraphBookGenerator {
  async generate(
    bookId: string,
    topic: string,
    bookScale: string = '1000',
    genLevel?: number
  ): Promise<void> {
    const userSpecified = genLevel !== undefined; // true=用户选了层级, false=自动
    const level = genLevel ?? resolveGenLevel(bookScale, undefined, topic);
 
    // 获取当前激活的策略并执行
    const strategy = getCurrentStrategy();
    console.log(`[LangGraph] 开始生成: bookId=${bookId}, scale=${bookScale}, genLevel=${level}, strategy=${strategy.name}`);
 
    await strategy.generate(bookId, topic, bookScale, level, userSpecified ? level : undefined);
  }
}
 
export const langGraphGenerator = new LangGraphBookGenerator();
 
// 重新导出 book-type-config 中的函数供外部使用
export { getScaleConfig } from './book-type-config';
 
// ============ 独立大纲生成函数(供 API 预览调用,不依赖完整策略) ============
 
import { callLLMWithMessages } from '../../services/llm';
import { buildOutlineMessages } from './prompts/builder';
import { parseOutline } from './parsers/outline.parser';
 
/**
 * 独立的大纲生成函数(供 API 预览调用,不依赖策略系统)
 * 用于让用户在正式开始生成前预览大纲结构
 */
export async function generateOutline(bookId: string, topic: string, bookScale: string = '1000'): Promise<any> {
  console.log('[generateOutline] 预览大纲, bookId:', bookId, 'scale:', bookScale);
 
  const messages = buildOutlineMessages(topic, bookScale);
  const response = await callLLMWithMessages(messages);
  const outline = parseOutline(response);
 
  if (!outline) {
    throw new Error('大纲解析失败');
  }
 
  return outline;
}