Working invocaiton panels

This commit is contained in:
William Guss
2024-07-27 16:03:37 -07:00
parent 1ae9c48e3b
commit 6abfc94129
14 changed files with 240 additions and 195 deletions

View File

@@ -107,7 +107,6 @@ You can then visualize your promtps by visiting the frontend on `http://localhos
## Todos
### Bugs
- [ ] Fix weird rehashing issue of the main prompt whenever subprompt changes? Or just make commits more of a background deal.

View File

@@ -16,7 +16,7 @@ function App() {
<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">
<main className="flex-1 max-h-screen overflow-auto hide-scrollbar">
<Routes>
<Route path="/" element={<Home />} />
<Route path="/lmp/:name/:id?" element={<LMP />} />

View File

@@ -0,0 +1,78 @@
import React, { useState, useEffect, useRef } from 'react';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { FiChevronDown, FiChevronRight, FiMaximize2, FiMinimize2 } from 'react-icons/fi';
import '../styles/SourceCodeView.css';
export function CodeSection({
title,
code,
showCode,
setShowCode,
lines,
startingLineNumber = 1,
isDependent = false,
collapsedHeight = '150px' // New prop with default value
}) {
const [isHovering, setIsHovering] = useState(false);
const codeRef = useRef(null);
useEffect(() => {
if (codeRef.current) {
codeRef.current.style.maxHeight = showCode ? 'none' : collapsedHeight;
}
}, [showCode, collapsedHeight]);
return (
<div className="code-section mb-4">
<button
onClick={() => setShowCode(!showCode)}
className="section-header flex items-center justify-between w-full text-sm text-gray-300 hover:text-white py-2 px-4 rounded-t-md bg-gray-800 hover:bg-gray-700 transition-colors duration-200"
>
<span className="flex items-center">
{showCode ? <FiChevronDown className="mr-2" /> : <FiChevronRight className="mr-2" />}
{title}
</span>
<span className="text-xs text-gray-400">
{lines} lines
</span>
</button>
<div
className={`code-container ${showCode ? 'expanded' : ''}`}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
ref={codeRef}
onClick={() => !showCode && setShowCode(true)}
>
<SyntaxHighlighter
language="python"
style={atomDark}
showLineNumbers={true}
startingLineNumber={startingLineNumber}
customStyle={{
margin: 0,
padding: '1em',
borderRadius: '0 0 6px 6px',
}}
>
{code}
</SyntaxHighlighter>
{!showCode && (
<div className="gradient-overlay">
{isHovering && (
<button
className="show-more-button"
onClick={(e) => {
e.stopPropagation();
setShowCode(true);
} }
>
Show more ({lines} lines)
</button>
)}
</div>
)}
</div>
</div>
);
}

View File

@@ -4,6 +4,8 @@ import { atomDark } from 'react-syntax-highlighter/dist/esm/styles/prism';
import { FiChevronDown, FiChevronRight, FiMaximize2, FiMinimize2 } from 'react-icons/fi';
import '../styles/SourceCodeView.css';
import { CodeSection } from './CodeSection';
const SourceCodeView = ({ dependencies, source, uses, showDependenciesInitial = false }) => {
const [showDependencies, setShowDependencies] = useState(showDependenciesInitial);
const [showSource, setShowSource] = useState(true);
@@ -23,70 +25,6 @@ const SourceCodeView = ({ dependencies, source, uses, showDependenciesInitial =
if(showDependenciesInitial) setShowDependencies(showDependenciesInitial);
}, [showDependenciesInitial]);
const CodeSection = ({ title, code, showCode, setShowCode, lines, startingLineNumber = 1, isDependent = false }) => {
const [isHovering, setIsHovering] = useState(false);
const codeRef = useRef(null);
useEffect(() => {
if (codeRef.current) {
codeRef.current.style.maxHeight = showCode ? 'none' : '150px';
}
}, [showCode]);
return (
<div className="code-section mb-4">
<button
onClick={() => setShowCode(!showCode)}
className="section-header flex items-center justify-between w-full text-sm text-gray-300 hover:text-white py-2 px-4 rounded-t-md bg-gray-800 hover:bg-gray-700 transition-colors duration-200"
>
<span className="flex items-center">
{showCode ? <FiChevronDown className="mr-2" /> : <FiChevronRight className="mr-2" />}
{title}
</span>
<span className="text-xs text-gray-400">
{lines} lines
{isDependent && `, ${dependentLMPs} LMPs`}
</span>
</button>
<div
className={`code-container ${showCode ? 'expanded' : ''}`}
onMouseEnter={() => setIsHovering(true)}
onMouseLeave={() => setIsHovering(false)}
ref={codeRef}
onClick={() => !showCode && setShowCode(true)}
>
<SyntaxHighlighter
language="python"
style={atomDark}
showLineNumbers={true}
startingLineNumber={startingLineNumber}
customStyle={{
margin: 0,
padding: '1em',
borderRadius: '0 0 6px 6px',
}}
>
{code}
</SyntaxHighlighter>
{!showCode && (
<div className="gradient-overlay">
{isHovering && (
<button
className="show-more-button"
onClick={(e) => {
e.stopPropagation();
setShowCode(true);
}}
>
Show more ({lines} lines)
</button>
)}
</div>
)}
</div>
</div>
);
};
return (
<div className="source-code-container">

View File

@@ -1,153 +1,176 @@
import React, { useState, useRef, useEffect } from 'react';
import { FiLink, FiCopy, FiChevronDown, FiChevronRight } from 'react-icons/fi';
import React, { useState, useRef, useEffect, useMemo } from "react";
import { FiLink, FiCopy, FiChevronDown, FiChevronRight } from "react-icons/fi";
import { lstrCleanStringify } from './lstrCleanStringify';
const TraceDetailsSidebar = ({ trace, onClose }) => {
const [activeTab, setActiveTab] = useState('Run');
import { CodeSection } from './CodeSection';
const InvocationDetailsSidebar = ({ invocation, onClose }) => {
const [activeTab, setActiveTab] = useState("Details");
const [inputExpanded, setInputExpanded] = useState(true);
const [outputExpanded, setOutputExpanded] = useState(true);
const [sidebarWidth, setSidebarWidth] = useState(600);
const [sidebarWidth, setSidebarWidth] = useState(document.body.clientWidth * 0.75);
const resizeRef = useRef(null);
const argsLines = useMemo(() => {
return lstrCleanStringify(invocation.args, 1);
}, [invocation.args]);
const kwargsLines = useMemo(() => {
return lstrCleanStringify(invocation.kwargs, 1);
}, [invocation.kwargs]);
const hasKwargs = useMemo(() => {
return Object.keys(invocation.kwargs).length > 0;
}, [invocation.kwargs]);
useEffect(() => {
if (argsLines.split('\n').length > 10 || (hasKwargs && kwargsLines.split('\n').length > 10)) {
setInputExpanded(false);
}
}, [argsLines, kwargsLines, hasKwargs]);
useEffect(() => {
const handleMouseMove = (e) => {
if (resizeRef.current) {
const newWidth = document.body.clientWidth - e.clientX;
setSidebarWidth(Math.max(400, newWidth));
setSidebarWidth(Math.max(800, newWidth));
}
};
const handleMouseUp = () => {
resizeRef.current = null;
document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};
const handleMouseDown = (e) => {
resizeRef.current = e.target;
document.addEventListener('mousemove', handleMouseMove);
document.addEventListener('mouseup', handleMouseUp);
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
};
const resizer = document.getElementById('sidebar-resizer');
resizer.addEventListener('mousedown', handleMouseDown);
const resizer = document.getElementById("sidebar-resizer");
resizer.addEventListener("mousedown", handleMouseDown);
return () => {
resizer.removeEventListener('mousedown', handleMouseDown);
resizer.removeEventListener("mousedown", handleMouseDown);
};
}, []);
return (
<>
<div id="sidebar-resizer" className="w-1 bg-gray-600 cursor-col-resize" />
<div className="bg-[#0d1117] border-l border-gray-800 overflow-y-auto flex flex-col" style={{ width: sidebarWidth }}>
<div
className="bg-[#0d1117] border-l border-gray-800 overflow-y-auto flex flex-col hide-scrollbar"
style={{ width: sidebarWidth }}
>
<div className="flex items-center justify-between p-4 border-b border-gray-800">
<div className="flex items-center space-x-2">
<FiLink className="text-blue-400" />
<h2 className="text-xl font-semibold text-blue-400">{trace.name}</h2>
<h2 className="text-xl font-semibold text-blue-400">
{invocation.lmp.name}
</h2>
</div>
<div className="flex items-center space-x-4">
<button className="text-gray-400 hover:text-white">
<FiCopy /> Run ID
<FiCopy /> Invocation ID
</button>
<button className="text-gray-400 hover:text-white">
<FiCopy /> Trace ID
</button>
<button onClick={onClose} className="text-gray-400 hover:text-white">
</button>
</div>
</div>{" "}
<button onClick={onClose} className="text-gray-400 hover:text-white">
</button>
</div>
<div className="flex border-b border-gray-800">
<button
className={`px-4 py-2 ${activeTab === 'Run' ? 'text-blue-400 border-b-2 border-blue-400' : 'text-gray-400'}`}
onClick={() => setActiveTab('Run')}
className={`px-4 py-2 ${
activeTab === "Details"
? "text-blue-400 border-b-2 border-blue-400"
: "text-gray-400"
}`}
onClick={() => setActiveTab("Details")}
>
Run
Details
</button>
<button
className={`px-4 py-2 ${activeTab === 'Feedback' ? 'text-blue-400 border-b-2 border-blue-400' : 'text-gray-400'}`}
onClick={() => setActiveTab('Feedback')}
className={`px-4 py-2 ${
activeTab === "Results"
? "text-blue-400 border-b-2 border-blue-400"
: "text-gray-400"
}`}
onClick={() => setActiveTab("Results")}
>
Feedback
</button>
<button
className={`px-4 py-2 ${activeTab === 'Metadata' ? 'text-blue-400 border-b-2 border-blue-400' : 'text-gray-400'}`}
onClick={() => setActiveTab('Metadata')}
>
Metadata
Results
</button>
</div>
<div className="flex flex-grow">
<div className="flex-grow p-4 overflow-y-auto">
<div className="flex justify-between items-center mb-4">
<button
className="flex items-center text-gray-300"
onClick={() => setInputExpanded(!inputExpanded)}
>
<span className="mr-2">Input</span>
{inputExpanded ? <FiChevronDown /> : <FiChevronRight />}
</button>
<span className="text-gray-500">YAML</span>
</div>
{inputExpanded && (
<pre className="bg-[#161b22] p-4 rounded text-sm text-gray-300 mb-4">
<code>
{`input: What is a document loader?
chat_history: []`}
</code>
</pre>
)}
<div className="flex justify-between items-center mb-4">
<button
className="flex items-center text-gray-300"
onClick={() => setOutputExpanded(!outputExpanded)}
>
<span className="mr-2">Output</span>
{outputExpanded ? <FiChevronDown /> : <FiChevronRight />}
</button>
</div>
{outputExpanded && (
<pre className="bg-[#161b22] p-4 rounded text-sm text-gray-300">
<code>
{trace.output}
</code>
</pre>
<div className="flex flex-grow source-code-container">
<div className="flex-grow p-4 overflow-y-auto w-[400px] hide-scrollbar">
<CodeSection
title="Args"
code={argsLines}
showCode={inputExpanded}
setShowCode={setInputExpanded}
collapsedHeight={'300px'}
lines={argsLines.split('\n').length}
language="json"
/>
{hasKwargs && (
<CodeSection
title="Kwargs"
code={kwargsLines}
showCode={inputExpanded}
setShowCode={setInputExpanded}
collapsedHeight={'300px'}
lines={kwargsLines.split('\n').length}
language="json"
/>
)}
{
invocation.results.map((result, index) => (
<CodeSection
key={index}
title={`Output ${index + 1}`}
code={result.content}
showCode={outputExpanded}
setShowCode={setOutputExpanded}
lines={result.content.split('\n').length}
language="plaintext"
/>
))
}
</div>
<div className="w-64 bg-[#0d1117] p-4 border-l border-gray-800 text-sm">
<div className="w-64 bg-[#0d1117] p-4 border-l border-gray-800 text-sm hide-scrollbar">
<div className="mb-2">
<p className="text-gray-500">START TIME</p>
<p className="text-gray-300">{trace.startTime}</p>
</div>
<div className="mb-2">
<p className="text-gray-500">END TIME</p>
<p className="text-gray-300">{trace.endTime}</p>
</div>
<div className="mb-2">
<p className="text-gray-500">TIME TO FIRST TOKEN</p>
<p className="text-gray-300">{trace.timeToFirstToken}</p>
</div>
<div className="mb-2">
<p className="text-gray-500">STATUS</p>
<p className="text-green-400 flex items-center">
<span className="mr-1"></span> Success
</p>
</div>
<div className="mb-2">
<p className="text-gray-500">TOTAL TOKENS</p>
<p className="text-gray-300">{trace.tokens} tokens</p>
<p className="text-gray-500">CREATED AT</p>
<p className="text-gray-300">{new Date(invocation.created_at).toTimeString()}</p>
</div>
<div className="mb-2">
<p className="text-gray-500">LATENCY</p>
<p className="text-red-400">{trace.latency}</p>
{/* <p className="text-gray-300">{formatDuration(invocation.latency_ms)}</p> */}
</div>
<div className="mb-2">
<p className="text-gray-500">TYPE</p>
<p className="text-gray-300 bg-blue-900 inline-block px-2 py-0.5 rounded">Chain</p>
<p className="text-gray-500">PROMPT TOKENS</p>
<p className="text-gray-300">
{invocation.prompt_tokens || "N/A"}
</p>
</div>
<div className="mb-2">
<p className="text-gray-500">COMPLETION TOKENS</p>
<p className="text-gray-300">
{invocation.completion_tokens || "N/A"}
</p>
</div>
<div className="mb-2">
<p className="text-gray-500">LMP TYPE</p>
<p className="text-gray-300 bg-blue-900 inline-block px-2 py-0.5 rounded">
{invocation.lmp.is_lm ? "LM" : "LMP"}
</p>
</div>
<div>
<p className="text-gray-500">TAGS</p>
<p className="text-gray-300 bg-gray-700 inline-block px-2 py-0.5 rounded">{trace.tags[0]}</p>
<p className="text-gray-500">DEPENDENCIES</p>
<p className="text-gray-300">{invocation.lmp.dependencies}</p>
</div>
</div>
</div>
@@ -156,4 +179,4 @@ chat_history: []`}
);
};
export default TraceDetailsSidebar;
export default InvocationDetailsSidebar;

