Skip to content

Commit a4bf186

Browse files
committed
feat(app): count the reloaded conversation in a turn's tokens
The conversation sent again after a pause is real spending of the plan, and the separate "reloaded" count next to the total confused people. The turn line, its tooltip and the usage dialog now show one number, input + cacheWrite + output. The daemon still records reloaded.
1 parent 143e14a commit a4bf186

14 files changed

Lines changed: 39 additions & 102 deletions

File tree

‎app/src/features/account/TokensByBot.tsx‎

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
// Tokens each bot used over a period (spec 8.7), in the usage dialog: the
2-
// new text read plus the replies written, with the conversation sent
3-
// again after a pause shown apart. Each bot shows its crew, as two crews can have
2+
// text read plus the replies written. Each bot shows its crew, as two crews can have
43
// bots with the same name.
54

65
import { useEffect, useState } from "react";
76
import { useT } from "../../i18n";
87
import { errorText } from "../../lib/api";
9-
import { reloadedTokens, tokens, usedTokens } from "../../lib/format";
8+
import { tokens, usedTokens } from "../../lib/format";
109
import type { BotTokens } from "../../lib/protocol.gen";
1110
import { useApi } from "../../store/context";
1211
import { Callout } from "../../ui/Callout";
@@ -99,9 +98,7 @@ function Row({ bot, most }: { bot: BotTokens; most: number }) {
9998
style={{ width: `${(used / most) * 100}%` }}
10099
/>
101100
</div>
102-
<span className="text-muted text-xs">
103-
{u.detail(bot.crew, bot.turns, reloadedTokens(bot.tokens.reloaded))}
104-
</span>
101+
<span className="text-muted text-xs">{u.detail(bot.crew, bot.turns)}</span>
105102
</div>
106103
</li>
107104
);
@@ -111,14 +108,13 @@ function Total({ bots }: { bots: BotTokens[] }) {
111108
const u = useT().account.usage.tokens;
112109
const used = bots.reduce((sum, bot) => sum + usedTokens(bot.tokens), 0);
113110
const turns = bots.reduce((sum, bot) => sum + bot.turns, 0);
114-
const reloaded = bots.reduce((sum, bot) => sum + bot.tokens.reloaded, 0);
115111
return (
116112
<li className="flex flex-col gap-1 border-line border-t pt-3">
117113
<div className="flex items-baseline justify-between gap-3 font-semibold">
118114
<span>{u.total}</span>
119115
<span className="tabular-nums">{tokens(used)}</span>
120116
</div>
121-
<span className="text-muted text-xs">{u.detail(null, turns, reloadedTokens(reloaded))}</span>
117+
<span className="text-muted text-xs">{u.detail(null, turns)}</span>
122118
</li>
123119
);
124120
}

‎app/src/features/account/tokens.test.tsx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ describe("tokens by bot", () => {
4545
expect(dialog.textContent).toContain("All bots9.8k");
4646
});
4747

