fix: load all thread pages in thread lists (#1044)

* fix(frontend): load all thread pages in thread lists

* Apply suggestions from code review

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
aworki
2026-03-10 23:11:08 +08:00
committed by GitHub
parent d5135ab757
commit 6ae7f0c0ee
2 changed files with 116 additions and 12 deletions

View File

@@ -1,27 +1,84 @@
import fs from "fs";
import path from "path";
export function POST() {
type ThreadSearchRequest = {
limit?: number;
offset?: number;
sortBy?: "updated_at" | "created_at";
sortOrder?: "asc" | "desc";
};
type MockThreadSearchResult = Record<string, unknown> & {
thread_id: string;
updated_at: string | undefined;
};
export async function POST(request: Request) {
const body = ((await request.json().catch(() => ({}))) ?? {}) as ThreadSearchRequest;
const rawLimit = body.limit;
let limit = 50;
if (typeof rawLimit === "number") {
const normalizedLimit = Math.max(0, Math.floor(rawLimit));
if (!Number.isNaN(normalizedLimit)) {
limit = normalizedLimit;
}
}
const rawOffset = body.offset;
let offset = 0;
if (typeof rawOffset === "number") {
const normalizedOffset = Math.max(0, Math.floor(rawOffset));
if (!Number.isNaN(normalizedOffset)) {
offset = normalizedOffset;
}
}
const sortBy = body.sortBy ?? "updated_at";
const sortOrder = body.sortOrder ?? "desc";
const threadsDir = fs.readdirSync(
path.resolve(process.cwd(), "public/demo/threads"),
{
withFileTypes: true,
},
);
const threadData = threadsDir
.map((threadId) => {
.map<MockThreadSearchResult | null>((threadId) => {
if (threadId.isDirectory() && !threadId.name.startsWith(".")) {
const threadData = fs.readFileSync(
path.resolve(`public/demo/threads/${threadId.name}/thread.json`),
"utf8",
);
const threadData = JSON.parse(
fs.readFileSync(
path.resolve(`public/demo/threads/${threadId.name}/thread.json`),
"utf8",
),
) as Record<string, unknown>;
return {
...threadData,
thread_id: threadId.name,
values: JSON.parse(threadData).values,
updated_at:
typeof threadData.updated_at === "string"
? threadData.updated_at
: typeof threadData.created_at === "string"
? threadData.created_at
: undefined,
};
}
return false;
return null;
})
.filter(Boolean);
return Response.json(threadData);
.filter((thread): thread is MockThreadSearchResult => thread !== null)
.sort((a, b) => {
const aTimestamp = a[sortBy];
const bTimestamp = b[sortBy];
const aParsed =
typeof aTimestamp === "string" ? Date.parse(aTimestamp) : 0;
const bParsed =
typeof bTimestamp === "string" ? Date.parse(bTimestamp) : 0;
const aValue = Number.isNaN(aParsed) ? 0 : aParsed;
const bValue = Number.isNaN(bParsed) ? 0 : bParsed;
return sortOrder === "asc" ? aValue - bValue : bValue - aValue;
});
const pagedThreads = threadData.slice(offset, offset + limit);
return Response.json(pagedThreads);
}

View File

@@ -372,8 +372,55 @@ export function useThreads(
return useQuery<AgentThread[]>({
queryKey: ["threads", "search", params],
queryFn: async () => {
const response = await apiClient.threads.search<AgentThreadState>(params);
return response as AgentThread[];
const maxResults = params.limit;
const initialOffset = params.offset ?? 0;
const DEFAULT_PAGE_SIZE = 50;
// Preserve prior semantics: if a non-positive limit is explicitly provided,
// delegate to a single search call with the original parameters.
if (maxResults !== undefined && maxResults <= 0) {
const response = await apiClient.threads.search<AgentThreadState>(params);
return response as AgentThread[];
}
const pageSize =
typeof maxResults === "number" && maxResults > 0
? Math.min(DEFAULT_PAGE_SIZE, maxResults)
: DEFAULT_PAGE_SIZE;
const threads: AgentThread[] = [];
let offset = initialOffset;
while (true) {
if (typeof maxResults === "number" && threads.length >= maxResults) {
break;
}
const currentLimit =
typeof maxResults === "number"
? Math.min(pageSize, maxResults - threads.length)
: pageSize;
if (typeof maxResults === "number" && currentLimit <= 0) {
break;
}
const response = (await apiClient.threads.search<AgentThreadState>({
...params,
limit: currentLimit,
offset,
})) as AgentThread[];
threads.push(...response);
if (response.length < currentLimit) {
break;
}
offset += response.length;
}
return threads;
},
refetchOnWindowFocus: false,
});