feat(Hook): 实现智能摘要 useSummary Hook

- 实现流式摘要生成逻辑
- 支持摘要选项配置 (长度/风格)
- 添加生成状态管理
- 支持摘要更新和保存
This commit is contained in:
gaoziman 2025-12-28 01:30:27 +08:00
parent 5d301364fb
commit d61d689db0

View 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,
};
}