Skip to content

Commit

Permalink
feat: add bot token usage analyzer
Browse files Browse the repository at this point in the history
  • Loading branch information
RaoHai committed Dec 24, 2024
1 parent 6605804 commit 3749394
Show file tree
Hide file tree
Showing 8 changed files with 3,730 additions and 2,998 deletions.
18 changes: 17 additions & 1 deletion client/app/services/TokensController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,20 @@ export async function deleteToken(id: string) {
export async function createToken(data: LLMTokenInsert) {
const response = await axios.post(`${apiDomain}/api/user/llm_token`, data);
return response.data;
}
}

export async function analyzeTokenUsage() {
const response = await axios.get(`${apiDomain}/api/user/llm_token_usages/analyzer`);
return response.data;
}

export async function analyzeTopBots() {
const response = await axios.get(`${apiDomain}/api/user/llm_token_usages/top_bots`);
return response.data;
}

export async function analyzeTopUsers() {
const response = await axios.get(`${apiDomain}/api/user/llm_token_usages/top_users`);
return response.data;
}

2 changes: 2 additions & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
"node": ">=18"
},
"dependencies": {
"@ant-design/charts": "^2.2.6",
"@antv/g2": "^5.2.10",
"@auth0/nextjs-auth0": "^3.3.0",
"@fingerprintjs/fingerprintjs": "^4.3.0",
"@fullpage/react-fullpage": "^0.1.42",
Expand Down
6,562 changes: 3,569 additions & 2,993 deletions client/yarn.lock

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions migrations/supabase/migrations/20241224095717_remote_schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
drop function if exists "public"."analyze_user_token_usage"(start_date date, end_date date);

alter table "public"."profiles" add column "is_admin" boolean default false;

set check_function_bodies = off;

CREATE OR REPLACE FUNCTION public.bot_token_usage_rate(start_date date, end_date date)
RETURNS TABLE(bot_id text, bot_name text, input_tokens bigint, output_tokens bigint, total_tokens bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT * from (
SELECT
u.bot_id AS bot_id,
COALESCE(b.name, '未命名')::text as bot_name,
SUM(u.input_token)::BIGINT AS input_tokens, -- 将结果转换为 BIGINT
SUM(u.output_token)::BIGINT AS output_tokens, -- 将结果转换为 BIGINT
SUM(u.total_token)::BIGINT AS total_tokens -- 将结果转换为 BIGINT
FROM user_token_usage u
LEFT JOIN bots b
ON b.id::text = u.bot_id
GROUP BY u.bot_id, b.name
) bu
ORDER BY total_tokens DESC
limit 3;
END;
$function$
;

CREATE OR REPLACE FUNCTION public.user_token_usage_rate(start_date date, end_date date)
RETURNS TABLE(user_id text, user_name text, input_tokens bigint, output_tokens bigint, total_tokens bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT * from (
SELECT
u.user_id AS user_id,
COALESCE(p.name, '匿名用户')::text as user_name,
SUM(u.input_token)::BIGINT AS input_tokens, -- 将结果转换为 BIGINT
SUM(u.output_token)::BIGINT AS output_tokens, -- 将结果转换为 BIGINT
SUM(u.total_token)::BIGINT AS total_tokens -- 将结果转换为 BIGINT
FROM user_token_usage u
LEFT JOIN profiles p
ON p.id = u.user_id
WHERE
u.token_id = 'DEFAULT_TOKEN' AND
u.date >= start_date AND
u.date <= end_date
GROUP BY u.user_id, p.name
) bu
ORDER BY total_tokens DESC
limit 3;
END;
$function$
;

CREATE OR REPLACE FUNCTION public.analyze_user_token_usage(start_date date, end_date date)
RETURNS TABLE(bot_id text, bot_name text, usage_date date, input_tokens bigint, output_tokens bigint, total_tokens bigint)
LANGUAGE plpgsql
AS $function$
BEGIN
RETURN QUERY
SELECT
u.bot_id AS bot_id,
COALESCE(b.name, '未命名')::text as bot_name,
Date(u.date) AS usage_date, -- 使用别名来避免歧义
SUM(u.input_token)::BIGINT AS input_tokens, -- 将结果转换为 BIGINT
SUM(u.output_token)::BIGINT AS output_tokens, -- 将结果转换为 BIGINT
SUM(u.total_token)::BIGINT AS total_tokens -- 将结果转换为 BIGINT
FROM user_token_usage u
LEFT JOIN bots b
ON b.id::text = u.bot_id
WHERE
u.date >= start_date AND
u.date <= end_date
GROUP BY u.date, u.bot_id, b.name
ORDER by u.date DESC;
END;
$function$
;


21 changes: 19 additions & 2 deletions server/core/dao/userTokenUsageDAO.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from datetime import datetime
from supabase.client import Client

from core.models.user_token_usage import BotTokenUsageStats, UserTokenUsage, UserTokenUsageStats
from core.models.user_token_usage import BotTokenUsageRate, BotTokenUsageStats, UserTokenUsage, UserTokenUsageRate, UserTokenUsageStats
from petercat_utils.db.client.supabase import get_client
from core.dao.BaseDAO import BaseDAO

Expand Down Expand Up @@ -36,4 +36,21 @@ def analyze(self, start_date: datetime, end_date: datetime):
"end_date": end_date.strftime("%Y-%m-%d"),
}).execute()

return [BotTokenUsageStats(**stats) for stats in resp.data]
return [BotTokenUsageStats(**stats) for stats in resp.data]

def top_bots(self, start_date: datetime, end_date: datetime):
resp = self.client.rpc("bot_token_usage_rate", {
"start_date": start_date.strftime("%Y-%m-%d"),
"end_date": end_date.strftime("%Y-%m-%d"),
}).execute()

return [BotTokenUsageRate(**stats) for stats in resp.data]

def top_users(self, start_date: datetime, end_date: datetime):
resp = self.client.rpc("user_token_usage_rate", {
"start_date": start_date.strftime("%Y-%m-%d"),
"end_date": end_date.strftime("%Y-%m-%d"),
}).execute()

return [UserTokenUsageRate(**stats) for stats in resp.data]

15 changes: 15 additions & 0 deletions server/core/models/user_token_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,22 @@ class UserTokenUsageStats(BaseModel):

class BotTokenUsageStats(BaseModel):
bot_id: str
bot_name: str
usage_date: datetime
input_tokens: Optional[int] = 0
output_tokens: Optional[int] = 0
total_tokens: Optional[int] = 0

class BotTokenUsageRate(BaseModel):
bot_id: str
bot_name: str
input_tokens: Optional[int] = 0
output_tokens: Optional[int] = 0
total_tokens: Optional[int] = 0

class UserTokenUsageRate(BaseModel):
user_id: str
user_name: str
input_tokens: Optional[int] = 0
output_tokens: Optional[int] = 0
total_tokens: Optional[int] = 0
6 changes: 6 additions & 0 deletions server/core/service/user_token_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ def usage_stats(self, user_id: str, start_date: datetime.date, end_date: datetim
def analyze_token_usage(self, start_date: datetime.date, end_date: datetime.date):
return self.user_token_usage_dao.analyze(start_date=start_date, end_date=end_date)

def top_bots(self, start_date: datetime.date, end_date: datetime.date):
return self.user_token_usage_dao.top_bots(start_date=start_date, end_date=end_date)

def top_users(self, start_date: datetime.date, end_date: datetime.date):
return self.user_token_usage_dao.top_users(start_date=start_date, end_date=end_date)

def get_user_token_usage_service():
return UserTokenUsageService()

Expand Down
20 changes: 18 additions & 2 deletions server/user/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,24 @@ def token_usage(

@router.get("/llm_token_usages/analyzer")
def token_usage_analyze(
start_date: datetime = datetime.now() - timedelta(days=7),
start_date: datetime = datetime.now() - timedelta(days=30),
end_date: datetime = datetime.now(),
user_token_usage_service: Annotated[UserTokenUsageService | None, Depends(get_user_token_usage_service)] = None,
):
return user_token_usage_service.analyze_token_usage(start_date=start_date, end_date=end_date)
return user_token_usage_service.analyze_token_usage(start_date=start_date, end_date=end_date)

@router.get("/llm_token_usages/top_bots")
def top_used_bots(
start_date: datetime = datetime.now() - timedelta(days=30),
end_date: datetime = datetime.now(),
user_token_usage_service: Annotated[UserTokenUsageService | None, Depends(get_user_token_usage_service)] = None,
):
return user_token_usage_service.top_bots(start_date=start_date, end_date=end_date)

@router.get("/llm_token_usages/top_users")
def top_used_users(
start_date: datetime = datetime.now() - timedelta(days=30),
end_date: datetime = datetime.now(),
user_token_usage_service: Annotated[UserTokenUsageService | None, Depends(get_user_token_usage_service)] = None,
):
return user_token_usage_service.top_users(start_date=start_date, end_date=end_date)

0 comments on commit 3749394

Please sign in to comment.