Files
deerflow-code/frontend-web/src/core/admin/hooks.ts
T
AdministratorandClaude Opus 4.8 e7c745a512 feat: 排行榜日快照分析 + 数据库连接池/索引调优
- 排行榜改为按天预计算快照(admin_leaderboard_daily_stats),请求只读取合并,重统计移至后台调度;过去日不可变、当天 TTL 刷新,避免在线聚合长时间占用数据库
- 为 runs/tool_call_metrics/llm_call_metrics/threads_meta 增加面向时间段+用户聚合的复合索引(model __table_args__ + migration 20260608_01)
- 连接池可调:DatabaseConfig 新增 max_overflow / pool_timeout,接入 postgres/mysql 引擎;config.yaml 上调 pool_size=20/max_overflow=20
- 新增手动执行的索引脚本 scripts/sql/leaderboard_indexes_mysql.sql(幂等,适用于已存在数据、create_all 不补索引且 alembic 关闭的部署)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:52:24 +08:00

205 lines
5.6 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import {
addAdminMemoryEntry,
addAdminV1Fact,
deleteAdminMemoryEntry,
deleteAdminV1Fact,
generateUserLeaderboardReport,
getUserMemory,
getUserAnalytics,
getUserLeaderboard,
listUserQa,
listUserThreadMessages,
listUserThreads,
replaceAdminMemoryEntry,
updateAdminV1Fact,
updateAdminV1Section,
} from "./api";
export function useUserLeaderboard(
range: {
since: string;
until: string;
days?: number;
},
limit = 20,
) {
return useQuery({
queryKey: [
"admin-activity",
"leaderboard",
range.since,
range.until,
limit,
],
queryFn: () => getUserLeaderboard(range, limit),
refetchInterval: (query) => {
const status = query.state.data?.status;
return status === "running" || status === "refreshing" ? 3000 : false;
},
});
}
export function useUserAnalytics(
userId: string | null,
range: { since: string; until: string; days?: number },
limit = 20,
) {
return useQuery({
queryKey: ["admin-activity", "analytics", userId, range.since, range.until, limit],
queryFn: () => getUserAnalytics(userId!, range, limit),
enabled: !!userId,
});
}
export function useGenerateUserLeaderboardReport() {
return useMutation({
mutationFn: ({
range,
limit,
modelName,
}: {
range: { since: string; until: string; days?: number };
limit?: number;
modelName?: string;
}) => generateUserLeaderboardReport(range, limit ?? 30, modelName),
});
}
/** Threads owned by a given user. Disabled until a user is picked. */
export function useUserThreads(userId: string | null) {
return useQuery({
queryKey: ["admin-activity", "threads", userId],
queryFn: () => listUserThreads(userId!),
enabled: !!userId,
});
}
export function useUserQa(
userId: string | null,
params: { page: number; pageSize: number; q?: string },
) {
return useQuery({
queryKey: ["admin-activity", "qa", userId, params.page, params.pageSize, params.q ?? ""],
queryFn: () => listUserQa(userId!, params),
enabled: !!userId,
});
}
/** Messages of a given thread. Disabled until a thread is picked. */
export function useUserThreadMessages(
userId: string | null,
threadId: string | null,
) {
return useQuery({
queryKey: ["admin-activity", "messages", userId, threadId],
queryFn: () => listUserThreadMessages(userId!, threadId!),
enabled: !!userId && !!threadId,
});
}
/** All memory buckets of a given user. Disabled until a user is picked. */
export function useUserMemory(userId: string | null) {
return useQuery({
queryKey: ["admin-activity", "memory", userId],
queryFn: () => getUserMemory(userId!),
enabled: !!userId,
});
}
function useMemoryInvalidate(userId: string) {
const qc = useQueryClient();
return () => qc.invalidateQueries({ queryKey: ["admin-activity", "memory", userId] });
}
export function useAdminAddMemoryEntry(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({ content, agent }: { content: string; agent: string | null }) =>
addAdminMemoryEntry(userId, content, agent),
onSuccess: invalidate,
});
}
export function useAdminReplaceMemoryEntry(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({ oldText, content, agent }: { oldText: string; content: string; agent: string | null }) =>
replaceAdminMemoryEntry(userId, oldText, content, agent),
onSuccess: invalidate,
});
}
export function useAdminDeleteMemoryEntry(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({ oldText, agent }: { oldText: string; agent: string | null }) =>
deleteAdminMemoryEntry(userId, oldText, agent),
onSuccess: invalidate,
});
}
// ── V1 CRUD hooks ──────────────────────────────────────────────────────────
export function useAdminUpdateV1Section(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({
sectionType,
field,
summary,
agent,
}: {
sectionType: "user" | "history";
field: string;
summary: string;
agent: string | null;
}) => updateAdminV1Section(userId, sectionType, field, summary, agent),
onSuccess: invalidate,
});
}
export function useAdminAddV1Fact(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({
content,
category,
confidence,
agent,
}: {
content: string;
category: string;
confidence: number;
agent: string | null;
}) => addAdminV1Fact(userId, content, category, confidence, agent),
onSuccess: invalidate,
});
}
export function useAdminUpdateV1Fact(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({
factId,
updates,
agent,
}: {
factId: string;
updates: { content?: string; category?: string; confidence?: number };
agent: string | null;
}) => updateAdminV1Fact(userId, factId, updates, agent),
onSuccess: invalidate,
});
}
export function useAdminDeleteV1Fact(userId: string) {
const invalidate = useMemoryInvalidate(userId);
return useMutation({
mutationFn: ({ factId, agent }: { factId: string; agent: string | null }) =>
deleteAdminV1Fact(userId, factId, agent),
onSuccess: invalidate,
});
}