48-
test("the conversation sent again after a pause counts apart", async () => {
48+
test("the conversation sent again after a pause counts in the total", async () => {
4949
const fake = new FakeBotloft();
5050
const ops = fake.addCrew("Ops");
5151
const scout = fake.addBot(ops.id, "Scout", "Finds sources");
@@ -64,7 +64,7 @@ describe("tokens by bot", () => {
6464
within(list)
6565
.getAllByRole("listitem")
6666
.map((row) => row.textContent),
67-
).toEqual(["Scout4.1kOps · Worked 2 times · 72k reloaded"]);
67+
).toEqual(["Scout76.1kOps · Worked 2 times"]);
6868
});
6969

7070
test("bots with the same name in two crews show their crew", async () => {

‎app/src/features/chat/BotRun.tsx‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import { CircleAlert } from "lucide-react";
66
import { memo, type ReactNode } from "react";
77
import { useT } from "../../i18n";
8-
import { duration, reloadedTokens, tokens, usedTokens, when } from "../../lib/format";
8+
import { duration, tokens, usedTokens, when } from "../../lib/format";
99
import type { Bot, ChatItem, TurnItem } from "../../lib/protocol.gen";
1010
import { useArrival } from "../../ui/motion";
1111
import { BotAvatar, moodOf } from "../bots/BotAvatar";
@@ -33,15 +33,13 @@ function TurnEnd({ turn }: { turn: TurnItem }) {
3333
}
3434
const time = duration(turn.durationMs);
3535
const used = turn.tokens;
36-
const reloaded = used && reloadedTokens(used.reloaded);
3736
const detail = used && {
38-
read: tokens(used.input + used.cacheWrite - used.reloaded),
37+
read: tokens(used.input + used.cacheWrite),
3938
wrote: tokens(used.output),
40-
reloaded,
4139
};
4240
return (
4341
<p className="text-muted text-xs" title={t.chat.run.took(time, detail)}>
44-
{t.chat.run.done(time, used && tokens(usedTokens(used)), reloaded)}
42+
{t.chat.run.done(time, used && tokens(usedTokens(used)))}
4543
</p>
4644
);
4745
}

‎app/src/features/chat/chat.test.tsx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ describe("chat", () => {
9191
expect(within(chat()).getByText("Command")).toBeDefined();
9292
expect(within(chat()).getByText("npm test")).toBeDefined();
9393
const done = within(chat()).getByText("Done in 4.2 s · 2.5k tokens");
94-
expect(done.title).toBe("Took 4.2 s. Read 1.8k new tokens and wrote 640.");
94+
expect(done.title).toBe("Took 4.2 s. Read 1.8k tokens and wrote 640.");
9595
});
9696

9797
test("an approval waits for the owner, and a denial carries a note", async () => {

‎app/src/features/chat/context.test.tsx‎

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function open() {
3939
}
4040

4141
describe("conversation space", () => {
42-
test("a turn after a pause tells the conversation it sent again apart", async () => {
42+
test("a turn after a pause counts the conversation it sent again", async () => {
4343
const { fake, scout } = await openScout();
4444
act(() => {
4545
// The turn seen in spec 19: almost all of it was the conversation again.
@@ -51,24 +51,8 @@ describe("conversation space", () => {
5151
output: 983,
5252
});
5353
});
54-
const done = screen.getByText("Done in 4.2 s · 1.6k tokens · reloaded the conversation (72k)");
55-
expect(done.title).toBe(
56-
"Took 4.2 s. Read 638 new tokens and wrote 983. It also sent 72k of the conversation again, as the model no longer had it after a pause.",
57-
);
58-
});
59-
60-
test("a little sent again is not worth a mention", async () => {
61-
const { fake, scout } = await openScout();
62-
act(() => {
63-
fake.chat.turn(scout.id, null, {
64-
input: 2,
65-
cacheWrite: 1_400,
66-
reloaded: 300,
67-
cacheRead: 40_000,
68-
output: 100,
69-
});
70-
});
71-
expect(screen.getByText("Done in 4.2 s · 1.2k tokens")).toBeDefined();
54+
const done = screen.getByText("Done in 4.2 s · 73.6k tokens");
55+
expect(done.title).toBe("Took 4.2 s. Read 72.6k tokens and wrote 983.");
7256
});
7357

7458
test("token counts read short", () => {

‎app/src/i18n/en/account.ts‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ export const account = {
3030
tokens: {
3131
title: "Tokens by bot",
3232
intro:
33-
"Tokens are the pieces of text a bot reads and writes. The count adds the new text read and the replies written. The conversation sent again after a pause shows apart.",
33+
"Tokens are the pieces of text a bot reads and writes. The count adds the text read and the replies written.",
3434
period: "Period",
3535
periods: { today: "Today", week: "7 days", month: "30 days", all: "All time" },
3636
empty: "No bot worked in this period.",
3737
failed: "Could not load the tokens",
3838
archived: "archived",
3939
/** `crew` is null on the total of all bots. */
40-
detail: (crew: string | null, times: number, reloaded: string | null) =>
41-
`${crew === null ? "" : `${crew} · `}Worked ${times === 1 ? "once" : `${times} times`}${
42-
reloaded === null ? "" : ` · ${reloaded} reloaded`
43-
}`,
40+
detail: (crew: string | null, times: number) =>
41+
`${crew === null ? "" : `${crew} · `}Worked ${times === 1 ? "once" : `${times} times`}`,
4442
total: "All bots",
4543
},
4644
},

‎app/src/i18n/en/chat.ts‎

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66
export interface TurnTokens {
77
read: string;
88
wrote: string;
9-
/** The conversation sent again after a pause, if it was worth telling. */
10-
reloaded: string | null;
119
}
1210

1311
export const chat = {
@@ -55,19 +53,11 @@ export const chat = {
5553
},
5654
run: {
5755
working: "Working",
58-
done: (time: string, tokens: string | null, reloaded: string | null) =>
59-
`Done in ${time}${tokens === null ? "" : ` · ${tokens} tokens`}${
60-
reloaded === null ? "" : ` · reloaded the conversation (${reloaded})`
61-
}`,
56+
done: (time: string, tokens: string | null) =>
57+
`Done in ${time}${tokens === null ? "" : ` · ${tokens} tokens`}`,
6258
took: (time: string, tokens: TurnTokens | null) =>
6359
`Took ${time}${
64-
tokens === null
65-
? ""
66-
: `. Read ${tokens.read} new tokens and wrote ${tokens.wrote}.${
67-
tokens.reloaded === null
68-
? ""
69-
: ` It also sent ${tokens.reloaded} of the conversation again, as the model no longer had it after a pause.`
70-
}`
60+
tokens === null ? "" : `. Read ${tokens.read} tokens and wrote ${tokens.wrote}.`
7161
}`,
7262
stopped: (reason: string) => `The bot stopped working on this: ${reason}`,
7363
},

‎app/src/i18n/es/account.ts‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ export const account: Messages["account"] = {
3030
tokens: {
3131
title: "Tokens por bot",
3232
intro:
33-
"Los tokens son los trozos de texto que un bot lee y escribe. La cuenta suma el texto nuevo leído y las respuestas escritas. La conversación enviada de nuevo tras una pausa aparece aparte.",
33+
"Los tokens son los trozos de texto que un bot lee y escribe. La cuenta suma el texto leído y las respuestas escritas.",
3434
period: "Período",
3535
periods: { today: "Hoy", week: "7 días", month: "30 días", all: "Todo" },
3636
empty: "Ningún bot trabajó en este período.",
3737
failed: "No se pudieron cargar los tokens",
3838
archived: "archivado",
3939
/** `crew` is null on the total of all bots. */
40-
detail: (crew: string | null, times: number, reloaded: string | null) =>
41-
`${crew === null ? "" : `${crew} · `}Trabajó ${times === 1 ? "1 vez" : `${times} veces`}${
42-
reloaded === null ? "" : ` · ${reloaded} recargados`
43-
}`,
40+
detail: (crew: string | null, times: number) =>
41+
`${crew === null ? "" : `${crew} · `}Trabajó ${times === 1 ? "1 vez" : `${times} veces`}`,
4442
total: "Todos los bots",
4543
},
4644
},

‎app/src/i18n/es/chat.ts‎

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,19 +49,11 @@ export const chat: Messages["chat"] = {
4949
},
5050
run: {
5151
working: "Trabajando",
52-
done: (time: string, tokens: string | null, reloaded: string | null) =>
53-
`Terminado en ${time}${tokens === null ? "" : ` · ${tokens} tokens`}${
54-
reloaded === null ? "" : ` · recargó la conversación (${reloaded})`
55-
}`,
52+
done: (time: string, tokens: string | null) =>
53+
`Terminado en ${time}${tokens === null ? "" : ` · ${tokens} tokens`}`,
5654
took: (time: string, tokens: TurnTokens | null) =>
5755
`Tardó ${time}${
58-
tokens === null
59-
? ""
60-
: `. Leyó ${tokens.read} tokens nuevos y escribió ${tokens.wrote}.${
61-
tokens.reloaded === null
62-
? ""
63-
: ` También volvió a enviar ${tokens.reloaded} de la conversación, que el modelo ya no tenía tras una pausa.`
64-
}`
56+
tokens === null ? "" : `. Leyó ${tokens.read} tokens y escribió ${tokens.wrote}.`
6557
}`,
6658
stopped: (reason: string) => `El bot dejó de trabajar en esto: ${reason}`,
6759
},

‎app/src/i18n/pt-BR/account.ts‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ export const account: Messages["account"] = {
3030
tokens: {
3131
title: "Tokens por bot",
3232
intro:
33-
"Tokens são os pedaços de texto que um bot lê e escreve. A conta soma o texto novo lido e as respostas escritas. A conversa mandada de novo depois de uma pausa aparece à parte.",
33+
"Tokens são os pedaços de texto que um bot lê e escreve. A conta soma o texto lido e as respostas escritas.",
3434
period: "Período",
3535
periods: { today: "Hoje", week: "7 dias", month: "30 dias", all: "Tudo" },
3636
empty: "Nenhum bot trabalhou nesse período.",
3737
failed: "Não deu para carregar os tokens",
3838
archived: "arquivado",
3939
/** `crew` is null on the total of all bots. */
40-
detail: (crew: string | null, times: number, reloaded: string | null) =>
41-
`${crew === null ? "" : `${crew} · `}Trabalhou ${times === 1 ? "1 vez" : `${times} vezes`}${
42-
reloaded === null ? "" : ` · ${reloaded} recarregados`
43-
}`,
40+
detail: (crew: string | null, times: number) =>
41+
`${crew === null ? "" : `${crew} · `}Trabalhou ${times === 1 ? "1 vez" : `${times} vezes`}`,
4442
total: "Todos os bots",
4543
},
4644
},

0 commit comments

Comments
 (0)