fix: MCP插件TimeoutError修复 + 多项Bug修复和性能优化

- fix: MCP插件管理接口改为后台任务,修复TimeoutError
- fix: MCP连接失败后上下文清理的cancel scope错误
- feat: MCP插件后台注册添加重试机制
- fix: 限制每章自动创建伏笔数量上限
- fix: 修复JSON非法转义字符清洗
- fix: SSE流式生成添加心跳保活
- fix: 职业生成改用POST请求避免URL长度限制
- perf: 使用torch CPU版本加速Docker构建
- fix: 自动修复JSON字符串值中的裸换行符
- feat: 集成json5容错解析器
This commit is contained in:
未来
2026-04-26 13:58:15 +08:00
parent 5c22f29bf9
commit 17e78955a9
18 changed files with 559 additions and 179 deletions
+57 -37
View File
@@ -171,47 +171,67 @@ export default function Careers() {
try {
const userRequirements = values.user_requirements?.trim() || '';
const eventSource = new EventSource(
`/api/careers/generate-system?` +
new URLSearchParams({
// 使用 fetch + POST 替代 EventSource GET,避免 URL 长度限制
const response = await fetch('/api/careers/generate-system', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
project_id: projectId || '',
main_career_count: values.main_career_count.toString(),
sub_career_count: values.sub_career_count.toString(),
main_career_count: values.main_career_count,
sub_career_count: values.sub_career_count,
user_requirements: userRequirements,
enable_mcp: 'false'
}).toString(),
{ withCredentials: true }
);
enable_mcp: false
})
});
eventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'progress') {
setAiProgress(data.progress || 0);
setAiMessage(data.message || '');
} else if (data.type === 'done') {
eventSource.close();
setTimeout(() => {
setAiGenerating(false);
message.success('AI新职业生成完成!');
fetchCareers();
}, 1000);
} else if (data.type === 'error') {
eventSource.close();
setAiGenerating(false);
message.error(data.message || '生成失败');
}
} catch (e) {
console.error('解析SSE数据失败:', e);
}
};
eventSource.onerror = () => {
eventSource.close();
if (!response.ok || !response.body) {
setAiGenerating(false);
message.error('连接中断,生成失败');
};
message.error(`请求失败: ${response.status}`);
return;
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ')) {
try {
const data = JSON.parse(line.slice(6));
if (data.type === 'progress') {
setAiProgress(data.progress || 0);
setAiMessage(data.message || '');
} else if (data.type === 'done') {
setTimeout(() => {
setAiGenerating(false);
message.success('AI新职业生成完成!');
fetchCareers();
}, 1000);
} else if (data.type === 'error') {
setAiGenerating(false);
message.error(data.error || data.message || '生成失败');
}
} catch (e) {
// 忽略非JSON行(如心跳注释)
}
}
}
}
setAiGenerating(false);
} catch (err: unknown) {
setAiGenerating(false);
const error = err as Error;
+1
View File
@@ -151,6 +151,7 @@ export class SSEPostClient {
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify(this.data),
signal: this.abortController.signal,
});