patch_rich_outline.py 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. """Phase 1 HA: rich-outline 节点加降级路径"""
  2. PATH = "/data/ai/audio/server/src/modules/book-generator/nodes/rich-outline.node.ts"
  3. with open(PATH, 'r', encoding='utf-8') as f:
  4. src = f.read()
  5. # 加 import AllProvidersFailedError
  6. old_import = "import { callLLMWithMessages, ChatMessage } from '../../../services/llm';"
  7. new_import = "import { callLLMWithMessages, ChatMessage, AllProvidersFailedError } from '../../../services/llm';"
  8. assert old_import in src, "old import not found"
  9. src = src.replace(old_import, new_import, 1)
  10. # 改 catch 块:识别 AllProvidersFailedError 走降级路径
  11. old_catch = """ } catch (error) {
  12. console.error('[RichOutline] 生成失败:', error);
  13. await bookStore.update(state.bookId, {
  14. genStage: 'failed', failedStage: 'outlining',
  15. errorMsg: error instanceof Error ? error.message : '富信息大纲生成失败',
  16. });
  17. return { error: error instanceof Error ? error.message : '失败', finished: true };
  18. }
  19. }"""
  20. new_catch = """ } catch (error: any) {
  21. // ============ HA 降级:所有供应商都挂了 ============
  22. if (error?.name === 'AllProvidersFailedError' || error instanceof AllProvidersFailedError) {
  23. console.error('[RichOutline] ⚠️ 所有 LLM 供应商均不可用,触发降级:', error.message);
  24. // 降级策略 1: 尝试复用已有的简单大纲(如果有)
  25. try {
  26. const existingBook: any = await bookStore.getById(state.bookId);
  27. const existingOutline = existingBook?.outlineJson
  28. ? (typeof existingBook.outlineJson === 'string' ? JSON.parse(existingBook.outlineJson) : existingBook.outlineJson)
  29. : null;
  30. if (existingOutline?.chapters?.length) {
  31. console.log('[RichOutline] 降级路径 1: 复用已有大纲');
  32. await bookStore.update(state.bookId, {
  33. errorMsg: `[降级-复用] 所有 LLM 供应商失败 (${error.attempts?.length || 0}个), 已复用已有大纲`,
  34. progress: PROGRESS.OUTLINE_DONE,
  35. failedStage: null,
  36. });
  37. return { progress: PROGRESS.OUTLINE_DONE } as any;
  38. }
  39. } catch (e) {
  40. console.warn('[RichOutline] 降级路径 1 失败:', e);
  41. }
  42. // 降级策略 2: 标记为 partial_outline,让后续节点继续
  43. // 不写 failedStage,让 processMonitor 有机会后续恢复
  44. console.log('[RichOutline] 降级路径 2: 标记 partial_outline, 等待后续重试');
  45. await bookStore.update(state.bookId, {
  46. genStage: 'partial_outline',
  47. errorMsg: `[降级] 所有 LLM 供应商失败 (${error.attempts?.length || 0}个): ${error.message?.substring(0, 500)}`,
  48. failedStage: null, // 关键:不标记 failed,让 processMonitor 后续重试
  49. });
  50. return { progress: PROGRESS.OUTLINE_DONE } as any;
  51. }
  52. // 真正不可恢复的错误(业务错误、解析错误等)
  53. console.error('[RichOutline] 生成失败:', error);
  54. await bookStore.update(state.bookId, {
  55. genStage: 'failed', failedStage: 'outlining',
  56. errorMsg: error instanceof Error ? error.message : '富信息大纲生成失败',
  57. });
  58. return { error: error instanceof Error ? error.message : '失败', finished: true };
  59. }
  60. }"""
  61. assert old_catch in src, "old catch not found"
  62. src = src.replace(old_catch, new_catch)
  63. with open(PATH, 'w', encoding='utf-8') as f:
  64. f.write(src)
  65. print("OK: rich-outline.node.ts patched")