mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
AIX: Debugger: add Profiler
This commit is contained in:
@@ -11,7 +11,7 @@ import { presentErrorToHumans } from '~/common/util/errorUtils';
|
||||
import type { AixWire_Particles } from '../server/api/aix.wiretypes';
|
||||
|
||||
import type { AixClientDebugger, AixFrameId } from './debugger/memstore-aix-client-debugger';
|
||||
import { aixClientDebugger_completeFrame, aixClientDebugger_init, aixClientDebugger_recordParticleReceived, aixClientDebugger_setRequest } from './debugger/reassembler-debug';
|
||||
import { aixClientDebugger_completeFrame, aixClientDebugger_init, aixClientDebugger_recordParticleReceived, aixClientDebugger_setProfilerMeasurements, aixClientDebugger_setRequest } from './debugger/reassembler-debug';
|
||||
|
||||
import { AixChatGenerateContent_LL, DEBUG_PARTICLES } from './aix.client';
|
||||
|
||||
@@ -243,6 +243,8 @@ export class ContentReassembler {
|
||||
aixClientDebugger_setRequest(this.debuggerFrameId, op.dispatchRequest);
|
||||
break;
|
||||
case '_debugProfiler':
|
||||
if (this.debuggerFrameId)
|
||||
aixClientDebugger_setProfilerMeasurements(this.debuggerFrameId, op.measurements);
|
||||
// Profiling particles will come in if the app is in "Debug Mode" + it's a Development build!
|
||||
// Additionally to show them on the console (rather than just in the debugger) set the
|
||||
// constant to `true`.
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Box, Card, Chip, Typography } from '@mui/joy';
|
||||
import { Box, Card, Chip, Divider, Typography } from '@mui/joy';
|
||||
|
||||
import { ChipToggleButton } from '~/common/components/ChipToggleButton';
|
||||
import TimelapseIcon from '@mui/icons-material/Timelapse';
|
||||
|
||||
import type { AixClientDebugger } from './memstore-aix-client-debugger';
|
||||
import { AixDebuggerMeasurementsTable } from './AixDebuggerMeasurementsTable';
|
||||
|
||||
|
||||
const _styles = {
|
||||
@@ -14,6 +16,7 @@ const _styles = {
|
||||
boxShadow: 'inset 2px 0 4px -2px rgba(0, 0, 0, 0.2)',
|
||||
fontFamily: 'code',
|
||||
fontSize: 'xs',
|
||||
p: 1.5,
|
||||
gap: 1,
|
||||
} as const,
|
||||
|
||||
@@ -66,11 +69,11 @@ export function AixDebuggerFrame(props: {
|
||||
<Box sx={{ fontSize: 'sm', display: 'grid', gridTemplateColumns: { xs: 'auto 1fr', md: 'auto 1fr auto 1fr' }, gap: 1, alignItems: 'center' }}>
|
||||
<Typography fontWeight='bold'>Request </Typography>
|
||||
<Typography fontWeight='bold'>{frame.id}</Typography>
|
||||
<div>status</div>
|
||||
<div>Status:</div>
|
||||
<Chip variant='soft' color={frame.isComplete ? 'success' : 'warning'}>{frame.isComplete ? 'Complete' : 'In Progress'}</Chip>
|
||||
<div>Date</div>
|
||||
<div>{new Date(frame.timestamp).toLocaleString()}</div>
|
||||
<div>URL:</div>
|
||||
<div>-> URL:</div>
|
||||
<Chip>{frame.url || 'No URL data available'}</Chip>
|
||||
<div>Context:</div>
|
||||
<Chip>{frame.context.contextName}</Chip>
|
||||
@@ -79,28 +82,42 @@ export function AixDebuggerFrame(props: {
|
||||
</Box>
|
||||
|
||||
{/* Headers */}
|
||||
<Typography color='warning' level='title-md' mb={-2}>
|
||||
Headers:
|
||||
</Typography>
|
||||
<Card variant='soft' color='warning' sx={_styles.requestCard}>
|
||||
<Typography color='warning' variant='soft' level='title-sm'>
|
||||
-> Headers
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Box sx={_styles.requestCardText}>
|
||||
{frame.headers || 'No headers data available'}
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
{/* Body */}
|
||||
<Typography color='primary' level='title-md' mb={-2}>
|
||||
Body:
|
||||
</Typography>
|
||||
<Card variant='soft' color='primary' sx={_styles.requestCard}>
|
||||
<Typography color='primary' variant='soft' level='title-sm'>
|
||||
-> Body
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Box sx={_styles.requestCardText}>
|
||||
{frame.body || 'No headers data available'}
|
||||
</Box>
|
||||
</Card>
|
||||
|
||||
{/* Performance Profiler */}
|
||||
<Card variant='soft' color='success' sx={_styles.requestCard}>
|
||||
<Typography color='success' variant='soft' level='title-sm' startDecorator={<TimelapseIcon />}>
|
||||
Internal Profiler:
|
||||
</Typography>
|
||||
{!!frame.profilerMeasurements?.length ? (
|
||||
<AixDebuggerMeasurementsTable measurements={frame.profilerMeasurements} />
|
||||
) : (
|
||||
'No profiler measurements available. Note: profiling is not available in production.'
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Particles List */}
|
||||
<Box mb={showParticles ? -2 : undefined} sx={_styles.particleNorminal}>
|
||||
<Typography level='title-md'>
|
||||
<Typography level='title-sm'>
|
||||
Particles {frame.particles.length > 0 && `(${frame.particles.length})`}
|
||||
{!frame.isComplete && ' • Streaming...'}
|
||||
</Typography>
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Table, Typography } from '@mui/joy';
|
||||
|
||||
import type { AixClientDebugger } from './memstore-aix-client-debugger';
|
||||
|
||||
|
||||
export function AixDebuggerMeasurementsTable(props: {
|
||||
measurements: AixClientDebugger.Measurements
|
||||
}) {
|
||||
|
||||
// empty placeholder
|
||||
if (!props.measurements?.length)
|
||||
return (
|
||||
<Typography level='body-sm' fontStyle='italic'>
|
||||
No performance measurements available
|
||||
</Typography>
|
||||
);
|
||||
|
||||
// assume the keys of the first measurement are uniform across all measurements
|
||||
const headers = Object.keys(props.measurements[0]);
|
||||
|
||||
return (
|
||||
<Table
|
||||
size='sm'
|
||||
variant='outlined'
|
||||
sx={{
|
||||
|
||||
backgroundColor: 'background.surface',
|
||||
'& th': { fontWeight: 'bold', whiteSpace: 'nowrap', p: 1 },
|
||||
'& td': { fontFamily: 'code', p: 1 },
|
||||
}}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
{headers.map(header => (
|
||||
<th key={header}>{header}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{props.measurements.map((measurement, index) => (
|
||||
<tr key={index}>
|
||||
{headers.map(header => {
|
||||
const value = measurement[header];
|
||||
// Format percentages with 1 decimal place
|
||||
const displayValue = header === 'percent' && typeof value === 'number'
|
||||
? `${value.toFixed(1)}%`
|
||||
: value;
|
||||
return <td key={header}>{displayValue}</td>;
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
@@ -23,11 +23,15 @@ export namespace AixClientDebugger {
|
||||
headers: string;
|
||||
body: string;
|
||||
isComplete: boolean;
|
||||
// upstream profiler measurements
|
||||
profilerMeasurements?: Measurements;
|
||||
// NOTE: in the future we could debug the raw SSE streams.. not now
|
||||
// aix response particles
|
||||
particles: Particle[];
|
||||
}
|
||||
|
||||
export type Measurements = Record<string, string | number>[];
|
||||
|
||||
export interface Particle {
|
||||
timestamp: number;
|
||||
content: Record<string, any>;
|
||||
@@ -74,6 +78,7 @@ interface AixClientDebuggerActions {
|
||||
// frames
|
||||
createFrame: (initialContext: AixClientDebugger.Context) => AixFrameId;
|
||||
setRequest: (fId: AixFrameId, updates: Pick<AixClientDebugger.Frame, 'url' | 'headers' | 'body'>) => void;
|
||||
setProfilerMeasurements: (fId: AixFrameId, measurements: AixClientDebugger.Measurements) => void;
|
||||
addParticle: (fId: AixFrameId, particle: AixClientDebugger.Particle, isAborted?: boolean) => void;
|
||||
completeFrame: (fId: AixFrameId) => void;
|
||||
|
||||
@@ -115,6 +120,14 @@ export const useAixClientDebuggerStore = create<AixClientDebuggerStore>((_set) =
|
||||
}),
|
||||
})),
|
||||
|
||||
setProfilerMeasurements: (fId, measurements) =>
|
||||
_set(state => ({
|
||||
frames: state.frames.map(frame => frame.id !== fId ? frame : {
|
||||
...frame,
|
||||
profilerMeasurements: measurements,
|
||||
}),
|
||||
})),
|
||||
|
||||
addParticle: (fId, particle, isAborted = false) =>
|
||||
_set(state => ({
|
||||
frames: state.frames.map(frame => frame.id !== fId ? frame : {
|
||||
|
||||
@@ -16,6 +16,13 @@ export function aixClientDebugger_setRequest(
|
||||
});
|
||||
}
|
||||
|
||||
export function aixClientDebugger_setProfilerMeasurements(
|
||||
frameId: AixFrameId,
|
||||
measurements: AixClientDebugger.Measurements,
|
||||
): void {
|
||||
useAixClientDebuggerStore.getState().setProfilerMeasurements(frameId, measurements);
|
||||
}
|
||||
|
||||
export function aixClientDebugger_recordParticleReceived(frameId: AixFrameId, particleContent: Record<string, any>, isAborted = false): void {
|
||||
useAixClientDebuggerStore.getState().addParticle(frameId, {
|
||||
timestamp: Date.now(),
|
||||
|
||||
Reference in New Issue
Block a user