Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a structured multi-step PPT generation workflow that allows users to generate, edit, and confirm a design outline and page-by-page designs before generating slide images. It also adds support for parsing multiple document formats, configures three distinct model roles, and implements dark mode and localization. The review feedback highlights several important issues, including a critical bug where the temporary directory is cleaned up before the exported presentation file is served, a usability issue in the outline editor that prevents typing newlines, potential validation failures when slide prompts are received out of order, a possible IndexError on empty model choices, and unhandled exceptions during document parsing.
| return FileResponse( | ||
| path=str(output_path), |
There was a problem hiding this comment.
There was a problem hiding this comment.
Fixed in efd576c. Exported files now use a BackgroundTask to remove the temporary directory only after FileResponse finishes sending, and error paths clean up the directory immediately.
| const updateSlideKeyPoints = useCallback((page: number, value: string) => { | ||
| markOutlineDirty() | ||
| const keyPoints = value | ||
| .split('\n') | ||
| .map((item) => item.trim()) | ||
| .filter(Boolean) | ||
| setOutline((current) => current | ||
| ? { | ||
| ...current, | ||
| slides: current.slides.map((slide) => ( | ||
| slide.page === page ? { ...slide, key_points: keyPoints } : slide | ||
| )) | ||
| } | ||
| : current) | ||
| }, [markOutlineDirty]) |
There was a problem hiding this comment.
在 updateSlideKeyPoints 中,每次输入(onChange)都会通过 .filter(Boolean) 过滤掉空行。当用户在 textarea 中按下回车键准备换行时,输入值末尾会产生一个空行,该空行会被立即过滤掉,导致用户无法正常换行输入多个要点。建议在输入时不进行过滤(保留空行),而是在提交大纲(handleGeneratePrompts)时再统一过滤空行。
const updateSlideKeyPoints = useCallback((page: number, value: string) => {
markOutlineDirty()
const keyPoints = value.split('\n')
setOutline((current) => current
? {
...current,
slides: current.slides.map((slide) => (
slide.page === page ? { ...slide, key_points: keyPoints } : slide
))
}
: current)
}, [markOutlineDirty])
There was a problem hiding this comment.
Fixed in efd576c. The outline key-points textarea now preserves blank trailing lines while editing, so users can press Enter to create a new item naturally.
| const parsedOutline = outline | ||
| if (!Array.isArray(parsedOutline.slides) || parsedOutline.slides.length !== generationConfig.pageCount) { | ||
| throw new Error(t('workflow.pageCountMismatch', { count: generationConfig.pageCount })) | ||
| } |
There was a problem hiding this comment.
配合 updateSlideKeyPoints 的修改,建议在提交大纲生成逐页设计时,在此处对 key_points 进行统一的过滤和去空格处理,以确保发送给后端的请求数据干净且不包含空行。
const cleanedSlides = outline.slides.map((slide) => ({
...slide,
key_points: slide.key_points.map((item) => item.trim()).filter(Boolean)
}))
const parsedOutline = { ...outline, slides: cleanedSlides }
if (!Array.isArray(parsedOutline.slides) || parsedOutline.slides.length !== generationConfig.pageCount) {
throw new Error(t('workflow.pageCountMismatch', { count: generationConfig.pageCount }))
}
There was a problem hiding this comment.
Fixed in efd576c. Key points are now trimmed and empty entries are removed only when submitting the outline to generate page designs.
| slide_prompts = request.slide_prompts or [] | ||
| if len(slide_prompts) != ppt_config.num_pages: | ||
| raise ValueError(f"确认后的逐页设计数量不匹配: 期望{ppt_config.num_pages}页,实际{len(slide_prompts)}页") | ||
|
|
||
| pages = [item.page for item in slide_prompts] | ||
| expected = list(range(1, ppt_config.num_pages + 1)) | ||
| if pages != expected: |
There was a problem hiding this comment.
如果前端传入的 slide_prompts 顺序被打乱(例如 [2, 1, 3]),直接对比 pages != expected 会导致校验失败并抛出 ValueError。建议在校验前先对 slide_prompts 按页码进行排序,这样不仅能提高校验的容错性,还能确保后续生成的 PromptData 顺序是正确的。
| slide_prompts = request.slide_prompts or [] | |
| if len(slide_prompts) != ppt_config.num_pages: | |
| raise ValueError(f"确认后的逐页设计数量不匹配: 期望{ppt_config.num_pages}页,实际{len(slide_prompts)}页") | |
| pages = [item.page for item in slide_prompts] | |
| expected = list(range(1, ppt_config.num_pages + 1)) | |
| if pages != expected: | |
| slide_prompts = sorted(request.slide_prompts or [], key=lambda x: x.page) | |
| if len(slide_prompts) != ppt_config.num_pages: | |
| raise ValueError(f"确认后的逐页设计数量不匹配: 期望{ppt_config.num_pages}页,实际{len(slide_prompts)}页") | |
| pages = [item.page for item in slide_prompts] | |
| expected = list(range(1, ppt_config.num_pages + 1)) | |
| if pages != expected: |
There was a problem hiding this comment.
Fixed in efd576c. Confirmed slide prompts are sorted by page before validation and PromptData construction, so shuffled input is accepted when the page set is valid.
| response = client.chat.completions.create(model=profile.model, messages=messages) | ||
| return response.choices[0].message.content or "" |
There was a problem hiding this comment.
There was a problem hiding this comment.
Fixed in efd576c. Text generation now returns an empty string safely when the provider response contains no choices instead of raising IndexError.
| temp_path = Path(temp_file.name) | ||
|
|
||
| try: | ||
| content_str = DocumentParser().parse(temp_path).normalized_markdown |
There was a problem hiding this comment.
Fixed in efd576c. Upload parsing now maps unsupported parser errors to 400 responses and missing parser/runtime failures to explicit 500 responses with clearer messages.
Summary
doc/L9.md,补充中英文 README、API 文档和高清演示视频。Test Plan
npm run lintnpm run testnpm run build/tmp/aippt-test-20260531-demo/bin/python -m pytest testsdoc/L9.md走上传、多页生成、单页编辑、PDF/PPTX 导出和演示录制流程。Notes
config.yaml仍作为本地配置文件被忽略;仓库提供config.example.yaml作为模板。