- 排行榜改为按天预计算快照(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>
205 lines
5.6 KiB
TypeScript
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,
|
|
});
|
|
}
|