mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-06 02:48:13 +00:00
Merge pull request #18366 from BerriAI/litellm_ui_spend_logs_error_code
[Feature] UI - Add Error Code Filtering on UI
This commit is contained in:
commit
fa738f217c
6 changed files with 61 additions and 9 deletions
|
|
@ -1771,7 +1771,7 @@ async def ui_view_spend_logs( # noqa: PLR0915
|
|||
if error_code is not None:
|
||||
metadata_filters.append({
|
||||
"path": ["error_information", "error_code"],
|
||||
"equals": error_code,
|
||||
"equals": f'"{error_code}"',
|
||||
})
|
||||
|
||||
if metadata_filters:
|
||||
|
|
@ -1781,7 +1781,6 @@ async def ui_view_spend_logs( # noqa: PLR0915
|
|||
where_conditions["AND"] = where_conditions.get("AND", []) + [
|
||||
{"metadata": filter_cond} for filter_cond in metadata_filters
|
||||
]
|
||||
|
||||
if end_user is not None:
|
||||
where_conditions["end_user"] = end_user
|
||||
|
||||
|
|
|
|||
|
|
@ -1895,9 +1895,12 @@ async def test_ui_view_spend_logs_with_error_code(client):
|
|||
metadata_filter = where_conditions["metadata"]
|
||||
if metadata_filter.get("path") == ["error_information", "error_code"]:
|
||||
error_code = metadata_filter.get("equals")
|
||||
if error_code == "404":
|
||||
# Handle both string and integer error codes
|
||||
# The endpoint wraps error_code in quotes, so strip them for comparison
|
||||
error_code_value = str(error_code).strip('"')
|
||||
if error_code_value == "404":
|
||||
return [mock_spend_logs[0]]
|
||||
elif error_code == "500":
|
||||
elif error_code_value == "500":
|
||||
return [mock_spend_logs[1]]
|
||||
return mock_spend_logs
|
||||
|
||||
|
|
@ -1907,9 +1910,12 @@ async def test_ui_view_spend_logs_with_error_code(client):
|
|||
metadata_filter = where_conditions["metadata"]
|
||||
if metadata_filter.get("path") == ["error_information", "error_code"]:
|
||||
error_code = metadata_filter.get("equals")
|
||||
if error_code == "404":
|
||||
# Handle both string and integer error codes
|
||||
# The endpoint wraps error_code in quotes, so strip them for comparison
|
||||
error_code_value = str(error_code).strip('"')
|
||||
if error_code_value == "404":
|
||||
return 1
|
||||
elif error_code == "500":
|
||||
elif error_code_value == "500":
|
||||
return 1
|
||||
return len(mock_spend_logs)
|
||||
|
||||
|
|
@ -1995,7 +2001,10 @@ async def test_ui_view_spend_logs_with_error_code_and_key_alias(client):
|
|||
elif metadata_filter.get("path") == ["error_information", "error_code"]:
|
||||
error_code_filter = metadata_filter.get("equals")
|
||||
|
||||
if key_alias_filter == "test-key-1" and error_code_filter == "500":
|
||||
# Handle both string and integer error codes
|
||||
# The endpoint wraps error_code in quotes, so strip them for comparison
|
||||
error_code_value = str(error_code_filter).strip('"')
|
||||
if key_alias_filter == "test-key-1" and error_code_value == "500":
|
||||
return [mock_spend_logs[2]] # Only log3 matches both conditions
|
||||
return mock_spend_logs
|
||||
|
||||
|
|
@ -2012,7 +2021,10 @@ async def test_ui_view_spend_logs_with_error_code_and_key_alias(client):
|
|||
elif metadata_filter.get("path") == ["error_information", "error_code"]:
|
||||
error_code_filter = metadata_filter.get("equals")
|
||||
|
||||
if key_alias_filter == "test-key-1" and error_code_filter == "500":
|
||||
# Handle both string and integer error codes
|
||||
# The endpoint wraps error_code in quotes, so strip them for comparison
|
||||
error_code_value = str(error_code_filter).strip('"')
|
||||
if key_alias_filter == "test-key-1" and error_code_value == "500":
|
||||
return 1
|
||||
return len(mock_spend_logs)
|
||||
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ const FilterComponent: React.FC<FilterComponentProps> = ({
|
|||
"Key Alias",
|
||||
"User ID",
|
||||
"End User",
|
||||
"Error Code",
|
||||
"Key Hash",
|
||||
"Model",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2687,6 +2687,7 @@ export const uiSpendLogsCall = async (
|
|||
status_filter?: string,
|
||||
model?: string,
|
||||
keyAlias?: string,
|
||||
error_code?: string,
|
||||
) => {
|
||||
try {
|
||||
// Construct base URL
|
||||
|
|
@ -2706,6 +2707,7 @@ export const uiSpendLogsCall = async (
|
|||
if (status_filter) queryParams.append("status_filter", status_filter);
|
||||
if (model) queryParams.append("model", model);
|
||||
if (keyAlias) queryParams.append("key_alias", keyAlias);
|
||||
if (error_code) queryParams.append("error_code", error_code);
|
||||
// Append query parameters to URL if any exist
|
||||
const queryString = queryParams.toString();
|
||||
if (queryString) {
|
||||
|
|
|
|||
|
|
@ -366,6 +366,24 @@ export default function SpendLogsTable({
|
|||
setExpandedRequestId(requestId);
|
||||
};
|
||||
|
||||
// Function to extract unique error codes from logs
|
||||
const extractErrorCodes = (logs: LogEntry[], searchText: string = "") => {
|
||||
const errorCodes = new Set<string>();
|
||||
logs.forEach((log) => {
|
||||
const metadata = log.metadata || {};
|
||||
if (metadata.status === "failure" && metadata.error_information) {
|
||||
const errorCode = metadata.error_information.error_code;
|
||||
if (errorCode && (!searchText || errorCode.toLowerCase().includes(searchText.toLowerCase()))) {
|
||||
errorCodes.add(errorCode);
|
||||
}
|
||||
}
|
||||
});
|
||||
return Array.from(errorCodes).map((code) => ({
|
||||
label: code,
|
||||
value: code,
|
||||
}));
|
||||
};
|
||||
|
||||
const logFilterOptions: FilterOption[] = [
|
||||
{
|
||||
name: "Team ID",
|
||||
|
|
@ -426,6 +444,14 @@ export default function SpendLogsTable({
|
|||
return filtered.map((u: string) => ({ label: u, value: u }));
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Error Code",
|
||||
label: "Error Code",
|
||||
isSearchable: true,
|
||||
searchFn: async (searchText: string) => {
|
||||
return extractErrorCodes(logsData.data, searchText);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Key Hash",
|
||||
label: "Key Hash",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ export const FILTER_KEYS = {
|
|||
END_USER: "End User",
|
||||
STATUS: "Status",
|
||||
KEY_ALIAS: "Key Alias",
|
||||
ERROR_CODE: "Error Code",
|
||||
} as const;
|
||||
|
||||
export type FilterKey = keyof typeof FILTER_KEYS;
|
||||
|
|
@ -53,6 +54,7 @@ export function useLogFilterLogic({
|
|||
[FILTER_KEYS.END_USER]: "",
|
||||
[FILTER_KEYS.STATUS]: "",
|
||||
[FILTER_KEYS.KEY_ALIAS]: "",
|
||||
[FILTER_KEYS.ERROR_CODE]: "",
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
|
@ -94,6 +96,7 @@ export function useLogFilterLogic({
|
|||
filters[FILTER_KEYS.STATUS] || undefined,
|
||||
filters[FILTER_KEYS.MODEL] || undefined,
|
||||
filters[FILTER_KEYS.KEY_ALIAS] || undefined,
|
||||
filters[FILTER_KEYS.ERROR_CODE] || undefined,
|
||||
);
|
||||
|
||||
if (currentTimestamp === lastSearchTimestamp.current && response.data) {
|
||||
|
|
@ -133,7 +136,8 @@ export function useLogFilterLogic({
|
|||
filters[FILTER_KEYS.KEY_HASH] ||
|
||||
filters[FILTER_KEYS.REQUEST_ID] ||
|
||||
filters[FILTER_KEYS.USER_ID] ||
|
||||
filters[FILTER_KEYS.END_USER]
|
||||
filters[FILTER_KEYS.END_USER] ||
|
||||
filters[FILTER_KEYS.ERROR_CODE]
|
||||
),
|
||||
[filters],
|
||||
);
|
||||
|
|
@ -182,6 +186,14 @@ export function useLogFilterLogic({
|
|||
filteredData = filteredData.filter((log) => log.end_user === filters[FILTER_KEYS.END_USER]);
|
||||
}
|
||||
|
||||
if (filters[FILTER_KEYS.ERROR_CODE]) {
|
||||
filteredData = filteredData.filter((log) => {
|
||||
const metadata = log.metadata || {};
|
||||
const errorInfo = metadata.error_information;
|
||||
return errorInfo && errorInfo.error_code === filters[FILTER_KEYS.ERROR_CODE];
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
data: filteredData,
|
||||
total: logs.total,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue