feat(Hook): 实现智能摘要 useSummary Hook
- 实现流式摘要生成逻辑 - 支持摘要选项配置 (长度/风格) - 添加生成状态管理 - 支持摘要更新和保存
This commit is contained in:
parent
5d301364fb
commit
d61d689db0
169
src/components/features/SummaryGenerator/useSummary.ts
Normal file
169
src/components/features/SummaryGenerator/useSummary.ts
Normal file
@ -0,0 +1,169 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import type {
|
||||
SummaryOptions,
|
||||
SummaryStatus,
|
||||
SummaryData,
|
||||
SummaryMessage,
|
||||
UseSummaryReturn,
|
||||
} from './types';
|
||||
|
||||
/**
|
||||
* 智能摘要核心 Hook
|
||||
* 管理摘要生成的状态和操作
|
||||
*/
|
||||
export function useSummary(): UseSummaryReturn {
|
||||
// 生成状态
|
||||
const [status, setStatus] = useState<SummaryStatus>('idle');
|
||||
const [summary, setSummary] = useState<SummaryData | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [streamingContent, setStreamingContent] = useState<string>('');
|
||||
|
||||
// 摘要选项
|
||||
const [options, setOptionsState] = useState<SummaryOptions>({
|
||||
length: 'standard',
|
||||
style: 'bullet',
|
||||
});
|
||||
|
||||
// 更新选项
|
||||
const setOptions = useCallback((newOptions: Partial<SummaryOptions>) => {
|
||||
setOptionsState(prev => ({ ...prev, ...newOptions }));
|
||||
}, []);
|
||||
|
||||
// 生成摘要
|
||||
const generate = useCallback(async (conversationId: string, messages: SummaryMessage[]) => {
|
||||
if (messages.length === 0) {
|
||||
setError('暂无对话内容');
|
||||
return;
|
||||
}
|
||||
|
||||
setStatus('generating');
|
||||
setError(null);
|
||||
setStreamingContent('');
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/summary/generate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
conversationId,
|
||||
messages,
|
||||
options,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json();
|
||||
throw new Error(errorData.error || '生成摘要失败');
|
||||
}
|
||||
|
||||
// 处理流式响应
|
||||
const reader = response.body?.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
|
||||
if (!reader) {
|
||||
throw new Error('无法读取响应');
|
||||
}
|
||||
|
||||
let fullContent = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value, { stream: true });
|
||||
const lines = chunk.split('\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.slice(6);
|
||||
|
||||
if (data === '[DONE]') {
|
||||
// 流式传输完成
|
||||
setStatus('completed');
|
||||
setSummary({
|
||||
content: fullContent,
|
||||
generatedAt: new Date(),
|
||||
messageCount: messages.length,
|
||||
options,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(data);
|
||||
|
||||
if (parsed.type === 'content') {
|
||||
fullContent += parsed.content;
|
||||
setStreamingContent(fullContent);
|
||||
} else if (parsed.type === 'error') {
|
||||
throw new Error(parsed.error);
|
||||
}
|
||||
} catch {
|
||||
// 忽略解析错误,可能是不完整的 JSON
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有收到 [DONE],也设置为完成
|
||||
if (fullContent) {
|
||||
setStatus('completed');
|
||||
setSummary({
|
||||
content: fullContent,
|
||||
generatedAt: new Date(),
|
||||
messageCount: messages.length,
|
||||
options,
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
setStatus('error');
|
||||
setError(err instanceof Error ? err.message : '生成摘要时发生错误');
|
||||
}
|
||||
}, [options]);
|
||||
|
||||
// 重置状态
|
||||
const reset = useCallback(() => {
|
||||
setStatus('idle');
|
||||
setSummary(null);
|
||||
setError(null);
|
||||
setStreamingContent('');
|
||||
}, []);
|
||||
|
||||
// 保存摘要到数据库
|
||||
const saveSummary = useCallback(async (conversationId: string): Promise<boolean> => {
|
||||
if (!summary) return false;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/conversations/${conversationId}/summary`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
summary: summary.content,
|
||||
}),
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}, [summary]);
|
||||
|
||||
return {
|
||||
status,
|
||||
summary,
|
||||
error,
|
||||
streamingContent,
|
||||
options,
|
||||
setOptions,
|
||||
generate,
|
||||
reset,
|
||||
saveSummary,
|
||||
};
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user