Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewriting to use react query and cleaning up the backend. Closes #25. Closes #31 #112

Merged
merged 9 commits into from
Aug 5, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
initial tsquery refactor
MadcowD committed Aug 5, 2024
commit 7f3f495ecc1f9f3892ba658ebe091fcc1145c1c1
27 changes: 27 additions & 0 deletions ell-studio/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions ell-studio/package.json
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-slot": "^1.1.0",
"@tanstack/react-query": "^5.51.21",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
47 changes: 30 additions & 17 deletions ell-studio/src/App.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,45 @@
import React, { useEffect } from 'react';
import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Sidebar from './components/Sidebar';
import Home from './pages/Home';
import LMP from './pages/LMP';
import Traces from './pages/Traces';
import { ThemeProvider } from './contexts/ThemeContext';
import './styles/globals.css';
import './styles/sourceCode.css';
import refractor from 'refractor'

// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false, // default: true
retry: false, // default: 3
staleTime: 5 * 60 * 1000, // 5 minutes
},
},
});

function App() {
return (
<ThemeProvider>
<Router>
<div className="flex min-h-screen max-h-screen bg-gray-900 text-gray-100">
<Sidebar />
<div className="flex-1 flex flex-col max-h-screen overflow-hidden">
<main className="flex-1 max-h-screen overflow-auto hide-scrollbar">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/lmp/:name/:id?" element={<LMP />} />
<Route path="/traces" element={<Traces />} />
</Routes>
</main>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<Router>
<div className="flex min-h-screen max-h-screen bg-gray-900 text-gray-100">
<Sidebar />
<div className="flex-1 flex flex-col max-h-screen overflow-hidden">
<main className="flex-1 max-h-screen overflow-auto hide-scrollbar">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/lmp/:name/:id?" element={<LMP />} />
<Route path="/traces" element={<Traces />} />
</Routes>
</main>
</div>
</div>
</div>
</Router>
</ThemeProvider>
</Router>
</ThemeProvider>
</QueryClientProvider>
);
}

132 changes: 132 additions & 0 deletions ell-studio/src/hooks/useBackend.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { useQuery } from '@tanstack/react-query';
import axios from 'axios';


const API_BASE_URL = "http://localhost:8080";

export const useLMPDetails = (name, id) => {
return useQuery({
queryKey: ['lmpDetails', name, id],
queryFn: async () => {
const lmpResponse = await axios.get(`${API_BASE_URL}/api/lmps/${name}${id ? `/${id}` : ""}`);
const all_lmps_matching = lmpResponse.data;
return all_lmps_matching
.map((lmp) => ({ ...lmp, created_at: new Date(lmp.created_at) }))
.sort((a, b) => b.created_at - a.created_at)[0];
}
});
};

export const useVersionHistory = (name) => {
return useQuery({
queryKey: ['versionHistory', name],
queryFn: async () => {
const response = await axios.get(`${API_BASE_URL}/api/lmps/${name}`);
return (response.data || []).sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
}
});
};

export const useInvocations = (name, id) => {
return useQuery({
queryKey: ['invocations', name, id],
queryFn: async () => {
const response = await axios.get(`${API_BASE_URL}/api/invocations/${name}${id ? `/${id}` : ""}`);
return response.data.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
}
});
};

export const useUses = (usesIds) => {
return useQuery({
queryKey: ['uses', usesIds],
queryFn: async () => {
return Promise.all(
usesIds.map(async (use) => {
const useResponse = await axios.get(`${API_BASE_URL}/api/lmps/${use}`);
return useResponse.data[0];
})
);
},
enabled: !!usesIds && usesIds.length > 0,
});
};

export const useAllInvocations = () => {
return useQuery({
queryKey: ['allInvocations'],
queryFn: async () => {
const response = await axios.get(`${API_BASE_URL}/api/invocations`);
return response.data.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
}
});
};


