From a10878b2bf7373a468daa96507ca29835f107d3b Mon Sep 17 00:00:00 2001 From: William Guss Date: Fri, 9 Aug 2024 13:54:15 -0700 Subject: [PATCH] fix display --- ell-studio/src/components/LMPHistoryChart.js | 103 +++++++++++++++++++ ell-studio/src/hooks/useBackend.js | 12 ++- ell-studio/src/pages/Invocations.js | 50 +++++---- src/ell/studio/server.py | 24 +++++ 4 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 ell-studio/src/components/LMPHistoryChart.js diff --git a/ell-studio/src/components/LMPHistoryChart.js b/ell-studio/src/components/LMPHistoryChart.js new file mode 100644 index 00000000..73918418 --- /dev/null +++ b/ell-studio/src/components/LMPHistoryChart.js @@ -0,0 +1,103 @@ +import React, { useMemo, useState } from 'react'; +import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, Brush, Legend } from 'recharts'; +import { format, differenceInDays, differenceInHours, startOfDay, startOfHour, startOfMinute, eachDayOfInterval, eachHourOfInterval, eachMinuteOfInterval, addMinutes, getMinutes } from 'date-fns'; + +const LMPHistoryChart = ({ data, title }) => { + const [dateRange, setDateRange] = useState(null); + + const aggregatedData = useMemo(() => { + if (!data?.length) return []; + + const sortedData = [...data].sort((a, b) => new Date(a.date) - new Date(b.date)); + const zoomStart = dateRange?.start || new Date(sortedData[0].date); + const zoomEnd = dateRange?.end || new Date(sortedData[sortedData.length - 1].date); + + const daysDiff = differenceInDays(zoomEnd, zoomStart); + const hoursDiff = differenceInHours(zoomEnd, zoomStart); + + let aggregationInterval; + let aggregationFunction; + + if (daysDiff > 30) { + aggregationInterval = eachDayOfInterval; + aggregationFunction = startOfDay; + } else if (hoursDiff > 24) { + aggregationInterval = eachHourOfInterval; + aggregationFunction = startOfHour; + } else { + aggregationInterval = eachMinuteOfInterval; + aggregationFunction = startOfMinute; + } + + const aggregatedMap = new Map(); + sortedData.forEach((item) => { + const date = new Date(item.date); + if (date >= zoomStart && date <= zoomEnd) { + const key = format(aggregationFunction(date), "yyyy-MM-dd'T'HH:mm"); + const existing = aggregatedMap.get(key) || 0; + aggregatedMap.set(key, existing + item.count); + } + }); + + return aggregationInterval({ start: zoomStart, end: zoomEnd }).map((date) => { + const key = format(date, "yyyy-MM-dd'T'HH:mm"); + return { + date: key, + versions: aggregatedMap.get(key) || 0, + }; + }); + }, [data, dateRange]); + + const formatXAxis = (tickItem) => { + const date = new Date(tickItem); + const daysDiff = differenceInDays(dateRange?.end || new Date(), dateRange?.start || new Date(data[0].date)); + if (daysDiff > 30) return format(date, "MMM d"); + if (daysDiff > 1) return format(date, "MMM d HH:mm"); + return format(date, "HH:mm"); + }; + + const formatTooltip = (value, name, props) => { + return [`${value} versions`, "LMPs Created"]; + }; + + const handleZoom = (domain) => { + setDateRange({ + start: new Date(domain.startDate), + end: new Date(domain.endDate), + }); + }; + + return ( +
+

{title}

+ + + + + + + + + + + + format(new Date(label), "PPpp")} + formatter={formatTooltip} + /> + + + + + +
+ ); +}; + +export default LMPHistoryChart; \ No newline at end of file diff --git a/ell-studio/src/hooks/useBackend.js b/ell-studio/src/hooks/useBackend.js index 89c5cac9..77995510 100644 --- a/ell-studio/src/hooks/useBackend.js +++ b/ell-studio/src/hooks/useBackend.js @@ -129,4 +129,14 @@ export const useTraces = (lmps) => { }, enabled: !!lmps && lmps.length > 0, }); - }; \ No newline at end of file + }; + +export const useLMPHistory = (days = 365) => { + return useQuery({ + queryKey: ['lmpHistory', days], + queryFn: async () => { + const response = await axios.get(`${API_BASE_URL}/api/lmp-history?days=${days}`); + return response.data; + }, + }); +}; \ No newline at end of file diff --git a/ell-studio/src/pages/Invocations.js b/ell-studio/src/pages/Invocations.js index bfa177d7..95370362 100644 --- a/ell-studio/src/pages/Invocations.js +++ b/ell-studio/src/pages/Invocations.js @@ -3,8 +3,9 @@ import { FiCopy, FiZap, FiEdit2, FiFilter, FiClock, FiColumns, FiPause, FiPlay } import InvocationsTable from '../components/invocations/InvocationsTable'; import InvocationsLayout from '../components/invocations/InvocationsLayout'; import MetricChart from '../components/MetricChart'; +import LMPHistoryChart from '../components/LMPHistoryChart'; // New import import { useNavigate, useLocation } from 'react-router-dom'; -import { useInvocationsFromLMP } from '../hooks/useBackend'; +import { useInvocationsFromLMP, useLMPHistory } from '../hooks/useBackend'; // Added useLMPHistory const Traces = () => { const [selectedTrace, setSelectedTrace] = useState(null); @@ -16,6 +17,7 @@ const Traces = () => { const pageSize = 50; const { data: invocations , isLoading } = useInvocationsFromLMP(null, null, currentPage, pageSize); + const { data: lmpHistory, isLoading: isLMPHistoryLoading } = useLMPHistory(365); // Fetch 1 year of data useEffect(() => { const searchParams = new URLSearchParams(location.search); @@ -56,7 +58,7 @@ const Traces = () => { return sum / invocations.length; }, [invocations]); - if (isLoading) { + if (isLoading || isLMPHistoryLoading) { return
Loading...
; } @@ -65,7 +67,7 @@ const Traces = () => { selectedTrace={selectedTrace} setSelectedTrace={setSelectedTrace} showSidebar={true} - containerClass={'p-6'} + containerClass={'p-6 flex flex-col h-full'} >

@@ -85,22 +87,32 @@ const Traces = () => {

-
- - +
+
+ +
+
+ +
+ {/*
+ +
*/}