Skip to content

Commit c8d33ed

Browse files
waynesuttonShahfarzanecursoragent
committed
fix(messages): stop message handlers from accumulating tokens onto sessions
Message handlers (upsert, batchUpsert) were accumulating per-message promptTokens/completionTokens onto the session record. Since session-level sync already sets these as authoritative absolute values, the accumulation could result in double-counted token totals (fixes #32). Remove all token accumulation from message handlers. Session tokens are now set exclusively by session-level sync. Message handlers only update messageCount and searchableText. Co-authored-by: Shah <42602105+Shahfarzane@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2359196 commit c8d33ed

1 file changed

Lines changed: 10 additions & 34 deletions

File tree

convex/messages.ts

Lines changed: 10 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,6 @@ export const upsert = internalMutation({
5353

5454
// Store session data we need for later (avoid re-reading)
5555
let sessionId: Id<"sessions">;
56-
let sessionPromptTokens: number;
57-
let sessionCompletionTokens: number;
5856
let sessionMessageCount: number;
5957
let sessionSearchableText: string | undefined;
6058

@@ -84,14 +82,10 @@ export const upsert = internalMutation({
8482
updatedAt: now,
8583
});
8684
// Use defaults since we just created it
87-
sessionPromptTokens = 0;
88-
sessionCompletionTokens = 0;
8985
sessionMessageCount = 0;
9086
sessionSearchableText = undefined;
9187
} else {
9288
sessionId = session._id;
93-
sessionPromptTokens = session.promptTokens;
94-
sessionCompletionTokens = session.completionTokens;
9589
sessionMessageCount = session.messageCount;
9690
sessionSearchableText = session.searchableText;
9791
}
@@ -174,12 +168,10 @@ export const upsert = internalMutation({
174168
const sessionUpdate: Record<string, unknown> = { updatedAt: now };
175169

176170
if (shouldUpdateSessionStats) {
177-
const newPromptTokens = sessionPromptTokens + (args.promptTokens || 0);
178-
const newCompletionTokens = sessionCompletionTokens + (args.completionTokens || 0);
171+
// Only update messageCount. Session tokens are set exclusively by
172+
// session-level sync (the authoritative source). Never accumulate
173+
// per-message tokens onto the session to avoid double-counting.
179174
sessionUpdate.messageCount = sessionMessageCount + 1;
180-
sessionUpdate.promptTokens = newPromptTokens;
181-
sessionUpdate.completionTokens = newCompletionTokens;
182-
sessionUpdate.totalTokens = newPromptTokens + newCompletionTokens;
183175
}
184176

185177
if (newSearchableText) {
@@ -252,8 +244,6 @@ export const batchUpsert = internalMutation({
252244
.first();
253245

254246
// Track session stats for batch update
255-
let sessionPromptTokens = 0;
256-
let sessionCompletionTokens = 0;
257247
let sessionMessageCount = 0;
258248
let sessionSearchableText = "";
259249
let sessionId: Id<"sessions">;
@@ -286,8 +276,6 @@ export const batchUpsert = internalMutation({
286276
});
287277
} else {
288278
sessionId = session._id;
289-
sessionPromptTokens = session.promptTokens;
290-
sessionCompletionTokens = session.completionTokens;
291279
sessionMessageCount = session.messageCount;
292280
sessionSearchableText = session.searchableText || "";
293281
}
@@ -304,11 +292,10 @@ export const batchUpsert = internalMutation({
304292

305293
// Early return for dedup
306294
if (existing && now - existing.createdAt < MESSAGE_DEDUP_MS) {
307-
return { action: "skipped" as const, tokens: { prompt: 0, completion: 0 }, text: "" };
295+
return { action: "skipped" as const, text: "" };
308296
}
309297

310298
let messageId: Id<"messages">;
311-
let addedTokens = { prompt: 0, completion: 0 };
312299

313300
if (existing) {
314301
// Update existing
@@ -330,7 +317,7 @@ export const batchUpsert = internalMutation({
330317
await Promise.all(existingParts.map((p) => ctx.db.delete(p._id)));
331318
}
332319

333-
return { action: "updated" as const, tokens: { prompt: 0, completion: 0 }, text: "" };
320+
return { action: "updated" as const, text: "" };
334321
}
335322

336323
// Insert new message
@@ -345,10 +332,6 @@ export const batchUpsert = internalMutation({
345332
durationMs: msg.durationMs,
346333
createdAt: now,
347334
});
348-
addedTokens = {
349-
prompt: msg.promptTokens || 0,
350-
completion: msg.completionTokens || 0,
351-
};
352335

353336
// Insert parts in parallel
354337
if (msg.parts && msg.parts.length > 0) {
@@ -379,25 +362,21 @@ export const batchUpsert = internalMutation({
379362
.join(" ");
380363
}
381364

382-
return { action: "inserted" as const, tokens: addedTokens, text: textContent };
365+
return { action: "inserted" as const, text: textContent };
383366
} catch (e) {
384-
return { action: "error" as const, error: `${msg.externalId}: ${e}`, tokens: { prompt: 0, completion: 0 }, text: "" };
367+
return { action: "error" as const, error: `${msg.externalId}: ${e}`, text: "" };
385368
}
386369
})
387370
);
388371

389372
// Aggregate results for session update
390373
let newMessages = 0;
391-
let totalPromptTokens = 0;
392-
let totalCompletionTokens = 0;
393374
const textParts: Array<string> = [];
394375

395376
for (const result of results) {
396377
if (result.action === "inserted") {
397378
inserted++;
398379
newMessages++;
399-
totalPromptTokens += result.tokens.prompt;
400-
totalCompletionTokens += result.tokens.completion;
401380
if (result.text) textParts.push(result.text);
402381
} else if (result.action === "updated") {
403382
updated++;
@@ -408,19 +387,16 @@ export const batchUpsert = internalMutation({
408387
}
409388
}
410389

411-
// Single session update for all new messages
390+
// Single session update for all new messages. Only messageCount and searchableText.
391+
// Session tokens are set exclusively by session-level sync (the authoritative source).
392+
// Never accumulate per-message tokens onto the session to avoid double-counting.
412393
if (newMessages > 0 || textParts.length > 0) {
413-
const newPromptTotal = sessionPromptTokens + totalPromptTokens;
414-
const newCompletionTotal = sessionCompletionTokens + totalCompletionTokens;
415394
const newSearchable = textParts.length > 0
416395
? `${sessionSearchableText} ${textParts.join(" ")}`.slice(0, 10000)
417396
: sessionSearchableText;
418397

419398
await ctx.db.patch(sessionId, {
420399
messageCount: sessionMessageCount + newMessages,
421-
promptTokens: newPromptTotal,
422-
completionTokens: newCompletionTotal,
423-
totalTokens: newPromptTotal + newCompletionTotal,
424400
searchableText: newSearchable || undefined,
425401
updatedAt: now,
426402
});

0 commit comments

Comments
 (0)