Skip to content

Commit 9115560

Browse files
committed
prevent graph from reloading
1 parent 6b36d35 commit 9115560

File tree

2 files changed

+55
-10
lines changed

2 files changed

+55
-10
lines changed

ell-studio/src/pages/Home.js

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useMemo, useEffect } from 'react';
22
import { Link } from 'react-router-dom';
33
import { useTheme } from '../contexts/ThemeContext';
44
import { getTimeAgo } from '../utils/lmpUtils';
55
import { DependencyGraph } from '../components/depgraph/DependencyGraph';
66
import { useLatestLMPs, useTraces } from '../hooks/useBackend';
77
import VersionBadge from '../components/VersionBadge';
8+
const MemoizedDependencyGraph = React.memo(DependencyGraph);
89
function Home() {
910
const [expandedLMP, setExpandedLMP] = useState(null);
1011
const { darkMode } = useTheme();
1112
const { data: lmps, isLoading: isLoadingLMPs } = useLatestLMPs();
1213
const { data: traces, isLoading: isLoadingTraces } = useTraces(lmps);
14+
1315

1416
const toggleExpand = (lmpName, event) => {
1517
if (event.target.tagName.toLowerCase() !== 'a') {
@@ -21,7 +23,22 @@ function Home() {
2123
return id.length > 8 ? `${id.substring(0, 8)}...` : id;
2224
};
2325

24-
if (isLoadingLMPs || isLoadingTraces) {
26+
const [firstTraces, setFirstTraces] = useState(traces);
27+
const [firstLMPs, setFirstLMPs] = useState(lmps);
28+
29+
useEffect(() => {
30+
if((!firstTraces || !firstLMPs) && traces && lmps) {
31+
console.log("Setting first traces and lmps");
32+
setFirstTraces(traces);
33+
setFirstLMPs(lmps);
34+
}
35+
}, [traces, firstTraces, lmps, firstLMPs]);
36+
37+
// TODO: Make graph dynamically update.
38+
const memoizedTraces = useMemo(() => firstTraces, [firstTraces]);
39+
const memoizedLMPs = useMemo(() => firstLMPs, [firstLMPs]);
40+
41+
if (!memoizedLMPs || !memoizedTraces) {
2542
return <div className={`bg-${darkMode ? 'gray-900' : 'gray-100'} min-h-screen flex items-center justify-center`}>
2643
<p className={`text-${darkMode ? 'white' : 'black'}`}>Loading...</p>
2744
</div>;
@@ -31,7 +48,7 @@ function Home() {
3148
<div className={`bg-${darkMode ? 'gray-900' : 'gray-100'} min-h-screen`}>
3249
<div className="container mx-auto px-4 py-8">
3350
<h1 className={`text-3xl font-bold mb-6 ${darkMode ? 'text-gray-100' : 'text-gray-800'}`}>Language Model Programs</h1>
34-
{lmps && traces && <DependencyGraph lmps={lmps} traces={traces}/>}
51+
<MemoizedDependencyGraph lmps={memoizedLMPs} traces={memoizedTraces} key={memoizedLMPs.length + memoizedTraces.length}/>
3552
<div className="space-y-4">
3653
{lmps.map((lmp) => (
3754
<div

src/ell/studio/__main__.py

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from fastapi.staticfiles import StaticFiles
77
from fastapi.responses import FileResponse
88
from watchfiles import awatch
9-
9+
import time
1010

1111
def main():
1212
parser = ArgumentParser(description="ELL Studio Data Server")
@@ -30,20 +30,48 @@ async def serve_react_app(full_path: str):
3030

3131
db_path = os.path.join(args.storage_dir, "ell.db")
3232

33-
async def db_watcher():
34-
async for changes in awatch(db_path):
35-
print(f"Database changed: {changes}")
36-
await app.notify_clients("database_updated")
33+
async def db_watcher(db_path, app):
34+
last_stat = None
3735

38-
# Start the database watcher
36+
while True:
37+
await asyncio.sleep(0.1) # Fixed interval of 0.1 seconds
38+
try:
39+
current_stat = os.stat(db_path)
40+
41+
if last_stat is None:
42+
print(f"Database file found: {db_path}")
43+
await app.notify_clients("database_updated")
44+
else:
45+
# Use a threshold for time comparison to account for filesystem differences
46+
time_threshold = 1 # 1 second threshold
47+
time_changed = abs(current_stat.st_mtime - last_stat.st_mtime) > time_threshold
48+
size_changed = current_stat.st_size != last_stat.st_size
49+
inode_changed = current_stat.st_ino != last_stat.st_ino
3950

51+
if time_changed or size_changed or inode_changed:
52+
print(f"Database changed: mtime {time.ctime(last_stat.st_mtime)} -> {time.ctime(current_stat.st_mtime)}, "
53+
f"size {last_stat.st_size} -> {current_stat.st_size}, "
54+
f"inode {last_stat.st_ino} -> {current_stat.st_ino}")
55+
await app.notify_clients("database_updated")
56+
57+
last_stat = current_stat
58+
except FileNotFoundError:
59+
if last_stat is not None:
60+
print(f"Database file deleted: {db_path}")
61+
await app.notify_clients("database_updated")
62+
last_stat = None
63+
await asyncio.sleep(1) # Wait a bit longer if the file is missing
64+
except Exception as e:
65+
print(f"Error checking database file: {e}")
66+
await asyncio.sleep(1) # Wait a bit longer on errors
4067

68+
# Start the database watcher
4169
loop = asyncio.new_event_loop()
4270

4371
config = uvicorn.Config(app=app, port=args.port, loop=loop)
4472
server = uvicorn.Server(config)
4573
loop.create_task(server.serve())
46-
loop.create_task(db_watcher())
74+
loop.create_task(db_watcher(db_path, app))
4775
loop.run_forever()
4876

4977
if __name__ == "__main__":

0 commit comments

Comments
 (0)