[eric] perf+ui: shrink runs chunks in parallel (5min -> ~1min) and shows a live pulsing label so users know its working

This commit is contained in:
eric
2026-05-31 18:50:46 -07:00
parent 56caabebbc
commit 11b7d9b7a4
2 changed files with 44 additions and 7 deletions
+9 -4
View File
@@ -528,10 +528,15 @@ async def summarize_file(req: _SummarizeRequest):
else:
chunks = [raw[i:i + CHUNK_CHARS] for i in range(0, len(raw), CHUNK_CHARS)]
per_chunk_budget = max(800, req.target_tokens // len(chunks) + 600)
partials = []
for i, ch in enumerate(chunks):
label = f"{os.path.basename(src)} (part {i + 1} of {len(chunks)})"
partials.append(await _summarize_block(ch, per_chunk_budget, label))
# Parallel summarization. Sequential was N chunks * ~60s each
# (5+ min wall time for a 4-chunk PDF on Haiku). Aux providers
# all handle parallel requests fine; the only ceiling is the
# provider's per-key rate limit, and a single user summarizing
# one file will never hit that.
partials = await asyncio.gather(*[
_summarize_block(ch, per_chunk_budget, f"{os.path.basename(src)} (part {i + 1} of {len(chunks)})")
for i, ch in enumerate(chunks)
])
merge_input = "\n\n".join(f"## Part {i + 1}\n{p}" for i, p in enumerate(partials))
summary = await _summarize_block(merge_input, req.target_tokens, f"merged summary of {os.path.basename(src)}")
except Exception as e:
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import Box from '@mui/material/Box';
import IconButton from '@mui/material/IconButton';
import Modal from '@mui/material/Modal';
@@ -6,6 +6,37 @@ import Snackbar from '@mui/material/Snackbar';
import CloseIcon from '@mui/icons-material/Close';
import { ClaudeTokens } from '@/shared/styles/claudeTokens';
const SHRINK_PROGRESS_MESSAGES = [
'Reading the file',
'Pulling out the key bits',
'Compressing',
'Almost done',
];
function ShrinkingLabel() {
const [idx, setIdx] = useState(0);
useEffect(() => {
const t = setInterval(() => setIdx((i) => (i + 1) % SHRINK_PROGRESS_MESSAGES.length), 4000);
return () => clearInterval(t);
}, []);
return (
<Box component="span" sx={{ display: 'inline-flex', alignItems: 'center', gap: 0.5 }}>
<Box component="span" sx={{
display: 'inline-block', width: 8, height: 8, borderRadius: '50%',
bgcolor: 'currentColor', opacity: 0.85,
animation: 'osw-pulse 1.2s ease-in-out infinite',
'@keyframes osw-pulse': {
'0%, 100%': { transform: 'scale(0.6)', opacity: 0.45 },
'50%': { transform: 'scale(1)', opacity: 0.95 },
},
}} />
<Box component="span" sx={{ minWidth: 130, textAlign: 'left' }}>
{SHRINK_PROGRESS_MESSAGES[idx]}
</Box>
</Box>
);
}
interface Props {
c: ClaudeTokens;
lightboxSrc: string | null;
@@ -100,11 +131,12 @@ export const ChatInputOverlays: React.FC<Props> = ({
border: 'none', borderRadius: '6px',
px: 1.5, py: 0.7, fontSize: '0.82rem', fontWeight: 500, cursor: 'pointer',
whiteSpace: 'nowrap',
transition: 'background 0.15s ease, opacity 0.15s ease',
'&:hover': { bgcolor: c.accent.hover },
'&:disabled': { opacity: 0.6, cursor: 'wait' },
'&:disabled': { opacity: 0.85, cursor: 'wait', bgcolor: c.accent.primary },
}}
>
{summarizingPath === oversizeQueue[0]?.path ? 'Shrinking…' : 'Shrink it'}
{summarizingPath === oversizeQueue[0]?.path ? <ShrinkingLabel /> : 'Shrink it'}
</Box>
<Box
component="button"