JeffJiang 3a99c4e81c
feat: enhance chat history loading with new hooks and UI components (#2338)
* Refactor API fetch calls to use a unified fetch function; enhance chat history loading with new hooks and UI components

- Replaced `fetchWithAuth` with a generic `fetch` function across various API modules for consistency.
- Updated `useThreadStream` and `useThreadHistory` hooks to manage chat history loading, including loading states and pagination.
- Introduced `LoadMoreHistoryIndicator` component for better user experience when loading more chat history.
- Enhanced message handling in `MessageList` to accommodate new loading states and history management.
- Added support for run messages in the thread context, improving the overall message handling logic.
- Updated translations for loading indicators in English and Chinese.

* Fix test assertions for run ordering in RunManager tests

- Updated assertions in `test_list_by_thread` to reflect correct ordering of runs.
- Modified `test_list_by_thread_is_stable_when_timestamps_tie` to ensure stable ordering when timestamps are tied.
2026-04-19 10:23:09 +08:00

108 lines
2.2 KiB
TypeScript

/**
* API functions for file uploads
*/
import { fetch } from "../api/fetcher";
import { getBackendBaseURL } from "../config";
export interface UploadedFileInfo {
filename: string;
size: number;
path: string;
virtual_path: string;
artifact_url: string;
extension?: string;
modified?: number;
markdown_file?: string;
markdown_path?: string;
markdown_virtual_path?: string;
markdown_artifact_url?: string;
}
export interface UploadResponse {
success: boolean;
files: UploadedFileInfo[];
message: string;
}
export interface ListFilesResponse {
files: UploadedFileInfo[];
count: number;
}
async function readErrorDetail(
response: Response,
fallback: string,
): Promise<string> {
const error = await response.json().catch(() => ({ detail: fallback }));
return error.detail ?? fallback;
}
/**
* Upload files to a thread
*/
export async function uploadFiles(
threadId: string,
files: File[],
): Promise<UploadResponse> {
const formData = new FormData();
files.forEach((file) => {
formData.append("files", file);
});
const response = await fetch(
`${getBackendBaseURL()}/api/threads/${threadId}/uploads`,
{
method: "POST",
body: formData,
},
);
if (!response.ok) {
throw new Error(await readErrorDetail(response, "Upload failed"));
}
return response.json();
}
/**
* List all uploaded files for a thread
*/
export async function listUploadedFiles(
threadId: string,
): Promise<ListFilesResponse> {
const response = await fetch(
`${getBackendBaseURL()}/api/threads/${threadId}/uploads/list`,
);
if (!response.ok) {
throw new Error(
await readErrorDetail(response, "Failed to list uploaded files"),
);
}
return response.json();
}
/**
* Delete an uploaded file
*/
export async function deleteUploadedFile(
threadId: string,
filename: string,
): Promise<{ success: boolean; message: string }> {
const response = await fetch(
`${getBackendBaseURL()}/api/threads/${threadId}/uploads/${filename}`,
{
method: "DELETE",
},
);
if (!response.ok) {
throw new Error(await readErrorDetail(response, "Failed to delete file"));
}
return response.json();
}