feat: add code/diff toggle for optimization candidates
Add ability to switch between full optimized code view and unified diff view for each optimization candidate, making it easier to understand what changes were made during optimization.
This commit is contained in:
parent
130e3507bd
commit
4dae4b3a35
2 changed files with 187 additions and 3 deletions
|
|
@ -253,6 +253,7 @@ async function TraceContent({ traceId }: { traceId: string }) {
|
|||
instrumentedTests={instrumentedTests}
|
||||
instrumentedPerfTests={instrumentedPerfTests}
|
||||
testFramework={optimizationFeatures?.test_framework ?? null}
|
||||
originalCode={optimizationFeatures?.original_code ?? null}
|
||||
/>
|
||||
|
||||
{/* Errors */}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,11 @@ import {
|
|||
Sparkles,
|
||||
Activity,
|
||||
FlaskConical,
|
||||
GitCompare,
|
||||
} from "lucide-react"
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
|
||||
import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"
|
||||
import { createTwoFilesPatch } from "diff"
|
||||
import { CopyButton } from "@/components/observability/copy-button"
|
||||
import { InfoIcon } from "@/components/observability/info-icon"
|
||||
import {
|
||||
|
|
@ -153,6 +155,182 @@ const CodeFileDisplay = memo(function CodeFileDisplay({
|
|||
)
|
||||
})
|
||||
|
||||
interface CandidateCodeDisplayProps {
|
||||
candidateCode: string
|
||||
originalCode: string | null
|
||||
isExpanded: boolean
|
||||
onToggle: () => void
|
||||
}
|
||||
|
||||
/** Component for displaying optimized code with toggle between full code and diff view */
|
||||
const CandidateCodeDisplay = memo(function CandidateCodeDisplay({
|
||||
candidateCode,
|
||||
originalCode,
|
||||
isExpanded,
|
||||
onToggle,
|
||||
}: CandidateCodeDisplayProps) {
|
||||
const [viewMode, setViewMode] = useState<"code" | "diff">("code")
|
||||
|
||||
const parsedCandidate = parseCodeBlock(candidateCode)
|
||||
|
||||
// Parse original code to find matching file
|
||||
const parsedOriginal = useMemo(() => {
|
||||
if (!originalCode) return null
|
||||
|
||||
// Parse all files from original code markdown
|
||||
const files: ParsedCodeBlock[] = []
|
||||
const regex = /```(\w+):([^\n]+)\n([\s\S]*?)```/g
|
||||
let match
|
||||
while ((match = regex.exec(originalCode)) !== null) {
|
||||
const [, language, path, code] = match
|
||||
files.push({
|
||||
language: language || "python",
|
||||
filename: path.split("/").pop() || null,
|
||||
path: path || null,
|
||||
code: code.trimEnd(),
|
||||
})
|
||||
}
|
||||
|
||||
// Find matching file by path or filename
|
||||
if (parsedCandidate.path) {
|
||||
const matchingFile = files.find(f =>
|
||||
f.path === parsedCandidate.path ||
|
||||
(f.path && parsedCandidate.path && f.path.endsWith(parsedCandidate.path)) ||
|
||||
(f.path && parsedCandidate.path && parsedCandidate.path.endsWith(f.path))
|
||||
)
|
||||
if (matchingFile) return matchingFile
|
||||
}
|
||||
|
||||
// Return first file as fallback
|
||||
return files[0] || null
|
||||
}, [originalCode, parsedCandidate.path])
|
||||
|
||||
// Generate unified diff
|
||||
const unifiedDiff = useMemo(() => {
|
||||
if (!parsedOriginal) return null
|
||||
|
||||
const filename = parsedCandidate.filename || parsedOriginal.filename || "code"
|
||||
const diff = createTwoFilesPatch(
|
||||
`a/${filename}`,
|
||||
`b/${filename}`,
|
||||
parsedOriginal.code,
|
||||
parsedCandidate.code,
|
||||
"original",
|
||||
"optimized",
|
||||
{ context: 3 }
|
||||
)
|
||||
return diff
|
||||
}, [parsedOriginal, parsedCandidate])
|
||||
|
||||
const hasDiff = parsedOriginal !== null
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* File header with view toggle */}
|
||||
<div
|
||||
className="px-4 py-2 flex items-center justify-between bg-gray-100 dark:bg-gray-800"
|
||||
>
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={onToggle}
|
||||
onKeyDown={e => { if (e.key === "Enter" || e.key === " ") onToggle() }}
|
||||
className="flex items-center gap-2 cursor-pointer hover:opacity-80"
|
||||
>
|
||||
<FileText className="h-3.5 w-3.5 text-gray-400" />
|
||||
{parsedCandidate.filename ? (
|
||||
<>
|
||||
<span className="text-sm font-mono font-medium text-gray-900 dark:text-white">
|
||||
{parsedCandidate.filename}
|
||||
</span>
|
||||
{parsedCandidate.path && parsedCandidate.path !== parsedCandidate.filename && (
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400" title={parsedCandidate.path}>
|
||||
({parsedCandidate.path})
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<span className="text-sm font-medium text-gray-700 dark:text-gray-300">
|
||||
Optimized Code
|
||||
</span>
|
||||
)}
|
||||
{isExpanded ? (
|
||||
<ChevronDown className="h-3.5 w-3.5 text-gray-400" />
|
||||
) : (
|
||||
<ChevronRight className="h-3.5 w-3.5 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{/* View toggle */}
|
||||
{hasDiff && (
|
||||
<div className="flex items-center bg-gray-200 dark:bg-gray-700 rounded-md p-0.5">
|
||||
<button
|
||||
onClick={() => setViewMode("code")}
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors flex items-center gap-1 ${
|
||||
viewMode === "code"
|
||||
? "bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm"
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<Code className="h-3 w-3" />
|
||||
Code
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setViewMode("diff")}
|
||||
className={`px-2 py-1 text-xs font-medium rounded transition-colors flex items-center gap-1 ${
|
||||
viewMode === "diff"
|
||||
? "bg-white dark:bg-gray-600 text-gray-900 dark:text-white shadow-sm"
|
||||
: "text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<GitCompare className="h-3 w-3" />
|
||||
Diff
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<span className="text-xs text-gray-500 dark:text-gray-400">
|
||||
{parsedCandidate.code.split("\n").length} lines
|
||||
</span>
|
||||
<CopyButton
|
||||
text={viewMode === "diff" && unifiedDiff ? unifiedDiff : parsedCandidate.code}
|
||||
label={viewMode === "diff" ? "diff" : "code"}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Code/Diff content */}
|
||||
{isExpanded && (
|
||||
<div className="max-h-[500px] overflow-y-auto">
|
||||
{viewMode === "code" ? (
|
||||
<SyntaxHighlighter
|
||||
language={parsedCandidate.language}
|
||||
style={oneDark}
|
||||
customStyle={{ margin: 0, padding: "1rem", fontSize: "0.875rem", lineHeight: 1.5 }}
|
||||
showLineNumbers
|
||||
>
|
||||
{parsedCandidate.code}
|
||||
</SyntaxHighlighter>
|
||||
) : unifiedDiff ? (
|
||||
<SyntaxHighlighter
|
||||
language="diff"
|
||||
style={oneDark}
|
||||
customStyle={{ margin: 0, padding: "1rem", fontSize: "0.875rem", lineHeight: 1.5 }}
|
||||
showLineNumbers
|
||||
>
|
||||
{unifiedDiff}
|
||||
</SyntaxHighlighter>
|
||||
) : (
|
||||
<div className="p-4 text-sm text-gray-500 dark:text-gray-400">
|
||||
No original code available for comparison
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
interface LLMCall {
|
||||
id: string
|
||||
call_type: string | null
|
||||
|
|
@ -220,6 +398,7 @@ interface LLMCallsTimelineProps {
|
|||
instrumentedTests: InstrumentedTest[]
|
||||
instrumentedPerfTests: InstrumentedPerfTest[]
|
||||
testFramework: string | null
|
||||
originalCode: string | null
|
||||
}
|
||||
|
||||
function getModelColorClasses(modelName: string): string {
|
||||
|
|
@ -262,6 +441,7 @@ interface CallItemProps {
|
|||
instrumentedTest: InstrumentedTest | undefined
|
||||
instrumentedPerfTest: InstrumentedPerfTest | undefined
|
||||
testFramework: string | null
|
||||
originalCode: string | null
|
||||
onSummaryClick: (e: MouseEvent<HTMLElement>) => void
|
||||
onToggleSection: (callId: string, section: string) => void
|
||||
}
|
||||
|
|
@ -278,6 +458,7 @@ const CallItem = memo(function CallItem({
|
|||
instrumentedTest,
|
||||
instrumentedPerfTest,
|
||||
testFramework,
|
||||
originalCode,
|
||||
onSummaryClick,
|
||||
onToggleSection,
|
||||
}: CallItemProps) {
|
||||
|
|
@ -409,11 +590,11 @@ const CallItem = memo(function CallItem({
|
|||
</p>
|
||||
)}
|
||||
</div>
|
||||
<CodeFileDisplay
|
||||
rawCode={candidate.code}
|
||||
<CandidateCodeDisplay
|
||||
candidateCode={candidate.code}
|
||||
originalCode={originalCode}
|
||||
isExpanded={isSectionExpanded("candidate")}
|
||||
onToggle={() => handleToggleSection("candidate")}
|
||||
label="Optimized Code"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -521,6 +702,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
instrumentedTests,
|
||||
instrumentedPerfTests,
|
||||
testFramework,
|
||||
originalCode,
|
||||
}: LLMCallsTimelineProps) {
|
||||
const [expandedCalls, setExpandedCalls] = useState<Set<string>>(new Set())
|
||||
const [expandedSections, setExpandedSections] = useState<Record<string, Set<string>>>({})
|
||||
|
|
@ -734,6 +916,7 @@ export const LLMCallsTimeline = memo(function LLMCallsTimeline({
|
|||
instrumentedTest={callToInstrumentedTestMap.get(call.id)}
|
||||
instrumentedPerfTest={callToInstrumentedPerfTestMap.get(call.id)}
|
||||
testFramework={testFramework}
|
||||
originalCode={originalCode}
|
||||
onSummaryClick={handleSummaryClick}
|
||||
onToggleSection={toggleSection}
|
||||
/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue