Skip to content

Commit 26b4eef

Browse files
committed
fix: Remove userGroup field from usage tracking (fixes #673)
Since users can have multiple groups, it's unclear which group usage should be attributed to. This change removes the userGroup field from the usages table. Changes: - Add migration to drop userGroup column from usages table - Remove userGroup field from UsageEntity - Remove userGroup from usage tracking calls - Remove group-level quota check from CheckUsageMiddleware - Update tests to not set userGroup field The change maintains user-level quota enforcement while removing ambiguous group-level tracking.
1 parent 5e69c25 commit 26b4eef

6 files changed

Lines changed: 61 additions & 21 deletions

File tree

backend/src/controllers/usages/usages.e2e.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,6 @@ async function createUsageEntity(date: Date, userEntity: UserEntity, usageReposi
244244
const usageEntity = new UsageEntity();
245245
usageEntity.date = startOfDay(date);
246246
usageEntity.userId = userEntity.id;
247-
usageEntity.userGroup = userEntity.userGroups?.[0]?.id ?? 'admin';
248247
usageEntity.counter = 'token_usage';
249248
usageEntity.key = 'azure-open-ai';
250249
usageEntity.subKey = 'gpt-4o';

backend/src/domain/chat/middlewares/check-usage-middleware.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,38 +40,29 @@ export class CheckUsageMiddleware implements ChatMiddleware {
4040
await next(context);
4141
return;
4242
}
43+
4344
const userGroup = await this.userGroups.findOneBy({ id: userGroupId });
44-
const monthlyTokens = userGroup?.monthlyTokens ?? 0;
4545
const monthlyUserTokens = userGroup?.monthlyUserTokens ?? 0;
4646

47-
if (!userGroup || (monthlyTokens < 0 && monthlyUserTokens < 0)) {
47+
if (!userGroup || monthlyUserTokens < 0) {
4848
await next(context);
4949
return;
5050
}
5151

5252
const dateFrom = startOfMonth(new Date());
5353
const dateTo = addMonths(dateFrom, 1);
5454

55-
if (monthlyTokens > 0) {
56-
const groupUsage =
57-
(await this.usages.sum('count', {
58-
date: And(MoreThanOrEqual(dateFrom), LessThan(dateTo)),
59-
userGroup: userGroupId,
60-
})) ?? 0;
61-
62-
if (groupUsage >= monthlyTokens) {
63-
throw new HttpException('Monthly token limit exceeded for user group.', HttpStatus.TOO_MANY_REQUESTS);
64-
}
65-
}
55+
// Note: Group-level quota check removed because userGroup field was removed from usage tracking (issue #673)
56+
// Only user-level quotas are enforced now
6657

6758
if (monthlyUserTokens > 0) {
68-
const groupUsage =
59+
const userUsage =
6960
(await this.usages.sum('count', {
7061
date: And(MoreThanOrEqual(dateFrom), LessThan(dateTo)),
7162
userId: user.id,
7263
})) ?? 0;
7364

74-
if (groupUsage >= monthlyUserTokens) {
65+
if (userUsage >= monthlyUserTokens) {
7566
throw new HttpException('Monthly token limit exceeded for user.', HttpStatus.TOO_MANY_REQUESTS);
7667
}
7768
}

backend/src/domain/chat/middlewares/store-usage-middleware.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ export class StorageUsageMiddleware implements ChatMiddleware {
2828
date: new Date(),
2929
key: usage.llm,
3030
subKey: usage.model,
31-
userGroup: context.user.userGroupIds?.[0],
3231
userId: context.user.id,
3332
});
3433
} catch (err) {

backend/src/domain/chat/use-cases/rate-message.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export class RateMessageHandler implements ICommandHandler<RateMessage, RateMess
5656
date: new Date(),
5757
key: rating,
5858
subKey: conversation.llm || 'none',
59-
userGroup: '', //TODO: fixme
6059
userId: user.id,
6160
});
6261

backend/src/domain/database/entities/usage.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,6 @@ export class UsageEntity {
1010
@PrimaryColumn()
1111
userId!: string;
1212

13-
@PrimaryColumn()
14-
userGroup!: string;
15-
1613
@PrimaryColumn()
1714
counter!: string;
1815

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export class RemoveUserGroupFromUsages1767451199884 implements MigrationInterface {
4+
name = 'RemoveUserGroupFromUsages1767451199884';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
// Step 1: Create a temporary table with merged usage data
8+
// This merges rows that would become duplicates after removing userGroup
9+
await queryRunner.query(`
10+
CREATE TEMP TABLE usages_merged AS
11+
SELECT
12+
date,
13+
"userId",
14+
counter,
15+
key,
16+
"subKey",
17+
SUM(count) as count
18+
FROM company_chat.usages
19+
GROUP BY date, "userId", counter, key, "subKey"
20+
`);
21+
22+
// Step 2: Drop the old primary key constraint
23+
await queryRunner.query(`ALTER TABLE company_chat.usages DROP CONSTRAINT "PK_0acc90e335c519dc4e2140320f1"`);
24+
25+
// Step 3: Truncate the table and reload with merged data
26+
await queryRunner.query(`TRUNCATE company_chat.usages`);
27+
28+
await queryRunner.query(`
29+
INSERT INTO company_chat.usages (date, "userId", "userGroup", counter, key, "subKey", count)
30+
SELECT date, "userId", '', counter, key, "subKey", count
31+
FROM usages_merged
32+
`);
33+
34+
// Step 4: Drop the userGroup column
35+
await queryRunner.query(`ALTER TABLE company_chat.usages DROP COLUMN "userGroup"`);
36+
37+
// Step 5: Add new primary key constraint without userGroup
38+
await queryRunner.query(
39+
`ALTER TABLE company_chat.usages ADD CONSTRAINT "PK_usages_without_group" PRIMARY KEY (date, "userId", counter, key, "subKey")`,
40+
);
41+
}
42+
43+
public async down(queryRunner: QueryRunner): Promise<void> {
44+
// Drop the new primary key
45+
await queryRunner.query(`ALTER TABLE company_chat.usages DROP CONSTRAINT "PK_usages_without_group"`);
46+
47+
// Re-add the userGroup column
48+
await queryRunner.query(`ALTER TABLE company_chat.usages ADD COLUMN "userGroup" character varying NOT NULL DEFAULT ''`);
49+
50+
// Restore the original primary key
51+
await queryRunner.query(
52+
`ALTER TABLE company_chat.usages ADD CONSTRAINT "PK_0acc90e335c519dc4e2140320f1" PRIMARY KEY (date, "userId", "userGroup", counter, key, "subKey")`,
53+
);
54+
}
55+
}

0 commit comments

Comments
 (0)