update:1.增加AI批量创作章节内容时,支持加载模型列表,使用不同模型批量生成内容 2.支持AI生成/续写大纲时,选择自定义模型进行生成

This commit is contained in:
xiamuceer
2025-12-04 21:14:44 +08:00
parent 16a1686911
commit 187feac671
6 changed files with 199 additions and 31 deletions
+82 -10
View File
@@ -29,6 +29,7 @@ export default function Chapters() {
const [targetWordCount, setTargetWordCount] = useState<number>(3000);
const [availableModels, setAvailableModels] = useState<Array<{value: string, label: string}>>([]);
const [selectedModel, setSelectedModel] = useState<string | undefined>();
const [batchSelectedModel, setBatchSelectedModel] = useState<string | undefined>(); // 批量生成的模型选择
const [analysisVisible, setAnalysisVisible] = useState(false);
const [analysisChapterId, setAnalysisChapterId] = useState<string | null>(null);
// 分析任务状态管理
@@ -48,6 +49,7 @@ export default function Chapters() {
const [batchGenerateVisible, setBatchGenerateVisible] = useState(false);
const [batchGenerating, setBatchGenerating] = useState(false);
const [batchTaskId, setBatchTaskId] = useState<string | null>(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模型,不选择则使用默认模型"
>
<Select
placeholder="使用默认模型"
placeholder={selectedModel ? `默认: ${availableModels.find(m => m.value === selectedModel)?.label || selectedModel}` : "使用默认模型"}
value={selectedModel}
onChange={setSelectedModel}
size="large"
@@ -1736,11 +1780,12 @@ export default function Chapters() {
{availableModels.map(model => (
<Select.Option key={model.value} value={model.value} label={model.label}>
{model.label}
{model.value === selectedModel && ' (默认)'}
</Select.Option>
))}
</Select>
<div style={{ color: '#666', fontSize: 12, marginTop: 4 }}>
{selectedModel ? `当前默认模型: ${availableModels.find(m => m.value === selectedModel)?.label || selectedModel}` : '加载模型列表中...'}
</div>
</Form.Item>
@@ -1889,6 +1934,7 @@ export default function Chapters() {
>
{!batchGenerating ? (
<Form
form={batchForm}
layout="vertical"
onFinish={handleBatchGenerate}
initialValues={{
@@ -1897,6 +1943,7 @@ export default function Chapters() {
enableAnalysis: false,
styleId: selectedStyleId,
targetWordCount: 3000,
model: selectedModel,
}}
>
<Alert
@@ -1989,6 +2036,31 @@ export default function Chapters() {
</div>
</Form.Item>
<Form.Item
label="AI模型"
tooltip="批量生成时所有章节使用相同模型,不选择则使用默认模型"
>
<Select
placeholder={batchSelectedModel ? `默认: ${availableModels.find(m => m.value === batchSelectedModel)?.label || batchSelectedModel}` : "使用默认模型"}
value={batchSelectedModel}
onChange={setBatchSelectedModel}
size="large"
allowClear
showSearch
optionFilterProp="label"
>
{availableModels.map(model => (
<Select.Option key={model.value} value={model.value} label={model.label}>
{model.label}
{model.value === batchSelectedModel && ' (默认)'}
</Select.Option>
))}
</Select>
<div style={{ color: '#666', fontSize: 12, marginTop: 4 }}>
{batchSelectedModel ? `当前默认模型: ${availableModels.find(m => m.value === batchSelectedModel)?.label || batchSelectedModel}` : '加载模型列表中...'}
</div>
</Form.Item>
<Form.Item
label="同步分析"
name="enableAnalysis"
+78 -5
View File
@@ -165,6 +165,12 @@ export default function Outline() {
try {
setIsGenerating(true);
// 添加详细的调试日志
console.log('=== 大纲生成调试信息 ===');
console.log('1. Form values 原始数据:', values);
console.log('2. values.model:', values.model);
console.log('3. values.provider:', values.provider);
// 关闭生成表单Modal
Modal.destroyAll();
@@ -174,7 +180,7 @@ export default function Outline() {
setSSEModalVisible(true);
// 准备请求数据
const requestData = {
const requestData: any = {
project_id: currentProject.id,
genre: currentProject.genre || '通用',
theme: values.theme || currentProject.theme || '',
@@ -184,11 +190,26 @@ export default function Outline() {
requirements: values.requirements,
mode: values.mode || 'auto',
story_direction: values.story_direction,
plot_stage: values.plot_stage || 'development',
provider: values.provider,
model: values.model
plot_stage: values.plot_stage || 'development'
};
// 只有在用户选择了模型时才添加model参数
if (values.model) {
requestData.model = values.model;
console.log('4. 添加model到请求:', values.model);
} else {
console.log('4. values.model为空,不添加到请求');
}
// 添加provider参数(如果有)
if (values.provider) {
requestData.provider = values.provider;
console.log('5. 添加provider到请求:', values.provider);
}
console.log('6. 最终请求数据:', JSON.stringify(requestData, null, 2));
console.log('=========================');
// 使用SSE客户端
const apiUrl = `/api/outlines/generate-stream`;
const client = new SSEPostClient(apiUrl, requestData, {
@@ -224,10 +245,35 @@ export default function Outline() {
}
};
const showGenerateModal = () => {
const showGenerateModal = async () => {
const hasOutlines = outlines.length > 0;
const initialMode = hasOutlines ? 'continue' : 'new';
// 直接加载可用模型列表
const settingsResponse = await fetch('/api/settings');
const settings = await settingsResponse.json();
const { api_key, api_base_url, api_provider } = settings;
let loadedModels: Array<{value: string, label: string}> = [];
let defaultModel: string | undefined = undefined;
if (api_key && api_base_url) {
try {
const modelsResponse = await fetch(
`/api/settings/models?api_key=${encodeURIComponent(api_key)}&api_base_url=${encodeURIComponent(api_base_url)}&provider=${api_provider}`
);
if (modelsResponse.ok) {
const data = await modelsResponse.json();
if (data.models && data.models.length > 0) {
loadedModels = data.models;
defaultModel = settings.llm_model;
}
}
} catch (error) {
console.log('获取模型列表失败,将使用默认模型');
}
}
Modal.confirm({
title: hasOutlines ? (
<Space>
@@ -249,6 +295,7 @@ export default function Outline() {
plot_stage: 'development',
keep_existing: true,
theme: currentProject.theme || '',
model: defaultModel, // 添加默认模型
}}
>
{hasOutlines && (
@@ -360,6 +407,32 @@ export default function Outline() {
);
}}
</Form.Item>
{/* 自定义模型选择 - 移到外层,所有模式都显示 */}
{loadedModels.length > 0 && (
<Form.Item
label="AI模型"
name="model"
tooltip="选择用于生成的AI模型,不选则使用系统默认模型"
>
<Select
placeholder={defaultModel ? `默认: ${loadedModels.find(m => m.value === defaultModel)?.label || defaultModel}` : "使用默认模型"}
allowClear
showSearch
optionFilterProp="label"
options={loadedModels}
onChange={(value) => {
console.log('用户在下拉框中选择了模型:', value);
// 手动同步到Form
generateForm.setFieldsValue({ model: value });
console.log('已同步到Form,当前Form值:', generateForm.getFieldsValue());
}}
/>
<div style={{ color: '#666', fontSize: 12, marginTop: 4 }}>
{defaultModel ? `当前默认模型: ${loadedModels.find(m => m.value === defaultModel)?.label || defaultModel}` : '未配置默认模型'}
</div>
</Form.Item>
)}
</Form>
),
okText: hasOutlines ? '开始续写' : '开始生成',