mirror of
https://github.com/openclaw/openclaw.git
synced 2026-02-09 05:19:32 +08:00
fix: context overflow compaction and subagent announce improvements (#11664) (thanks @tyler6204)
* initial commit * feat: implement deriveSessionTotalTokens function and update usage tests * Added deriveSessionTotalTokens function to calculate total tokens based on usage and context tokens. * Updated usage tests to include cases for derived session total tokens. * Refactored session usage calculations in multiple files to utilize the new function for improved accuracy. * fix: restore overflow truncation fallback + changelog/test hardening (#11551) (thanks @tyler6204)
This commit is contained in:
@@ -564,27 +564,51 @@
|
||||
|
||||
/* Compaction indicator */
|
||||
.compaction-indicator {
|
||||
align-self: center;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 13px;
|
||||
padding: 10px 12px;
|
||||
line-height: 1.2;
|
||||
padding: 6px 14px;
|
||||
margin-bottom: 8px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--border);
|
||||
background: var(--panel-strong);
|
||||
color: var(--text);
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
animation: fade-in 0.2s var(--ease-out);
|
||||
}
|
||||
|
||||
.compaction-indicator svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
stroke: currentColor;
|
||||
fill: none;
|
||||
stroke-width: 1.5px;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.compaction-indicator--active {
|
||||
animation: compaction-pulse 1.5s ease-in-out infinite;
|
||||
color: var(--info);
|
||||
border-color: rgba(59, 130, 246, 0.35);
|
||||
}
|
||||
|
||||
.compaction-indicator--active svg {
|
||||
animation: compaction-spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.compaction-indicator--complete {
|
||||
animation: fade-in 0.2s var(--ease-out);
|
||||
color: var(--ok);
|
||||
border-color: rgba(34, 197, 94, 0.35);
|
||||
}
|
||||
|
||||
@keyframes compaction-pulse {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
@keyframes compaction-spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,68 @@ function createProps(overrides: Partial<ChatProps> = {}): ChatProps {
|
||||
}
|
||||
|
||||
describe("chat view", () => {
|
||||
it("renders compacting indicator as a badge", () => {
|
||||
const container = document.createElement("div");
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
compactionStatus: {
|
||||
active: true,
|
||||
startedAt: Date.now(),
|
||||
completedAt: null,
|
||||
},
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const indicator = container.querySelector(".compaction-indicator--active");
|
||||
expect(indicator).not.toBeNull();
|
||||
expect(indicator?.textContent).toContain("Compacting context...");
|
||||
});
|
||||
|
||||
it("renders completion indicator shortly after compaction", () => {
|
||||
const container = document.createElement("div");
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(1_000);
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
compactionStatus: {
|
||||
active: false,
|
||||
startedAt: 900,
|
||||
completedAt: 900,
|
||||
},
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
const indicator = container.querySelector(".compaction-indicator--complete");
|
||||
expect(indicator).not.toBeNull();
|
||||
expect(indicator?.textContent).toContain("Context compacted");
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("hides stale compaction completion indicator", () => {
|
||||
const container = document.createElement("div");
|
||||
const nowSpy = vi.spyOn(Date, "now").mockReturnValue(10_000);
|
||||
render(
|
||||
renderChat(
|
||||
createProps({
|
||||
compactionStatus: {
|
||||
active: false,
|
||||
startedAt: 0,
|
||||
completedAt: 0,
|
||||
},
|
||||
}),
|
||||
),
|
||||
container,
|
||||
);
|
||||
|
||||
expect(container.querySelector(".compaction-indicator")).toBeNull();
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
|
||||
it("shows a stop button when aborting is available", () => {
|
||||
const container = document.createElement("div");
|
||||
const onAbort = vi.fn();
|
||||
|
||||
@@ -85,7 +85,7 @@ function renderCompactionIndicator(status: CompactionIndicatorStatus | null | un
|
||||
// Show "compacting..." while active
|
||||
if (status.active) {
|
||||
return html`
|
||||
<div class="callout info compaction-indicator compaction-indicator--active">
|
||||
<div class="compaction-indicator compaction-indicator--active" role="status" aria-live="polite">
|
||||
${icons.loader} Compacting context...
|
||||
</div>
|
||||
`;
|
||||
@@ -96,7 +96,7 @@ function renderCompactionIndicator(status: CompactionIndicatorStatus | null | un
|
||||
const elapsed = Date.now() - status.completedAt;
|
||||
if (elapsed < COMPACTION_TOAST_DURATION_MS) {
|
||||
return html`
|
||||
<div class="callout success compaction-indicator compaction-indicator--complete">
|
||||
<div class="compaction-indicator compaction-indicator--complete" role="status" aria-live="polite">
|
||||
${icons.check} Context compacted
|
||||
</div>
|
||||
`;
|
||||
@@ -268,8 +268,6 @@ export function renderChat(props: ChatProps) {
|
||||
|
||||
${props.error ? html`<div class="callout danger">${props.error}</div>` : nothing}
|
||||
|
||||
${renderCompactionIndicator(props.compactionStatus)}
|
||||
|
||||
${
|
||||
props.focusMode
|
||||
? html`
|
||||
@@ -353,6 +351,8 @@ export function renderChat(props: ChatProps) {
|
||||
: nothing
|
||||
}
|
||||
|
||||
${renderCompactionIndicator(props.compactionStatus)}
|
||||
|
||||
${
|
||||
props.showNewMessages
|
||||
? html`
|
||||
|
||||
Reference in New Issue
Block a user