mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-07 10:17:43 +02:00
[eric] ux: if one message is too long to ever fit, block it with a plain 'shorten or split it' note instead of a pointless auto-compact loop
This commit is contained in:
@@ -149,12 +149,17 @@ const ChatInput = forwardRef<ChatInputHandle, Props>(({ onSend, disabled, mode,
|
||||
contextPaths, sessionFrameworkOverhead,
|
||||
});
|
||||
if (block) {
|
||||
// Auto-compact instead of prompting. Conversation history is the only
|
||||
// overflow source we can shrink without losing user content — files were
|
||||
// already auto-shrunk above, the prompt itself is the message the user
|
||||
// just wrote, MCPs are framework. So if we're over, hit /compact, capture
|
||||
// the send intent, and let the next-message effect fire it after the
|
||||
// server-side compaction acks. User did nothing; problem solved silently.
|
||||
if (block.kind === 'too_long') {
|
||||
// This one message is too big to send even with zero history, so
|
||||
// compaction can't save it. Hard-block and tell the user plainly; they
|
||||
// shorten it and the block clears on the next send attempt. Don't fire
|
||||
// /compact (pointless) and don't queue a retry (it'd just re-block).
|
||||
pendingSendRef.current = null;
|
||||
setSendBlock(block);
|
||||
return;
|
||||
}
|
||||
// kind === 'compacting': history is the overflow source, which we CAN
|
||||
// shrink. Auto-compact, capture the send intent, flash a status banner.
|
||||
if (sessionId) {
|
||||
pendingSendRef.current = () => { handleSend(); };
|
||||
try {
|
||||
@@ -164,14 +169,14 @@ const ChatInput = forwardRef<ChatInputHandle, Props>(({ onSend, disabled, mode,
|
||||
await fetch(`${API_BASE}/agents/sessions/${sessionId}/compact`, { method: 'POST', headers });
|
||||
} catch (err) { console.error('[auto-compact] failed:', err); pendingSendRef.current = null; }
|
||||
}
|
||||
// Briefly flash the banner as a status (not a prompt) so the user sees
|
||||
// something happened. Auto-clear after 2s; in 99% of cases the auto-retry
|
||||
// send has already fired by then.
|
||||
setSendBlock(block);
|
||||
setTimeout(() => setSendBlock(null), 2000);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fits now (e.g. user shortened a too-long message): clear any lingering block banner.
|
||||
setSendBlock(null);
|
||||
|
||||
onboardingBus.emit('chat:message_sent');
|
||||
if (window.location.hash.includes('/apps/')) {
|
||||
onboardingBus.emit('app:generation_started');
|
||||
|
||||
@@ -17,6 +17,9 @@ function shrinkThreshold(modelCtx: number): number {
|
||||
}
|
||||
|
||||
export type SendBlock = null | {
|
||||
// 'compacting' = history overflow, auto-compact can fix it.
|
||||
// 'too_long' = this single message exceeds the window on its own; hard block.
|
||||
kind: 'compacting' | 'too_long';
|
||||
estimate: number;
|
||||
window: number;
|
||||
history: number;
|
||||
|
||||
@@ -35,7 +35,16 @@ export function computeSendBlock({ trimmed, currentModelCtx, historyUsed, contex
|
||||
for (const cp of contextPaths) {
|
||||
if ((cp.tokens || 0) > (largest?.tokens || 0)) largest = { path: cp.path, tokens: cp.tokens || 0 };
|
||||
}
|
||||
// Distinguish "history is the culprit" (compaction can fix it) from "this one
|
||||
// message is too big on its own" (compaction can't help: dropping all prior
|
||||
// turns still leaves framework+files+prompt over the window). The latter only
|
||||
// happens with a giant pasted prompt, since attached files auto-shrink on
|
||||
// attach. We surface that as a hard block instead of a doomed compact loop.
|
||||
const nonHistory = framework + filesSum + promptTokens + systemTokens;
|
||||
const kind: 'compacting' | 'too_long' =
|
||||
nonHistory > Math.floor(win * 0.95) ? 'too_long' : 'compacting';
|
||||
return {
|
||||
kind,
|
||||
estimate, window: win,
|
||||
history, system: systemTokens, framework, files: filesSum, prompt: promptTokens,
|
||||
largestFile: largest,
|
||||
|
||||
@@ -9,9 +9,26 @@ interface Props {
|
||||
c: ClaudeTokens;
|
||||
}
|
||||
|
||||
/** Status indicator, not a prompt. Auto-compact already fired from handleSend; this
|
||||
* just lets the user know we're freeing up space so the send doesn't look frozen. */
|
||||
/** Two states:
|
||||
* - 'compacting': auto-compact already fired; pulsing-dot status so the send
|
||||
* doesn't look frozen while we free up room.
|
||||
* - 'too_long': this single message can't fit no matter what; a plain, friendly
|
||||
* block telling the user to shorten it (no jargon, no spinner). */
|
||||
export const SendBlockBanner: React.FC<Props> = ({ sendBlock, c }) => {
|
||||
if (sendBlock.kind === 'too_long') {
|
||||
return (
|
||||
<Box sx={{
|
||||
mx: 1.5, mt: 1, mb: 0.5, px: 2, py: 1.25,
|
||||
borderRadius: '12px',
|
||||
bgcolor: c.bg.surface,
|
||||
border: `1px solid ${c.border.medium}`,
|
||||
}}>
|
||||
<Typography sx={{ fontSize: '0.88rem', color: c.text.primary, lineHeight: 1.45 }}>
|
||||
That message is too long to send. Try shortening it or splitting it into a few smaller ones.
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Box sx={{
|
||||
mx: 1.5, mt: 1, mb: 0.5, px: 2, py: 1.25,
|
||||
|
||||
Reference in New Issue
Block a user