diff --git a/README.md b/README.md
index db74d3d..f6dcbf4 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-
+



diff --git a/backend/app/api/chapters.py b/backend/app/api/chapters.py
index f250a94..6e90595 100644
--- a/backend/app/api/chapters.py
+++ b/backend/app/api/chapters.py
@@ -1973,12 +1973,13 @@ async def batch_generate_chapters_in_order(
logger.info(f"📦 创建批量生成任务: {batch_id}, 章节: 第{start_number}-{end_number}章, 预估耗时: {estimated_time}分钟")
- # 启动后台批量生成任务
+ # 启动后台批量生成任务,传递model参数
background_tasks.add_task(
execute_batch_generation_in_order,
batch_id=batch_id,
user_id=user_id,
- ai_service=user_ai_service
+ ai_service=user_ai_service,
+ custom_model=batch_request.model
)
return BatchGenerateResponse(
@@ -2105,7 +2106,8 @@ async def cancel_batch_generation(
async def execute_batch_generation_in_order(
batch_id: str,
user_id: str,
- ai_service: AIService
+ ai_service: AIService,
+ custom_model: Optional[str] = None
):
"""
按顺序执行批量生成任务(后台任务)
@@ -2194,7 +2196,7 @@ async def execute_batch_generation_in_order(
if not can_generate:
raise Exception(f"前置条件不满足: {error_msg}")
- # 生成章节内容(复用现有流式生成逻辑的核心部分)
+ # 生成章节内容(复用现有流式生成逻辑的核心部分),传递model参数
await generate_single_chapter_for_batch(
db_session=db_session,
chapter=chapter,
@@ -2202,7 +2204,8 @@ async def execute_batch_generation_in_order(
style_id=task.style_id,
target_word_count=task.target_word_count,
ai_service=ai_service,
- write_lock=write_lock
+ write_lock=write_lock,
+ custom_model=custom_model
)
logger.info(f"✅ 章节生成完成: 第{chapter.chapter_number}章")
@@ -2316,7 +2319,8 @@ async def generate_single_chapter_for_batch(
style_id: Optional[int],
target_word_count: int,
ai_service: AIService,
- write_lock: Lock
+ write_lock: Lock,
+ custom_model: Optional[str] = None
):
"""
为批量生成执行单个章节的生成(非流式)
@@ -2503,10 +2507,14 @@ async def generate_single_chapter_for_batch(
# 非流式生成内容
full_content = ""
- async for chunk in ai_service.generate_text_stream(
- prompt=prompt,
- model=None # 批量生成时使用用户默认模型,后续可扩展
- ):
+ # 准备生成参数
+ generate_kwargs = {"prompt": prompt}
+ # 如果传入了自定义模型,使用指定的模型
+ if custom_model:
+ generate_kwargs["model"] = custom_model
+ logger.info(f" 批量生成使用自定义模型: {custom_model}")
+
+ async for chunk in ai_service.generate_text_stream(**generate_kwargs):
full_content += chunk
# 更新章节内容到数据库(使用锁保护)
diff --git a/backend/app/api/outlines.py b/backend/app/api/outlines.py
index a5eb214..db9ed7b 100644
--- a/backend/app/api/outlines.py
+++ b/backend/app/api/outlines.py
@@ -1111,10 +1111,19 @@ async def new_outline_generator(
# 调用AI
yield await SSEResponse.send_progress("🤖 正在调用AI生成...", 30)
+
+ # 添加调试日志
+ model_param = data.get("model")
+ provider_param = data.get("provider")
+ logger.info(f"=== 大纲生成AI调用参数 ===")
+ logger.info(f" provider参数: {provider_param}")
+ logger.info(f" model参数: {model_param}")
+ logger.info(f" 完整data: {data}")
+
ai_response = await user_ai_service.generate_text(
prompt=prompt,
- provider=data.get("provider"),
- model=data.get("model")
+ provider=provider_param,
+ model=model_param
)
yield await SSEResponse.send_progress("✅ AI生成完成,正在解析...", 70)
@@ -1446,10 +1455,16 @@ async def continue_outline_generator(
)
# 调用AI生成当前批次
+ model_param = data.get("model")
+ provider_param = data.get("provider")
+ logger.info(f"=== 续写批次{batch_num + 1} AI调用参数 ===")
+ logger.info(f" provider参数: {provider_param}")
+ logger.info(f" model参数: {model_param}")
+
ai_response = await user_ai_service.generate_text(
prompt=prompt,
- provider=data.get("provider"),
- model=data.get("model")
+ provider=provider_param,
+ model=model_param
)
yield await SSEResponse.send_progress(
diff --git a/frontend/package.json b/frontend/package.json
index 43f1fc8..a6a66e3 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "1.0.9",
+ "version": "1.0.10",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/pages/Chapters.tsx b/frontend/src/pages/Chapters.tsx
index 329a7e6..67238dc 100644
--- a/frontend/src/pages/Chapters.tsx
+++ b/frontend/src/pages/Chapters.tsx
@@ -29,6 +29,7 @@ export default function Chapters() {
const [targetWordCount, setTargetWordCount] = useState
(3000);
const [availableModels, setAvailableModels] = useState>([]);
const [selectedModel, setSelectedModel] = useState();
+ const [batchSelectedModel, setBatchSelectedModel] = useState(); // 批量生成的模型选择
const [analysisVisible, setAnalysisVisible] = useState(false);
const [analysisChapterId, setAnalysisChapterId] = useState(null);
// 分析任务状态管理
@@ -48,6 +49,7 @@ export default function Chapters() {
const [batchGenerateVisible, setBatchGenerateVisible] = useState(false);
const [batchGenerating, setBatchGenerating] = useState(false);
const [batchTaskId, setBatchTaskId] = useState(null);
+ const [batchForm] = Form.useForm();
const [batchProgress, setBatchProgress] = useState<{
status: string;
total: number;
@@ -208,6 +210,7 @@ export default function Chapters() {
setAvailableModels(data.models);
// 设置默认模型为当前配置的模型
setSelectedModel(settings.llm_model);
+ return settings.llm_model; // 返回模型名称
}
}
} catch (error) {
@@ -218,6 +221,7 @@ export default function Chapters() {
} catch (error) {
console.error('加载可用模型失败:', error);
}
+ return null;
};
// 检查并恢复批量生成任务
@@ -590,13 +594,23 @@ export default function Chapters() {
enableAnalysis: boolean;
styleId?: number;
targetWordCount?: number;
+ model?: string;
}) => {
if (!currentProject?.id) return;
+ // 调试日志
+ console.log('[批量生成] 表单values:', values);
+ console.log('[批量生成] batchSelectedModel状态:', batchSelectedModel);
+
// 使用批量生成对话框中选择的风格和字数,如果没有选择则使用默认值
const styleId = values.styleId || selectedStyleId;
const wordCount = values.targetWordCount || targetWordCount;
+ // 使用批量生成专用的模型状态
+ const model = batchSelectedModel;
+
+ console.log('[批量生成] 最终使用的model:', model);
+
if (!styleId) {
message.error('请选择写作风格');
return;
@@ -606,18 +620,30 @@ export default function Chapters() {
setBatchGenerating(true);
setBatchGenerateVisible(false); // 关闭配置对话框,避免遮挡进度弹窗
+ const requestBody: any = {
+ start_chapter_number: values.startChapterNumber,
+ count: values.count,
+ enable_analysis: values.enableAnalysis,
+ style_id: styleId,
+ target_word_count: wordCount,
+ };
+
+ // 如果有模型参数,添加到请求体中
+ if (model) {
+ requestBody.model = model;
+ console.log('[批量生成] 请求体包含model:', model);
+ } else {
+ console.log('[批量生成] 请求体不包含model,使用后端默认模型');
+ }
+
+ console.log('[批量生成] 完整请求体:', JSON.stringify(requestBody, null, 2));
+
const response = await fetch(`/api/chapters/project/${currentProject.id}/batch-generate`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify({
- start_chapter_number: values.startChapterNumber,
- count: values.count,
- enable_analysis: values.enableAnalysis,
- style_id: styleId,
- target_word_count: wordCount,
- }),
+ body: JSON.stringify(requestBody),
});
if (!response.ok) {
@@ -725,7 +751,7 @@ export default function Chapters() {
};
// 打开批量生成对话框
- const handleOpenBatchGenerate = () => {
+ const handleOpenBatchGenerate = async () => {
// 找到第一个未生成的章节
const firstIncompleteChapter = sortedChapters.find(
ch => !ch.content || ch.content.trim() === ''
@@ -743,6 +769,24 @@ export default function Chapters() {
return;
}
+ // 打开对话框时加载模型列表,等待完成
+ const defaultModel = await loadAvailableModels();
+
+ console.log('[打开批量生成] defaultModel:', defaultModel);
+ console.log('[打开批量生成] selectedStyleId:', selectedStyleId);
+
+ // 设置批量生成的模型选择状态
+ setBatchSelectedModel(defaultModel || undefined);
+
+ // 重置表单并设置初始值
+ batchForm.setFieldsValue({
+ startChapterNumber: firstIncompleteChapter.chapter_number,
+ count: 5,
+ enableAnalysis: false,
+ styleId: selectedStyleId,
+ targetWordCount: 3000,
+ });
+
setBatchGenerateVisible(true);
};
@@ -1723,7 +1767,7 @@ export default function Chapters() {
tooltip="选择用于生成章节内容的AI模型,不选择则使用默认模型"
>
- 不同模型有不同的特点和定价,请根据需要选择
+ {selectedModel ? `当前默认模型: ${availableModels.find(m => m.value === selectedModel)?.label || selectedModel}` : '加载模型列表中...'}
@@ -1889,6 +1934,7 @@ export default function Chapters() {
>
{!batchGenerating ? (
),
okText: hasOutlines ? '开始续写' : '开始生成',