export const useAllLMPs = () => {
return useQuery({
queryKey: ['allLMPs'],
queryFn: async () => {
const response = await axios.get(`${API_BASE_URL}/api/lmps`);
const lmps = response.data;

// Group LMPs by name
const lmpGroups = lmps.reduce((acc, lmp) => {
if (!acc[lmp.name]) {
acc[lmp.name] = [];
}
acc[lmp.name].push(lmp);
return acc;
}, {});

// Process each group
return Object.values(lmpGroups).map(group => {
const sortedVersions = group.sort((a, b) => new Date(b.created_at) - new Date(a.created_at));
const latestVersion = sortedVersions[0];

return {
...latestVersion,
versions: sortedVersions.map(version => ({
...version,
created_at: new Date(version.created_at),
})),
};
});
}
});
};


const fetchTraces = async (lmps) => {
const baseUrl = API_BASE_URL
const response = await axios.get(`${baseUrl}/api/traces`);
// Filter out duplicate traces based on consumed and consumer
const uniqueTraces = response.data.reduce((acc, trace) => {
const key = `${trace.consumed}-${trace.consumer}`;
if (!acc[key]) {
acc[key] = trace;
}
return acc;
}, {});

// Convert the object of unique traces back to an array
const uniqueTracesArray = Object.values(uniqueTraces);

// Filter out traces between LMPs that are not on the graph
const lmpIds = new Set(lmps.map(lmp => lmp.lmp_id));
const filteredTraces = uniqueTracesArray.filter(trace =>
lmpIds.has(trace.consumed) && lmpIds.has(trace.consumer)
);

return filteredTraces;
}



export const useTraces = (lmps) => {
return useQuery({
queryKey: ['traces', lmps],
queryFn: () => fetchTraces(lmps),
enabled: !!lmps && lmps.length > 0,
});
};
40 changes: 14 additions & 26 deletions ell-studio/src/pages/Home.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
import React, { useState, useEffect } from 'react';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { useTheme } from '../contexts/ThemeContext';
import { fetchLMPs, getTimeAgo, fetchTraces } from '../utils/lmpUtils';
import { getTimeAgo } from '../utils/lmpUtils';
import { DependencyGraph } from '../components/depgraph/DependencyGraph';
import { useAllLMPs, useTraces } from '../hooks/useBackend';

function Home() {
const [lmps, setLmps] = useState([]);
const [loaded, setLoaded] = useState(false);
const { darkMode } = useTheme();
const [expandedLMP, setExpandedLMP] = useState(null);
const [traces, setTraces] = useState([]);

useEffect(() => {
const getLMPs = async () => {
try {
const aggregatedLMPs = await fetchLMPs();
const traces = await fetchTraces(aggregatedLMPs);
setLmps(aggregatedLMPs);
setTraces(traces);

setLoaded(true);
} catch (error) {
console.error('Error fetching LMPs:', error);
}
};
getLMPs();
}, []);
const { darkMode } = useTheme();
const { data: lmps, isLoading: isLoadingLMPs } = useAllLMPs();
const { data: traces, isLoading: isLoadingTraces } = useTraces(lmps);

const toggleExpand = (lmpName, event) => {
// Prevent toggling when clicking on the link
if (event.target.tagName.toLowerCase() !== 'a') {
setExpandedLMP(expandedLMP === lmpName ? null : lmpName);
}
@@ -38,11 +21,17 @@ function Home() {
return id.length > 8 ? `${id.substring(0, 8)}...` : id;
};

if (isLoadingLMPs || isLoadingTraces) {
return <div className={`bg-${darkMode ? 'gray-900' : 'gray-100'} min-h-screen flex items-center justify-center`}>
<p className={`text-${darkMode ? 'white' : 'black'}`}>Loading...</p>
</div>;
}

return (
<div className={`bg-${darkMode ? 'gray-900' : 'gray-100'} min-h-screen`}>
<div className="container mx-auto px-4 py-8">
<h1 className={`text-3xl font-bold mb-6 ${darkMode ? 'text-gray-100' : 'text-gray-800'}`}>Language Model Programs</h1>
{loaded && <DependencyGraph lmps={lmps} traces={traces}/>}
{lmps && traces && <DependencyGraph lmps={lmps} traces={traces}/>}
<div className="space-y-4">
{lmps.map((lmp) => (
<div
@@ -106,5 +95,4 @@ function Home() {
);
}

export default Home;

export default Home;
Loading