Refactor AI chat: streaming, tool calls, UI polish

This commit is contained in:
2026-07-24 16:27:50 +08:00
parent e605586fc9
commit 8656394b9b
55 changed files with 2774 additions and 460 deletions

View File

@@ -1,15 +1,48 @@
import api from '../../api';
import type { AiApiResponse, AiConversation, AiMessagePage } from './types';
import type {
AiApiResponse,
AiAttachment,
AiConversation,
AiMessageFeedback,
AiMessagePage,
AiSkill,
} from './types';
const basePath = '/ai/chat/conversations';
export const aiChatApi = {
listConversations: async () => (await api.get<AiApiResponse<AiConversation[]>>(basePath)).data,
createConversation: async (title?: string) =>
(await api.post<AiApiResponse<AiConversation>>(basePath, title ? { title } : {})).data,
renameConversation: async (id: number, title: string) =>
(await api.patch<AiApiResponse<AiConversation>>(`${basePath}/${id}`, { title })).data,
listSkills: async () => (await api.get<AiApiResponse<AiSkill[]>>('/ai/chat/skills')).data,
listConversations: async () =>
(await api.get<AiApiResponse<AiConversation[]>>(basePath)).data,
createConversation: async (input?: { title?: string; lockedSkillKey?: string | null }) =>
(await api.post<AiApiResponse<AiConversation>>(basePath, input ?? {})).data,
updateConversation: async (
id: number,
input: { title?: string; lockedSkillKey?: string | null },
) => (await api.patch<AiApiResponse<AiConversation>>(`${basePath}/${id}`, input)).data,
deleteConversation: (id: number) => api.delete<void>(`${basePath}/${id}`),
uploadAttachment: async (file: File): Promise<AiAttachment> => {
const form = new FormData();
form.append('file', file);
return (
await api.post<AiApiResponse<AiAttachment>>('/ai/chat/attachments', form, {
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 120_000,
})
).data;
},
deleteAttachment: (id: number) => api.delete<void>(`/ai/chat/attachments/${id}`),
setFeedback: async (
messageId: number,
feedback: AiMessageFeedback,
reason?: string,
) =>
(
await api.patch<AiApiResponse<{ id: number; feedback: AiMessageFeedback }>>(
`/ai/chat/messages/${messageId}/feedback`,
{ feedback, reason },
)
).data,
listMessages: async (id: number): Promise<AiMessagePage> => {
const first = (
await api.get<AiApiResponse<AiMessagePage>>(`${basePath}/${id}/messages`, {
@@ -34,3 +67,7 @@ export const aiChatApi = {
export function conversationStreamUrl(id: number): string {
return `/api${basePath}/${id}/stream`;
}
export function regenerateStreamUrl(conversationId: number, messageId: number): string {
return `/api${basePath}/${conversationId}/messages/${messageId}/regenerate/stream`;
}