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



@@ -351,7 +351,7 @@ MuMuAINovel/
- 提交 [Issue](https://github.com/xiamuceer-j/MuMuAINovel/issues)
- Linux DO [讨论](https://linux.do/t/topic/1106333)
- 加入QQ群 [QQ群](frontend/public/qq.jpg)
-- 加入WX群 [WX群](frontend/public/WX.jpg)
+- 加入WX群 [WX群](frontend/public/WX.png)
---
diff --git a/backend/app/api/chapters.py b/backend/app/api/chapters.py
index 8e88c29..f250a94 100644
--- a/backend/app/api/chapters.py
+++ b/backend/app/api/chapters.py
@@ -953,6 +953,7 @@ async def generate_chapter_content_stream(
style_id = generate_request.style_id
target_word_count = generate_request.target_word_count or 3000
enable_mcp = generate_request.enable_mcp if hasattr(generate_request, 'enable_mcp') else True
+ custom_model = generate_request.model if hasattr(generate_request, 'model') else None
# 预先验证章节存在性(使用临时会话)
async for temp_db in get_db(request):
try:
@@ -1310,12 +1311,20 @@ async def generate_chapter_content_stream(
logger.info(f"开始AI流式创作章节 {chapter_id}")
+ # 准备生成参数
+ generate_kwargs = {"prompt": prompt}
+ if custom_model:
+ logger.info(f" 使用自定义模型: {custom_model}")
+ generate_kwargs["model"] = custom_model
+ # 注意:这里使用用户配置的AI服务,模型参数会覆盖默认模型
+ # 如果需要切换provider,需要在前端传递provider参数
+
# 流式生成内容
full_content = ""
chunk_count = 0
last_progress = 0
- async for chunk in user_ai_service.generate_text_stream(prompt=prompt):
+ async for chunk in user_ai_service.generate_text_stream(**generate_kwargs):
full_content += chunk
chunk_count += 1
@@ -2494,7 +2503,10 @@ async def generate_single_chapter_for_batch(
# 非流式生成内容
full_content = ""
- async for chunk in ai_service.generate_text_stream(prompt=prompt):
+ async for chunk in ai_service.generate_text_stream(
+ prompt=prompt,
+ model=None # 批量生成时使用用户默认模型,后续可扩展
+ ):
full_content += chunk
# 更新章节内容到数据库(使用锁保护)
diff --git a/backend/app/schemas/chapter.py b/backend/app/schemas/chapter.py
index d6e27db..2427a10 100644
--- a/backend/app/schemas/chapter.py
+++ b/backend/app/schemas/chapter.py
@@ -78,6 +78,7 @@ class ChapterGenerateRequest(BaseModel):
le=10000 # 最大10000字
)
enable_mcp: bool = Field(True, description="是否启用MCP工具增强(搜索参考资料)")
+ model: Optional[str] = Field(None, description="指定使用的AI模型,不提供则使用用户默认模型")
class BatchGenerateRequest(BaseModel):
@@ -94,6 +95,7 @@ class BatchGenerateRequest(BaseModel):
enable_analysis: bool = Field(False, description="是否启用同步分析")
enable_mcp: bool = Field(True, description="是否启用MCP工具增强(搜索参考资料)")
max_retries: int = Field(3, description="每个章节的最大重试次数", ge=0, le=5)
+ model: Optional[str] = Field(None, description="指定使用的AI模型,不提供则使用用户默认模型")
class BatchGenerateResponse(BaseModel):
diff --git a/frontend/package.json b/frontend/package.json
index 8990ee7..43f1fc8 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,7 +1,7 @@
{
"name": "frontend",
"private": true,
- "version": "1.0.8",
+ "version": "1.0.9",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/frontend/src/pages/Chapters.tsx b/frontend/src/pages/Chapters.tsx
index 159857a..329a7e6 100644
--- a/frontend/src/pages/Chapters.tsx
+++ b/frontend/src/pages/Chapters.tsx
@@ -27,6 +27,8 @@ export default function Chapters() {
const [writingStyles, setWritingStyles] = useState
([]);
const [selectedStyleId, setSelectedStyleId] = useState();
const [targetWordCount, setTargetWordCount] = useState(3000);
+ const [availableModels, setAvailableModels] = useState>([]);
+ const [selectedModel, setSelectedModel] = useState();
const [analysisVisible, setAnalysisVisible] = useState(false);
const [analysisChapterId, setAnalysisChapterId] = useState(null);
// 分析任务状态管理
@@ -187,6 +189,37 @@ export default function Chapters() {
}
};
+ const loadAvailableModels = async () => {
+ try {
+ // 从设置API获取用户配置的模型列表
+ const settingsResponse = await fetch('/api/settings');
+ if (settingsResponse.ok) {
+ const settings = await settingsResponse.json();
+ const { api_key, api_base_url, api_provider } = settings;
+
+ 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) {
+ setAvailableModels(data.models);
+ // 设置默认模型为当前配置的模型
+ setSelectedModel(settings.llm_model);
+ }
+ }
+ } catch (error) {
+ console.log('获取模型列表失败,将使用默认模型');
+ }
+ }
+ }
+ } catch (error) {
+ console.error('加载可用模型失败:', error);
+ }
+ };
+
// 检查并恢复批量生成任务
const checkAndRestoreBatchTask = async () => {
if (!currentProject?.id) return;
@@ -270,6 +303,10 @@ export default function Chapters() {
try {
await updateChapter(editingId, values);
+
+ // 刷新章节列表以获取完整的章节数据(包括outline_title等联查字段)
+ await refreshChapters();
+
message.success('章节更新成功');
setIsModalOpen(false);
form.resetFields();
@@ -288,6 +325,8 @@ export default function Chapters() {
});
setEditingId(id);
setIsEditorOpen(true);
+ // 打开编辑窗口时加载模型列表
+ loadAvailableModels();
}
};
@@ -335,7 +374,8 @@ export default function Chapters() {
// 进度回调
setSingleChapterProgress(progressValue);
setSingleChapterProgressMessage(progressMsg);
- }
+ },
+ selectedModel // 传递选中的模型
);
message.success('AI创作成功,正在分析章节内容...');
@@ -1517,9 +1557,21 @@ export default function Chapters() {
-
+
+
+
+
+ 不同模型有不同的特点和定价,请根据需要选择
+
+
+