Files
deerflow-code/frontend-web/src/components/workspace/messages/speed-metrics.test.ts
T

120 lines
3.3 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
// Node's type-stripping test runner requires the explicit TypeScript suffix.
// @ts-expect-error The application compiler intentionally uses extensionless imports.
import {
GENERATION_IDLE_MS,
ROLLING_WINDOW_MS,
charsToTokensPerSec,
messageHasToolCalls,
rollingTokensPerSec,
shouldFreezeGeneration,
usageTokensPerSec,
} from "./speed-metrics.ts";
test("rolling window reports the recent rate while characters grow", () => {
const samples = [
{ at: 0, chars: 0 },
{ at: 500, chars: 100 },
{ at: 1000, chars: 200 },
];
const rate = rollingTokensPerSec(samples, 200, 1000, 1000);
// 200 chars / 1000ms / 2 chars-per-token = 100 tok/s
assert.equal(rate, 100);
});
test("rolling window drops to 0 when every sample is older than the window", () => {
const samples = [
{ at: 0, chars: 0 },
{ at: 200, chars: 80 },
{ at: 400, chars: 160 },
];
const now = 400 + ROLLING_WINDOW_MS + 50;
assert.equal(rollingTokensPerSec(samples, 160, now), 0);
});
test("rolling window decays toward 0 after a mid-stream pause", () => {
const samples = [
{ at: 0, chars: 0 },
{ at: 400, chars: 80 },
];
const live = rollingTokensPerSec(samples, 80, 500, 1000);
const paused = rollingTokensPerSec(samples, 80, 900, 1000);
assert.ok(live > paused);
assert.ok(paused > 0);
assert.equal(rollingTokensPerSec(samples, 80, 400 + 1000 + 1, 1000), 0);
});
test("usageTokensPerSec is output tokens over the segment duration", () => {
assert.equal(usageTokensPerSec(278, 6200), (278 / 6200) * 1000);
assert.equal(usageTokensPerSec(0, 1000), 0);
assert.equal(usageTokensPerSec(10, 0), 0);
});
test("charsToTokensPerSec uses the 2-chars-per-token heuristic", () => {
assert.equal(charsToTokensPerSec(100, 1000), 50);
});
test("shouldFreezeGeneration ends the segment on the next call, tools, idle usage, or run stop", () => {
const base = {
endedAt: null,
isLoading: true,
isLatestGenerating: true,
hasToolCalls: false,
grewThisTick: false,
lastGrewAt: 1000,
now: 1100,
outputTokens: 0,
};
assert.equal(shouldFreezeGeneration(base), false);
assert.equal(
shouldFreezeGeneration({ ...base, isLatestGenerating: false }),
true,
);
assert.equal(shouldFreezeGeneration({ ...base, hasToolCalls: true }), true);
assert.equal(
shouldFreezeGeneration({
...base,
hasToolCalls: true,
grewThisTick: true,
}),
false,
);
assert.equal(
shouldFreezeGeneration({
...base,
outputTokens: 40,
now: 1000 + GENERATION_IDLE_MS,
}),
true,
);
assert.equal(
shouldFreezeGeneration({
...base,
outputTokens: 40,
now: 1000 + GENERATION_IDLE_MS - 1,
}),
false,
);
assert.equal(shouldFreezeGeneration({ ...base, isLoading: false }), true);
assert.equal(shouldFreezeGeneration({ ...base, endedAt: 1000 }), false);
});
test("messageHasToolCalls reads both top-level and additional_kwargs", () => {
assert.equal(messageHasToolCalls({}), false);
assert.equal(
messageHasToolCalls({
tool_calls: [{ id: "1", name: "search" }],
}),
true,
);
assert.equal(
messageHasToolCalls({
additional_kwargs: { tool_calls: [{ id: "1" }] },
}),
true,
);
});