207 lines
5.5 KiB
TypeScript
207 lines
5.5 KiB
TypeScript
// src/views/AgentChat/hooks/useAgentSessions.ts
|
||
// 会话管理 Hook - 加载、切换、创建会话
|
||
|
||
import { useState, useEffect, useCallback } from 'react';
|
||
import type { Dispatch, SetStateAction } from 'react';
|
||
import axios from 'axios';
|
||
import { MessageTypes, type Message } from '../constants/messageTypes';
|
||
import { getApiBase } from '@utils/apiConfig';
|
||
|
||
/**
|
||
* 会话数据结构
|
||
*/
|
||
export interface Session {
|
||
session_id: string;
|
||
title?: string;
|
||
created_at?: string;
|
||
timestamp?: string;
|
||
message_count?: number;
|
||
}
|
||
|
||
/**
|
||
* 用户信息(从 AuthContext 传入)
|
||
*/
|
||
export interface User {
|
||
id: string;
|
||
nickname?: string;
|
||
avatar?: string;
|
||
subscription_type?: string;
|
||
}
|
||
|
||
/**
|
||
* useAgentSessions Hook 参数
|
||
*/
|
||
export interface UseAgentSessionsParams {
|
||
/** 当前用户信息 */
|
||
user: User | null;
|
||
/** 消息列表 setter(用于创建新会话时设置欢迎消息) */
|
||
setMessages: Dispatch<SetStateAction<Message[]>>;
|
||
}
|
||
|
||
/**
|
||
* useAgentSessions Hook 返回值
|
||
*/
|
||
export interface UseAgentSessionsReturn {
|
||
/** 会话列表 */
|
||
sessions: Session[];
|
||
/** 当前选中的会话 ID */
|
||
currentSessionId: string | null;
|
||
/** 设置当前会话 ID */
|
||
setCurrentSessionId: Dispatch<SetStateAction<string | null>>;
|
||
/** 是否正在加载会话列表 */
|
||
isLoadingSessions: boolean;
|
||
/** 加载会话列表 */
|
||
loadSessions: () => Promise<void>;
|
||
/** 切换到指定会话 */
|
||
switchSession: (sessionId: string) => void;
|
||
/** 创建新会话(显示欢迎消息) */
|
||
createNewSession: () => void;
|
||
/** 加载指定会话的历史消息 */
|
||
loadSessionHistory: (sessionId: string) => Promise<void>;
|
||
}
|
||
|
||
/**
|
||
* useAgentSessions Hook
|
||
*
|
||
* 管理会话列表、会话切换、新建会话逻辑
|
||
*
|
||
* @param params - UseAgentSessionsParams
|
||
* @returns UseAgentSessionsReturn
|
||
*
|
||
* @example
|
||
* ```tsx
|
||
* const {
|
||
* sessions,
|
||
* currentSessionId,
|
||
* isLoadingSessions,
|
||
* switchSession,
|
||
* createNewSession,
|
||
* } = useAgentSessions({ user, setMessages });
|
||
* ```
|
||
*/
|
||
export const useAgentSessions = ({
|
||
user,
|
||
setMessages,
|
||
}: UseAgentSessionsParams): UseAgentSessionsReturn => {
|
||
const [sessions, setSessions] = useState<Session[]>([]);
|
||
const [currentSessionId, setCurrentSessionId] = useState<string | null>(null);
|
||
const [isLoadingSessions, setIsLoadingSessions] = useState(false);
|
||
|
||
/**
|
||
* 加载用户的会话列表
|
||
*/
|
||
const loadSessions = useCallback(async () => {
|
||
if (!user?.id) return;
|
||
|
||
setIsLoadingSessions(true);
|
||
try {
|
||
const response = await axios.get(`${getApiBase()}/mcp/agent/sessions`, {
|
||
params: { user_id: String(user.id), limit: 50 },
|
||
});
|
||
|
||
if (response.data.success) {
|
||
setSessions(response.data.data);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载会话列表失败:', error);
|
||
} finally {
|
||
setIsLoadingSessions(false);
|
||
}
|
||
}, [user?.id]);
|
||
|
||
/**
|
||
* 安全解析 JSON 字符串,如果已经是对象则直接返回
|
||
*/
|
||
const safeJsonParse = (value: unknown): unknown => {
|
||
if (!value) return null;
|
||
if (typeof value === 'object') return value;
|
||
if (typeof value === 'string') {
|
||
try {
|
||
return JSON.parse(value);
|
||
} catch {
|
||
console.warn('JSON 解析失败:', value?.toString().substring(0, 100));
|
||
return null;
|
||
}
|
||
}
|
||
return null;
|
||
};
|
||
|
||
/**
|
||
* 加载指定会话的历史消息
|
||
*/
|
||
const loadSessionHistory = useCallback(
|
||
async (sessionId: string) => {
|
||
if (!sessionId) return;
|
||
|
||
try {
|
||
const response = await axios.get(`${getApiBase()}/mcp/agent/history/${sessionId}`, {
|
||
params: { limit: 100 },
|
||
});
|
||
|
||
if (response.data.success) {
|
||
const history = response.data.data;
|
||
const formattedMessages: Message[] = history.map((msg: any, idx: number) => ({
|
||
id: `${sessionId}-${idx}`,
|
||
type: msg.message_type === 'user' ? MessageTypes.USER : MessageTypes.AGENT_RESPONSE,
|
||
content: msg.message || '',
|
||
plan: safeJsonParse(msg.plan),
|
||
stepResults: safeJsonParse(msg.steps),
|
||
timestamp: msg.timestamp,
|
||
}));
|
||
|
||
setMessages(formattedMessages);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载会话历史失败:', error);
|
||
}
|
||
},
|
||
[setMessages]
|
||
);
|
||
|
||
/**
|
||
* 切换到指定会话
|
||
*/
|
||
const switchSession = useCallback(
|
||
(sessionId: string) => {
|
||
setCurrentSessionId(sessionId);
|
||
loadSessionHistory(sessionId);
|
||
},
|
||
[loadSessionHistory]
|
||
);
|
||
|
||
/**
|
||
* 创建新会话(清空消息,显示欢迎消息)
|
||
*/
|
||
const createNewSession = useCallback(() => {
|
||
setCurrentSessionId(null);
|
||
setMessages([
|
||
{
|
||
id: Date.now(),
|
||
type: MessageTypes.AGENT_RESPONSE,
|
||
content: `你好${user?.nickname || ''}!👋\n\n我是**价小前**,你的 AI 投研助手。\n\n**我能做什么?**\n• 📊 全面分析股票基本面和技术面\n• 🔥 追踪市场热点和涨停板块\n• 📈 研究行业趋势和投资机会\n• 📰 汇总最新财经新闻和研报\n\n直接输入你的问题开始探索!`,
|
||
timestamp: new Date().toISOString(),
|
||
},
|
||
]);
|
||
}, [user?.nickname, setMessages]);
|
||
|
||
/**
|
||
* 组件挂载时加载会话列表并创建新会话
|
||
*/
|
||
useEffect(() => {
|
||
loadSessions();
|
||
createNewSession();
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [user]);
|
||
|
||
return {
|
||
sessions,
|
||
currentSessionId,
|
||
setCurrentSessionId,
|
||
isLoadingSessions,
|
||
loadSessions,
|
||
switchSession,
|
||
createNewSession,
|
||
loadSessionHistory,
|
||
};
|
||
};
|