View File

@@ -4,17 +4,7 @@ import React, { useMemo } from 'react';
import { Card } from './Card';
import { getTimeAgo } from '../utils/lmpUtils';
import VersionBadge from './VersionBadge';
const lstrCleanStringify = (obj_containing_lstrs) => {
return JSON.stringify(obj_containing_lstrs, (key, value) => {
if (value && value.__lstr === true) {
return value.content;
}
return value;
}, 2);
};
import { lstrCleanStringify } from './lstrCleanStringify';
const TracesRunsPane = ({ invocations, onSelectTrace }) => {
const traces = useMemo(() => {
@@ -30,6 +20,7 @@ const TracesRunsPane = ({ invocations, onSelectTrace }) => {
}));
}, [invocations]);
const schema = {
columns: [
{

View File

@@ -0,0 +1,8 @@
export const lstrCleanStringify = (obj_containing_lstrs, indentLevel = 2) => {
return JSON.stringify(obj_containing_lstrs, (key, value) => {
if (value && value.__lstr === true) {
return value.content;
}
return value;
}, indentLevel);
};

View File

@@ -10,6 +10,7 @@ import VersionHistoryPane from '../components/VersionHistoryPane';
import LMPDetailsSidePanel from '../components/LMPDetailsSidePanel';
import toast, { Toaster } from 'react-hot-toast';
import { Link } from 'react-router-dom';
import TraceDetailsSidebar from '../components/TraceDetailsSidebar';
function LMP() {
const { name, id } = useParams();
@@ -19,6 +20,7 @@ function LMP() {
const [uses, setUses] = useState([]);
const { darkMode } = useTheme();
const [activeTab, setActiveTab] = useState('runs');
const [selectedTrace, setSelectedTrace] = useState(null);
const API_BASE_URL = "http://localhost:8080";
@@ -117,7 +119,7 @@ function LMP() {
</header>
<div className="flex-grow flex overflow-hidden">
<main className="flex-grow p-6 overflow-y-auto">
<main className="flex-grow p-6 overflow-y-auto hide-scrollbar">
<div className="mb-6 bg-[#1c1f26] rounded-lg p-4">
<div className="flex justify-between items-center mb-4">
<h2 className="text-lg font-semibold">Language Model Program</h2>
@@ -185,7 +187,7 @@ function LMP() {
<TracesRunsPane
invocations={invocations}
producingLmp={lmp}
onSelectTrace={(trace) => console.log('Selected trace:', trace)}
onSelectTrace={(trace) => setSelectedTrace(trace)}
/>
</>
)}
@@ -200,6 +202,12 @@ function LMP() {
versionHistory={versionHistory}
onSeeAllClick={handleSeeAllClick}
/>
{selectedTrace && (
<TraceDetailsSidebar
invocation={selectedTrace}
onClose={() => setSelectedTrace(null)}
/>
)}
</div>
</div>
</div>

View File

@@ -40,7 +40,7 @@ const Traces = () => {
return (
<div className="flex bg-[#0d1117] text-gray-300 h-screen overflow-hidden">
<div className="flex-grow p-6 overflow-y-auto">
<div className="flex-grow p-6 overflow-y-auto hide-scrollbar">
<div className="flex items-center mb-6">
<div className="flex items-center space-x-2 text-sm text-gray-400">
<span>Personal</span>
@@ -112,7 +112,7 @@ const Traces = () => {
</div>
{selectedTrace && (
<TraceDetailsSidebar
trace={selectedTrace}
invocation={selectedTrace}
onClose={() => setSelectedTrace(null)}
/>
)}

View File

@@ -1,5 +1,10 @@
.source-code-wrapper {
height: 70vh;
overflow: auto;
}
.source-code-container {
width: 100%;
height: 100%;
max-width: 100%;
overflow-x: auto;
}

View File

@@ -8,4 +8,13 @@ body,
body {
overflow: hidden;
}
.hide-scrollbar {
scrollbar-width: none; /* Firefox */
-ms-overflow-style: none; /* Internet Explorer and Edge */
}
.hide-scrollbar::-webkit-scrollbar {
display: none; /* Chrome, Safari, and Opera */
}

View File

@@ -75,9 +75,7 @@ export const fetchTraces = async (lmps) => {
export function getTimeAgo(date) {
const now = new Date();
console.log(date)
const secondsPast = (now.getTime() - date.getTime()) / 1000;
console.log(now, date);
if (secondsPast < 60) {
return `${Math.round(secondsPast)} seconds ago`;
}

View File

@@ -6,10 +6,6 @@ from setuptools import setup, find_packages
from setuptools.command.develop import develop
from setuptools.command.install import install
class NPMInstall(object):
def run_npm_install(self):
print("Running npm install")

View File

@@ -26,16 +26,8 @@ def main():
async def serve_react_app(full_path: str):
return FileResponse(os.path.join(static_dir, "index.html"))
if args.dev:
# In development mode, use watchfiles for auto-reloading
run_process(
os.path.dirname(__file__),
target=lambda: uvicorn.run(app, host=args.host, port=args.port, reload=True),
watch_filter=lambda change, path: path.endswith(".py")
)
else:
# In production mode, run without auto-reloading
uvicorn.run(app, host=args.host, port=args.port)
# In production mode, run without auto-reloading
uvicorn.run(app, host=args.host, port=args.port)
if __name__ == "__main__":
main()