update:1.增加AI批量创作章节内容时,支持加载模型列表,使用不同模型批量生成内容 2.支持AI生成/续写大纲时,选择自定义模型进行生成
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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 ? '开始续写' : '开始生成',
|
||||
|
||||
Reference in New Issue
Block a user