refactor: migrate from legacy session API to new rooms API for multi-agent mention support
parent
b6d1c9e08d
commit
8a85c675ef
|
|
@ -42,21 +42,20 @@ export interface ChatHistoryResp {
|
|||
}
|
||||
|
||||
export const ChatAPI = {
|
||||
history: (agentId: string, sessionId?: string) =>
|
||||
api.get<ChatHistoryResp>(`/chat/${agentId}/messages`, { params: sessionId ? { sessionId } : {} }).then((r) => r.data),
|
||||
send: (agentId: string, content: string, sessionId?: string, model?: string, imageUrls?: string[]) =>
|
||||
history: (roomId: string) =>
|
||||
api.get<ChatHistoryResp>(`/rooms/${roomId}/messages`).then((r) => r.data),
|
||||
send: (roomId: string, content: string, targetAgentId: string, model?: string, model_id?: string, imageUrls?: string[]) =>
|
||||
api
|
||||
.post<{ user: ChatMessage; assistant: ChatMessage }>(`/chat/${agentId}/messages`, {
|
||||
.post<{ user: ChatMessage; assistant: ChatMessage }>(`/rooms/${roomId}/messages`, {
|
||||
content,
|
||||
sessionId,
|
||||
targetAgentId,
|
||||
model,
|
||||
model_id,
|
||||
imageUrls
|
||||
})
|
||||
.then((r) => r.data),
|
||||
clear: (agentId: string, sessionId?: string) =>
|
||||
api.delete(`/chat/${agentId}/messages`, { params: sessionId ? { sessionId } : {} }).then((r) => r.data),
|
||||
switchBranch: (agentId: string, userMsgId: string, branchId: string) =>
|
||||
api.post(`/chat/${agentId}/messages/${userMsgId}/switch-branch`, { branchId }).then((r) => r.data)
|
||||
clear: (roomId: string) =>
|
||||
api.delete(`/rooms/${roomId}/messages`).then((r) => r.data)
|
||||
};
|
||||
|
||||
export interface ChatAttachment {
|
||||
|
|
|
|||
|
|
@ -30,18 +30,18 @@ export interface SearchResult {
|
|||
|
||||
export const SessionAPI = {
|
||||
list: (agentId: string, archived: '0' | '1' | 'all' = '0') =>
|
||||
api.get<ChatSession[]>(`/agents/${agentId}/sessions`, { params: { archived } }).then((r) => r.data),
|
||||
create: (agentId: string, title?: string) => api.post<ChatSession>(`/agents/${agentId}/sessions`, { title }).then((r) => r.data),
|
||||
rename: (agentId: string, sessionId: string, title: string) => api.put(`/agents/${agentId}/sessions/${sessionId}`, { title }).then((r) => r.data),
|
||||
remove: (agentId: string, sessionId: string) => api.delete(`/agents/${agentId}/sessions/${sessionId}`).then((r) => r.data),
|
||||
api.get<ChatSession[]>(`/agents/${agentId}/rooms`, { params: { archived } }).then((r) => r.data),
|
||||
create: (agentId: string, title?: string) => api.post<ChatSession>(`/agents/${agentId}/rooms`, { title: title || '新会话' }).then((r) => r.data),
|
||||
rename: (agentId: string, sessionId: string, title: string) => api.put(`/agents/${agentId}/rooms/${sessionId}`, { title }).then((r) => r.data),
|
||||
remove: (agentId: string, sessionId: string) => api.delete(`/agents/${agentId}/rooms/${sessionId}`).then((r) => r.data),
|
||||
archive: (agentId: string, sessionId: string, archived: boolean) =>
|
||||
api.post(`/agents/${agentId}/sessions/${sessionId}/archive`, { archived }).then((r) => r.data),
|
||||
api.post(`/agents/${agentId}/rooms/${sessionId}/archive`, { archived }).then((r) => r.data),
|
||||
share: (agentId: string, sessionId: string, ttlHours = 0) =>
|
||||
api.post<{ token: string; expiresAt?: number }>(`/agents/${agentId}/sessions/${sessionId}/share`, { ttlHours }).then((r) => r.data),
|
||||
revokeShare: (agentId: string, sessionId: string) => api.delete(`/agents/${agentId}/sessions/${sessionId}/share`).then((r) => r.data),
|
||||
api.post<{ token: string; expiresAt?: number }>(`/agents/${agentId}/rooms/${sessionId}/share`, { ttlHours }).then((r) => r.data),
|
||||
revokeShare: (agentId: string, sessionId: string) => api.delete(`/agents/${agentId}/rooms/${sessionId}/share`).then((r) => r.data),
|
||||
search: (agentId: string, q: string, opts: { includeArchived?: boolean; limit?: number } = {}) =>
|
||||
api
|
||||
.get<SearchResult>(`/agents/${agentId}/sessions/search`, {
|
||||
.get<SearchResult>(`/agents/${agentId}/rooms/search`, {
|
||||
params: {
|
||||
q,
|
||||
includeArchived: opts.includeArchived ? '1' : '0',
|
||||
|
|
@ -50,7 +50,7 @@ export const SessionAPI = {
|
|||
})
|
||||
.then((r) => r.data),
|
||||
exportSession: (agentId: string, sessionId: string, format: 'md' | 'json' = 'md') => {
|
||||
const url = withApiBase(`/agents/${agentId}/sessions/${sessionId}/export?format=${format}`);
|
||||
const url = withApiBase(`/agents/${agentId}/rooms/${sessionId}/export?format=${format}`);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.rel = 'noopener';
|
||||
|
|
|
|||
|
|
@ -21,21 +21,21 @@ export interface ModelOverrides {
|
|||
}
|
||||
|
||||
export async function streamChat(
|
||||
agentId: string,
|
||||
roomId: string,
|
||||
targetAgentId: string,
|
||||
content: string,
|
||||
handlers: StreamEvents,
|
||||
signal?: AbortSignal,
|
||||
sessionId?: string,
|
||||
model?: string,
|
||||
modelId?: string,
|
||||
imageUrls?: string[]
|
||||
) {
|
||||
const resp = await fetch(`https://api.hoyidata.com/aura/v1/chat/${agentId}/messages/stream`, {
|
||||
const resp = await fetch(`https://api.hoyidata.com/aura/v1/rooms/${roomId}/messages/stream`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Accept: 'text/event-stream' },
|
||||
body: JSON.stringify({
|
||||
content,
|
||||
sessionId,
|
||||
targetAgentId,
|
||||
model,
|
||||
model_id: modelId,
|
||||
imageUrls: imageUrls ?? []
|
||||
|
|
|
|||
|
|
@ -75,8 +75,11 @@ export default function ChatPreview({ agent, agentId }: Props) {
|
|||
setSessionId(sid);
|
||||
}
|
||||
const model = String(agent?.model || '').split(',')[0]?.trim() || undefined;
|
||||
const modelId = model ? undefined : undefined;
|
||||
const targetAgentId = agentId;
|
||||
await streamChat(
|
||||
agentId,
|
||||
sid,
|
||||
targetAgentId,
|
||||
text,
|
||||
{
|
||||
onMeta: (m) => setStreaming((s) => ({ ...s, retrieved: m.retrieved || [] })),
|
||||
|
|
@ -104,7 +107,7 @@ export default function ChatPreview({ agent, agentId }: Props) {
|
|||
}),
|
||||
onDone: (data) => {
|
||||
setMessages((m) => [...m.filter((x) => x.id !== tempUser.id), data.user, data.assistant]);
|
||||
setStreaming({ active: false, text: '', retrieved: [], toolCalls: [] });
|
||||
setStreaming({ active: false, text: '', retrieved: [], toolCalls: [] });
|
||||
scrollBottom();
|
||||
},
|
||||
onError: (errMsg) => {
|
||||
|
|
@ -114,8 +117,8 @@ export default function ChatPreview({ agent, agentId }: Props) {
|
|||
}
|
||||
},
|
||||
ctrl.signal,
|
||||
sid,
|
||||
model
|
||||
model,
|
||||
modelId
|
||||
);
|
||||
} catch (e: any) {
|
||||
if (e?.name !== 'AbortError') {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import { useChatData } from './hooks/useChatData';
|
|||
import { useChatSender } from './hooks/useChatSender';
|
||||
import { markdownToPlainText } from './utils/copy';
|
||||
|
||||
const lastSessionKey = (agentId: string) => `chat:lastSession:${agentId}`;
|
||||
const lastRoomKey = (agentId: string) => `chat:lastRoom:${agentId}`;
|
||||
|
||||
export default function ChatPage() {
|
||||
const { id } = useParams();
|
||||
|
|
@ -22,7 +22,7 @@ export default function ChatPage() {
|
|||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const { message } = AntApp.useApp();
|
||||
|
||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||
const [roomId, setRoomId] = useState<string | null>(null);
|
||||
const [highlightId, setHighlightId] = useState<string | null>(null);
|
||||
const [overrides, setOverrides] = useState<ModelOverrides>({});
|
||||
|
||||
|
|
@ -41,9 +41,9 @@ export default function ChatPage() {
|
|||
const m = searchParams.get('msg');
|
||||
|
||||
if (s) {
|
||||
setSessionId(s);
|
||||
setRoomId(s);
|
||||
try {
|
||||
localStorage.setItem(lastSessionKey(id), s);
|
||||
localStorage.setItem(lastRoomKey(id), s);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
|
@ -58,13 +58,13 @@ export default function ChatPage() {
|
|||
|
||||
const saved = (() => {
|
||||
try {
|
||||
return localStorage.getItem(lastSessionKey(id));
|
||||
return localStorage.getItem(lastRoomKey(id));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
if (saved) {
|
||||
setSessionId(saved);
|
||||
setRoomId(saved);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -72,37 +72,37 @@ export default function ChatPage() {
|
|||
try {
|
||||
const list = await SessionAPI.list(id, '0');
|
||||
if (list.length > 0) {
|
||||
setSessionId(list[0].id);
|
||||
setRoomId(list[0].id);
|
||||
return;
|
||||
}
|
||||
const created = await SessionAPI.create(id);
|
||||
setSessionId(created.id);
|
||||
setRoomId(created.id);
|
||||
} catch (e: any) {
|
||||
message.error('加载会话失败:' + (e?.message ?? e));
|
||||
message.error('加载房间失败:' + (e?.message ?? e));
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id || !sessionId) return;
|
||||
if (!id || !roomId) return;
|
||||
try {
|
||||
localStorage.setItem(lastSessionKey(id), sessionId);
|
||||
localStorage.setItem(lastRoomKey(id), roomId);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
const cur = searchParams.get('session');
|
||||
if (cur === sessionId) return;
|
||||
if (cur === roomId) return;
|
||||
const next = new URLSearchParams(searchParams);
|
||||
next.set('session', sessionId);
|
||||
next.set('session', roomId);
|
||||
next.delete('msg');
|
||||
setSearchParams(next, { replace: true });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [id, sessionId]);
|
||||
}, [id, roomId]);
|
||||
|
||||
const { agent, agentList, messages, setMessages, branches, setBranches, loadMessages } = useChatData({
|
||||
agentId: id,
|
||||
sessionId,
|
||||
roomId,
|
||||
highlightId,
|
||||
setHighlightId,
|
||||
scrollBottom,
|
||||
|
|
@ -115,7 +115,7 @@ export default function ChatPage() {
|
|||
agentId: id,
|
||||
agent,
|
||||
agentList,
|
||||
sessionId,
|
||||
roomId,
|
||||
overrides,
|
||||
setOverrides: (updater) => setOverrides(updater),
|
||||
messages,
|
||||
|
|
@ -213,9 +213,9 @@ export default function ChatPage() {
|
|||
setHighlightId(null);
|
||||
setMessages(() => []);
|
||||
setBranches({});
|
||||
setSessionId(created.id);
|
||||
setRoomId(created.id);
|
||||
} catch (e: any) {
|
||||
message.error('创建会话失败:' + (e?.message ?? e));
|
||||
message.error('创建房间失败:' + (e?.message ?? e));
|
||||
}
|
||||
}}
|
||||
agentList={agentList}
|
||||
|
|
@ -232,8 +232,8 @@ export default function ChatPage() {
|
|||
setInput={(updater) => sender.setInput(updater(sender.input))}
|
||||
overrides={overrides}
|
||||
setOverrides={(updater) => setOverrides(updater)}
|
||||
sessionId={sessionId}
|
||||
setSessionId={setSessionId}
|
||||
roomId={roomId}
|
||||
setRoomId={setRoomId}
|
||||
setHighlightId={setHighlightId}
|
||||
sessionRefresh={sender.sessionRefresh}
|
||||
mcpDrawerOpen={mcpDrawerOpen}
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ export default function ChatDrawers(props: {
|
|||
setInput: (updater: (prev: string) => string) => void;
|
||||
overrides: ModelOverrides;
|
||||
setOverrides: (updater: (prev: ModelOverrides) => ModelOverrides) => void;
|
||||
sessionId: string | null;
|
||||
setSessionId: (v: string | null) => void;
|
||||
roomId: string | null;
|
||||
setRoomId: (v: string | null) => void;
|
||||
setHighlightId: (v: string | null) => void;
|
||||
sessionRefresh: number;
|
||||
mcpDrawerOpen: boolean;
|
||||
|
|
@ -31,8 +31,8 @@ export default function ChatDrawers(props: {
|
|||
setInput,
|
||||
overrides,
|
||||
setOverrides,
|
||||
sessionId,
|
||||
setSessionId,
|
||||
roomId,
|
||||
setRoomId,
|
||||
setHighlightId,
|
||||
sessionRefresh,
|
||||
mcpDrawerOpen,
|
||||
|
|
@ -165,9 +165,9 @@ export default function ChatDrawers(props: {
|
|||
{agentId ? (
|
||||
<SessionSidebar
|
||||
agentId={agentId}
|
||||
activeSessionId={sessionId}
|
||||
activeSessionId={roomId}
|
||||
onChange={(sid, opts) => {
|
||||
setSessionId(sid);
|
||||
setRoomId(sid);
|
||||
setHighlightId(opts?.highlightMessageId || null);
|
||||
setHistoryDrawerOpen(false);
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { parseAgentModels } from '../utils/agentModels';
|
|||
|
||||
export function useChatData(args: {
|
||||
agentId?: string;
|
||||
sessionId: string | null;
|
||||
roomId: string | null;
|
||||
highlightId: string | null;
|
||||
setHighlightId: (v: string | null) => void;
|
||||
scrollBottom: (force?: boolean) => void;
|
||||
|
|
@ -13,7 +13,7 @@ export function useChatData(args: {
|
|||
setOverrides: (updater: (prev: ModelOverrides) => ModelOverrides) => void;
|
||||
abort: () => void;
|
||||
}) {
|
||||
const { agentId, sessionId, highlightId, setHighlightId, scrollBottom, initialScrollDoneRef, setOverrides, abort } = args;
|
||||
const { agentId, roomId, highlightId, setHighlightId, scrollBottom, initialScrollDoneRef, setOverrides, abort } = args;
|
||||
const [agent, setAgent] = useState<Agent | null>(null);
|
||||
const [agentList, setAgentList] = useState<Agent[]>([]);
|
||||
const [messages, setMessages] = useState<ChatMessage[]>([]);
|
||||
|
|
@ -48,8 +48,8 @@ export function useChatData(args: {
|
|||
};
|
||||
|
||||
const loadMessages = async () => {
|
||||
if (!agentId || !sessionId) return;
|
||||
const his = await ChatAPI.history(agentId, sessionId || undefined);
|
||||
if (!roomId) return;
|
||||
const his = await ChatAPI.history(roomId);
|
||||
setMessages(Array.isArray(his.messages) ? his.messages : []);
|
||||
setBranches(his.branches || {});
|
||||
requestAnimationFrame(() => {
|
||||
|
|
@ -92,10 +92,10 @@ export function useChatData(args: {
|
|||
}, [agentId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!agentId || !sessionId) return;
|
||||
if (!roomId) return;
|
||||
loadMessages();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sessionId, highlightId, agentId]);
|
||||
}, [roomId, highlightId, agentId]);
|
||||
|
||||
return {
|
||||
agent,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export function useChatSender(args: {
|
|||
agentId?: string;
|
||||
agent: Agent | null;
|
||||
agentList: Agent[];
|
||||
sessionId: string | null;
|
||||
roomId: string | null;
|
||||
overrides: ModelOverrides;
|
||||
setOverrides: (updater: (prev: ModelOverrides) => ModelOverrides) => void;
|
||||
messages: ChatMessage[];
|
||||
|
|
@ -95,7 +95,7 @@ export function useChatSender(args: {
|
|||
notify: { success: (t: string) => void; error: (t: string) => void };
|
||||
abortRef: { current: AbortController | null };
|
||||
}) {
|
||||
const { agentId, agent, agentList, sessionId, overrides, setOverrides, setBranches, loadMessages, scrollBottom, notify, abortRef } = args;
|
||||
const { agentId, agent, agentList, roomId, overrides, setOverrides, setBranches, loadMessages, scrollBottom, notify, abortRef } = args;
|
||||
const [input, setInput] = useState('');
|
||||
const [sending, setSending] = useState(false);
|
||||
const [useStream, setUseStream] = useState(true);
|
||||
|
|
@ -153,7 +153,12 @@ export function useChatSender(args: {
|
|||
const content = attText ? `${text}\n\n${attText}` : text;
|
||||
|
||||
try {
|
||||
if (!roomId) {
|
||||
notify.error('房间未初始化');
|
||||
return;
|
||||
}
|
||||
await streamChat(
|
||||
roomId,
|
||||
targetAgentId,
|
||||
content,
|
||||
{
|
||||
|
|
@ -230,7 +235,6 @@ export function useChatSender(args: {
|
|||
}
|
||||
},
|
||||
ctrl.signal,
|
||||
sessionId,
|
||||
targetModel,
|
||||
targetModelId,
|
||||
imageUrls
|
||||
|
|
@ -253,8 +257,8 @@ export function useChatSender(args: {
|
|||
|
||||
const handleSendNonStream = async (text: string) => {
|
||||
if (!agentId) return;
|
||||
if (!sessionId) {
|
||||
notify.error('会话未初始化,请稍后重试');
|
||||
if (!roomId) {
|
||||
notify.error('房间未初始化,请稍后重试');
|
||||
return;
|
||||
}
|
||||
const tempUser: ChatMessage = { id: 'tmp-' + Date.now(), role: 'user', content: text, createdAt: Date.now() };
|
||||
|
|
@ -267,17 +271,20 @@ export function useChatSender(args: {
|
|||
|
||||
// 如果提及了其他 Agent,使用该 Agent 的第一个模型
|
||||
let targetModel: string;
|
||||
let targetModelId: string;
|
||||
if (targetAgent && targetAgent.id !== agentId) {
|
||||
const models = parseAgentModels(targetAgent.model);
|
||||
targetModel = models[0]?.name || '';
|
||||
targetModelId = models[0]?.id || '';
|
||||
} else {
|
||||
targetModel = overrides.model || agentModels[0]?.name || '';
|
||||
targetModelId = overrides.model_id || agentModels[0]?.id || '';
|
||||
}
|
||||
|
||||
const attText = buildAttachmentsText(attachments);
|
||||
const content = attText ? `${text}\n\n${attText}` : text;
|
||||
try {
|
||||
const res = await ChatAPI.send(targetAgentId, content, sessionId, targetModel, imageUrls);
|
||||
const res = await ChatAPI.send(roomId, content, targetAgentId, targetModel, targetModelId, imageUrls);
|
||||
args.setMessages((m) => [...(m || []).filter((x) => x.id !== tempUser.id), res.user, res.assistant]);
|
||||
setSessionRefresh((t) => t + 1);
|
||||
setAttachments([]);
|
||||
|
|
@ -309,11 +316,11 @@ export function useChatSender(args: {
|
|||
|
||||
const handleClear = async () => {
|
||||
if (!agentId) return;
|
||||
if (!sessionId) {
|
||||
notify.error('会话未初始化,请稍后重试');
|
||||
if (!roomId) {
|
||||
notify.error('房间未初始化,请稍后重试');
|
||||
return;
|
||||
}
|
||||
await ChatAPI.clear(agentId, sessionId);
|
||||
await ChatAPI.clear(roomId);
|
||||
args.setMessages(() => []);
|
||||
setBranches({});
|
||||
notify.success('对话已清空');
|
||||
|
|
|
|||
Loading…
Reference in New Issue