mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-10 15:03:36 +00:00
Key alias filter on logs (#10905)
* key hash filter added * key alias filter added
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import moment from "moment";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useState, useRef, useEffect } from "react";
|
||||
import { useState, useRef, useEffect, useCallback } from "react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
|
||||
import { uiSpendLogsCall, keyInfoV1Call, sessionSpendLogsCall } from "../networking";
|
||||
import { uiSpendLogsCall, keyInfoV1Call, sessionSpendLogsCall, keyListCall } from "../networking";
|
||||
import { DataTable } from "./table";
|
||||
import { columns, LogEntry } from "./columns";
|
||||
import { Row } from "@tanstack/react-table";
|
||||
@@ -21,6 +21,7 @@ import { GuardrailViewer } from './GuardrailViewer';
|
||||
import FilterComponent from "../common_components/filter";
|
||||
import { FilterOption } from "../common_components/filter";
|
||||
import { useLogFilterLogic } from "./log_filter_logic";
|
||||
import { fetchAllKeyAliases } from "../key_team_helpers/filter_helpers";
|
||||
|
||||
interface SpendLogsTableProps {
|
||||
accessToken: string | null;
|
||||
@@ -154,7 +155,6 @@ export default function SpendLogsTable({
|
||||
],
|
||||
queryFn: async () => {
|
||||
if (!accessToken || !token || !userRole || !userID) {
|
||||
console.log("Missing required auth parameters");
|
||||
return {
|
||||
data: [],
|
||||
total: 0,
|
||||
@@ -241,18 +241,53 @@ export default function SpendLogsTable({
|
||||
userRole
|
||||
})
|
||||
|
||||
// Add this effect to update selectedTeamId and selectedStatus when team filter changes
|
||||
const fetchKeyHashForAlias = useCallback(async (keyAlias: string) => {
|
||||
if (!accessToken) return;
|
||||
|
||||
try {
|
||||
const response = await keyListCall(
|
||||
accessToken,
|
||||
null,
|
||||
null,
|
||||
keyAlias,
|
||||
null,
|
||||
null,
|
||||
currentPage,
|
||||
pageSize
|
||||
);
|
||||
|
||||
const selectedKey = response.keys.find(
|
||||
(key: any) => key.key_alias === keyAlias
|
||||
);
|
||||
|
||||
if (selectedKey) {
|
||||
setSelectedKeyHash(selectedKey.token);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching key hash for alias:", error);
|
||||
}
|
||||
}, [accessToken, currentPage, pageSize]);
|
||||
|
||||
// Add this effect to update selected filters when filter changes
|
||||
useEffect(() => {
|
||||
if(!accessToken) return;
|
||||
|
||||
if (filters['Team ID']) {
|
||||
setSelectedTeamId(filters['Team ID']);
|
||||
|
||||
} else {
|
||||
setSelectedTeamId("");
|
||||
}
|
||||
setSelectedStatus(filters['Status'] || "");
|
||||
setSelectedModel(filters['Model'] || "");
|
||||
setSelectedKeyHash(filters['Key Hash'] || "");
|
||||
}, [filters]);
|
||||
|
||||
if (filters['Key Hash']) {
|
||||
setSelectedKeyHash(filters['Key Hash']);
|
||||
} else if (filters['Key Alias']) {
|
||||
fetchKeyHashForAlias(filters['Key Alias']);
|
||||
} else {
|
||||
setSelectedKeyHash("");
|
||||
}
|
||||
}, [filters, accessToken, fetchKeyHashForAlias]);
|
||||
|
||||
// Fetch logs for a session if selected
|
||||
const sessionLogs = useQuery<PaginatedResponse>({
|
||||
@@ -354,7 +389,7 @@ export default function SpendLogsTable({
|
||||
const filtered = allTeams.filter((team: Team) =>{
|
||||
return team.team_id.toLowerCase().includes(searchText.toLowerCase()) ||
|
||||
(team.team_alias && team.team_alias.toLowerCase().includes(searchText.toLowerCase()))
|
||||
});
|
||||
});
|
||||
return filtered.map((team: Team) => ({
|
||||
label: `${team.team_alias || team.team_id} (${team.team_id})`,
|
||||
value: team.team_id
|
||||
@@ -385,6 +420,22 @@ export default function SpendLogsTable({
|
||||
}));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Key Alias',
|
||||
label: 'Key Alias',
|
||||
isSearchable: true,
|
||||
searchFn: async (searchText: string) => {
|
||||
if (!accessToken) return [];
|
||||
const keyAliases = await fetchAllKeyAliases(accessToken);
|
||||
const filtered = keyAliases.filter(alias =>
|
||||
alias.toLowerCase().includes(searchText.toLowerCase())
|
||||
);
|
||||
return filtered.map(alias => ({
|
||||
label: alias,
|
||||
value: alias
|
||||
}));
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Key Hash',
|
||||
label: 'Key Hash',
|
||||
|
||||
@@ -15,6 +15,7 @@ export const FILTER_KEYS = {
|
||||
MODEL: "Model",
|
||||
USER_ID: "User ID",
|
||||
STATUS: "Status",
|
||||
KEY_ALIAS: "Key Alias",
|
||||
} as const;
|
||||
|
||||
export type FilterKey = keyof typeof FILTER_KEYS;
|
||||
@@ -47,7 +48,8 @@ export function useLogFilterLogic({
|
||||
[FILTER_KEYS.REQUEST_ID]: "",
|
||||
[FILTER_KEYS.MODEL]: "",
|
||||
[FILTER_KEYS.USER_ID]: "",
|
||||
[FILTER_KEYS.STATUS]: ""
|
||||
[FILTER_KEYS.STATUS]: "",
|
||||
[FILTER_KEYS.KEY_ALIAS]: ""
|
||||
}), []);
|
||||
|
||||
const [filters, setFilters] = useState<LogFilterState>(defaultFilters);
|
||||
@@ -96,6 +98,16 @@ export function useLogFilterLogic({
|
||||
return () => debouncedSearch.cancel();
|
||||
}, [debouncedSearch]);
|
||||
|
||||
const queryAllKeysQuery = useQuery({
|
||||
queryKey: ['allKeys'],
|
||||
queryFn: async () => {
|
||||
if (!accessToken) throw new Error('Access token required');
|
||||
return await fetchAllKeyAliases(accessToken);
|
||||
},
|
||||
enabled: !!accessToken
|
||||
});
|
||||
const allKeyAliases = queryAllKeysQuery.data || []
|
||||
|
||||
// Apply filters to keys whenever logs or filters change
|
||||
useEffect(() => {
|
||||
if (!logs || !logs.data) {
|
||||
@@ -140,6 +152,24 @@ export function useLogFilterLogic({
|
||||
);
|
||||
}
|
||||
|
||||
// Add key alias filtering
|
||||
if (filters[FILTER_KEYS.KEY_ALIAS]) {
|
||||
// We need to fetch the key info to get the key hash for the selected alias
|
||||
try {
|
||||
// Get the key hash for the selected alias
|
||||
const selectedKey = filters[FILTER_KEYS.KEY_ALIAS]
|
||||
|
||||
if (selectedKey) {
|
||||
// Filter logs by the key hash
|
||||
filteredData = filteredData.filter(
|
||||
log => log.metadata?.user_api_key_alias === selectedKey
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error fetching key info for alias:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const newFilteredLogs: PaginatedResponse = {
|
||||
data: filteredData,
|
||||
total: logs.total,
|
||||
@@ -151,17 +181,9 @@ export function useLogFilterLogic({
|
||||
if (JSON.stringify(newFilteredLogs) !== JSON.stringify(filteredLogs)) {
|
||||
setFilteredLogs(newFilteredLogs);
|
||||
}
|
||||
}, [logs, filters, filteredLogs]);
|
||||
}, [logs, filters, filteredLogs, accessToken]);
|
||||
|
||||
const queryAllKeysQuery = useQuery({
|
||||
queryKey: ['allKeys'],
|
||||
queryFn: async () => {
|
||||
if (!accessToken) throw new Error('Access token required');
|
||||
return await fetchAllKeyAliases(accessToken);
|
||||
},
|
||||
enabled: !!accessToken
|
||||
});
|
||||
const allKeyAliases = queryAllKeysQuery.data || []
|
||||
|
||||
|
||||
// Fetch all teams and users for potential filter dropdowns (optional, can be adapted)
|
||||
const { data: allTeams } = useQuery<Team[], Error>({
|
||||
|
||||
Reference in New Issue
Block a user