|
import type { ColumnDef } from "@tanstack/react-table"; |
|
import { getCountryFromIP } from "./ip_lookup"; |
|
import moment from "moment"; |
|
import React from "react"; |
|
import { CountryCell } from "./country_cell"; |
|
import { getProviderLogoAndName } from "../provider_info_helpers"; |
|
import { Tooltip } from "antd"; |
|
import { TimeCell } from "./time_cell"; |
|
|
|
export type LogEntry = { |
|
request_id: string; |
|
api_key: string; |
|
team_id: string; |
|
model: string; |
|
api_base?: string; |
|
call_type: string; |
|
spend: number; |
|
total_tokens: number; |
|
prompt_tokens: number; |
|
completion_tokens: number; |
|
startTime: string; |
|
endTime: string; |
|
user?: string; |
|
end_user?: string; |
|
custom_llm_provider?: string; |
|
metadata?: Record<string, any>; |
|
cache_hit: string; |
|
cache_key?: string; |
|
request_tags?: Record<string, any>; |
|
requester_ip_address?: string; |
|
messages: string | any[] | Record<string, any>; |
|
response: string | any[] | Record<string, any>; |
|
}; |
|
|
|
export const columns: ColumnDef<LogEntry>[] = [ |
|
{ |
|
id: "expander", |
|
header: () => null, |
|
cell: ({ row }) => { |
|
return row.getCanExpand() ? ( |
|
<button |
|
{...{ |
|
onClick: row.getToggleExpandedHandler(), |
|
style: { cursor: "pointer" }, |
|
}} |
|
> |
|
{row.getIsExpanded() ? "▼" : "▶"} |
|
</button> |
|
) : ( |
|
"●" |
|
); |
|
}, |
|
}, |
|
{ |
|
header: "Time", |
|
accessorKey: "startTime", |
|
cell: (info: any) => <TimeCell utcTime={info.getValue()} />, |
|
}, |
|
{ |
|
header: "Request ID", |
|
accessorKey: "request_id", |
|
cell: (info: any) => ( |
|
<Tooltip title={String(info.getValue() || "")}> |
|
<span className="font-mono text-xs max-w-[100px] truncate block"> |
|
{String(info.getValue() || "")} |
|
</span> |
|
</Tooltip> |
|
), |
|
}, |
|
{ |
|
header: "Country", |
|
accessorKey: "requester_ip_address", |
|
cell: (info: any) => <CountryCell ipAddress={info.getValue()} />, |
|
}, |
|
{ |
|
header: "Team", |
|
accessorKey: "metadata.user_api_key_team_alias", |
|
cell: (info: any) => <span>{String(info.getValue() || "-")}</span>, |
|
}, |
|
{ |
|
header: "Key Name", |
|
accessorKey: "metadata.user_api_key_alias", |
|
cell: (info: any) => <span>{String(info.getValue() || "-")}</span>, |
|
}, |
|
{ |
|
header: "Request", |
|
accessorKey: "messages", |
|
cell: (info: any) => { |
|
const messages = info.getValue(); |
|
try { |
|
const content = |
|
typeof messages === "string" ? JSON.parse(messages) : messages; |
|
let displayText = ""; |
|
|
|
if (Array.isArray(content)) { |
|
displayText = formatMessage(content[0]?.content); |
|
} else { |
|
displayText = formatMessage(content); |
|
} |
|
|
|
return <span className="truncate max-w-md text-sm">{displayText}</span>; |
|
} catch (e) { |
|
return ( |
|
<span className="truncate max-w-md text-sm"> |
|
{formatMessage(messages)} |
|
</span> |
|
); |
|
} |
|
}, |
|
}, |
|
{ |
|
header: "Model", |
|
accessorKey: "model", |
|
cell: (info: any) => { |
|
const row = info.row.original; |
|
const provider = row.custom_llm_provider; |
|
const modelName = String(info.getValue() || ""); |
|
return ( |
|
<div className="flex items-center space-x-2"> |
|
{provider && ( |
|
<img |
|
src={getProviderLogoAndName(provider).logo} |
|
alt="" |
|
className="w-4 h-4" |
|
onError={(e) => { |
|
const target = e.target as HTMLImageElement; |
|
target.style.display = 'none'; |
|
}} |
|
/> |
|
)} |
|
<Tooltip title={modelName}> |
|
<span className="max-w-[100px] truncate"> |
|
{modelName} |
|
</span> |
|
</Tooltip> |
|
</div> |
|
); |
|
}, |
|
}, |
|
{ |
|
header: "Tokens", |
|
accessorKey: "total_tokens", |
|
cell: (info: any) => { |
|
const row = info.row.original; |
|
return ( |
|
<span className="text-sm"> |
|
{String(row.total_tokens || "0")} |
|
<span className="text-gray-400 text-xs ml-1"> |
|
({String(row.prompt_tokens || "0")}+ |
|
{String(row.completion_tokens || "0")}) |
|
</span> |
|
</span> |
|
); |
|
}, |
|
}, |
|
{ |
|
header: "Internal User", |
|
accessorKey: "user", |
|
cell: (info: any) => <span>{String(info.getValue() || "-")}</span>, |
|
}, |
|
{ |
|
header: "End User", |
|
accessorKey: "end_user", |
|
cell: (info: any) => <span>{String(info.getValue() || "-")}</span>, |
|
}, |
|
{ |
|
header: "Cost", |
|
accessorKey: "spend", |
|
cell: (info: any) => ( |
|
<span>${Number(info.getValue() || 0).toFixed(6)}</span> |
|
), |
|
}, |
|
{ |
|
header: "Tags", |
|
accessorKey: "request_tags", |
|
cell: (info: any) => { |
|
const tags = info.getValue(); |
|
if (!tags || Object.keys(tags).length === 0) return "-"; |
|
return ( |
|
<div className="flex flex-wrap gap-1"> |
|
{Object.entries(tags).map(([key, value]) => ( |
|
<span |
|
key={key} |
|
className="px-2 py-1 bg-gray-100 rounded-full text-xs" |
|
> |
|
{key}: {String(value)} |
|
</span> |
|
))} |
|
</div> |
|
); |
|
}, |
|
}, |
|
]; |
|
|
|
const formatMessage = (message: any): string => { |
|
if (!message) return "N/A"; |
|
if (typeof message === "string") return message; |
|
if (typeof message === "object") { |
|
|
|
if (message.text) return message.text; |
|
if (message.content) return message.content; |
|
return JSON.stringify(message); |
|
} |
|
return String(message); |
|
}; |
|
|