Skip to content

Commit ece59e4

Browse files
完成了verify_new的路由,新的限额数据写在llm_usage里 (#1775)
Co-authored-by: Jerffery Zhao <58381542+konpoku@users.noreply.github.com>
1 parent 25590b2 commit ece59e4

3 files changed

Lines changed: 127 additions & 5 deletions

File tree

src/hasura/llm.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,56 @@
11
import { gql } from "graphql-request";
22
import { client } from "..";
33

4+
export const get_llm_usage_by_uuid = async (uuid: string) => {
5+
const query: any = await client.request(
6+
gql`
7+
query GetLlmUsageByUuid($uuid: uuid!) {
8+
llm_usage_by_pk(uuid: $uuid) {
9+
uuid
10+
total_tokens_used
11+
token_limit
12+
}
13+
}
14+
`,
15+
{ uuid },
16+
);
17+
return query.llm_usage_by_pk;
18+
};
19+
20+
export const set_llm_usage_by_uuid = async (
21+
uuid: string,
22+
total_usage: number,
23+
) => {
24+
const query: any = await client.request(
25+
gql`
26+
mutation SetLlmUsageByUuid(
27+
$uuid: uuid!
28+
$total_usage: bigint!
29+
$updated_at: timestamptz!
30+
) {
31+
update_llm_usage_by_pk(
32+
pk_columns: { uuid: $uuid }
33+
_set: {
34+
total_tokens_used: $total_usage
35+
last_updated_at: $updated_at
36+
}
37+
) {
38+
uuid
39+
total_tokens_used
40+
token_limit
41+
last_updated_at
42+
}
43+
}
44+
`,
45+
{
46+
uuid,
47+
total_usage,
48+
updated_at: new Date().toISOString(),
49+
},
50+
);
51+
return query.update_llm_usage_by_pk;
52+
};
53+
454
export const get_user_llm_usage = async (student_no: string) => {
555
const query: any = await client.request(
656
gql`

src/helpers/llm_cron.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import cron from "node-cron";
22
import redis from "./redis";
3-
import { set_user_llm_usage } from "../hasura/llm";
3+
import { set_llm_usage_by_uuid } from "../hasura/llm";
44

55
export const llm_cron = () => {
66
// Run every 5 minutes
7-
cron.schedule("*/5 * * * *", async () => {
7+
cron.schedule("*/1 * * * *", async () => {
88
console.log("Starting LLM usage sync...");
99
let cursor = "0";
1010
do {
@@ -21,11 +21,11 @@ export const llm_cron = () => {
2121

2222
for (const key of keys) {
2323
try {
24-
const studentNo = key.split(":")[1];
24+
const uuid = key.split(":")[1];
2525
const usageStr = await redis.get(key);
2626
if (usageStr) {
2727
const usage = parseInt(usageStr);
28-
await set_user_llm_usage(studentNo, usage);
28+
await set_llm_usage_by_uuid(uuid, usage);
2929
}
3030
} catch (e) {
3131
console.error(`Failed to sync usage for key ${key}:`, e);

src/routes/llm.ts

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import fs from "fs";
55
import path from "path";
66
import redis from "../helpers/redis";
77
import {
8+
get_llm_usage_by_uuid,
89
get_user_llm_usage,
910
init_user_llm_usage,
1011
get_llm_model_config,
1112
log_access_key_usage,
1213
check_access_key_usage,
1314
} from "../hasura/llm";
15+
import authenticate from "../middlewares/authenticate";
1416

1517
const router = express.Router();
1618

@@ -218,6 +220,75 @@ router.post("/verify", async (req, res) => {
218220
}
219221
});
220222

223+
router.post("/verify_new", authenticate(), async (req, res) => {
224+
try {
225+
const uuid: string | undefined = req.auth.user.uuid;
226+
const email: string | undefined = req.auth.user.email;
227+
const role: string | undefined = req.auth.user.role;
228+
229+
if (!uuid) {
230+
return res.status(400).json({ error: "Missing uuid in auth user" });
231+
}
232+
233+
// Invalidate old sessions for this uuid
234+
const now = Math.floor(Date.now() / 1000);
235+
await redis.set(`llm_min_iat:${uuid}`, now);
236+
237+
// Check llm_usage row by authenticated uuid.
238+
const dbUsage = await get_llm_usage_by_uuid(uuid);
239+
if (!dbUsage) {
240+
return res.status(401).json({
241+
error: "No llm_usage record found for authenticated uuid",
242+
});
243+
}
244+
245+
// Sync limit to Redis by uuid.
246+
const dbLimit = dbUsage.token_limit || 0;
247+
if (dbLimit > 0) {
248+
await redis.set(`llm_limit:${uuid}`, dbLimit);
249+
} else {
250+
await redis.del(`llm_limit:${uuid}`);
251+
}
252+
253+
// Sync usage from DB to Redis if missing (e.g. Redis restart)
254+
const currentUsage = await redis.get(`llm_usage:${uuid}`);
255+
if (!currentUsage) {
256+
await redis.set(`llm_usage:${uuid}`, dbUsage.total_tokens_used || 0);
257+
}
258+
259+
// Issue LLM session token using authenticated identity
260+
const sessionToken = jwt.sign(
261+
{
262+
sub: uuid,
263+
email: email,
264+
role: role,
265+
type: "llm_session",
266+
},
267+
JWT_SECRET,
268+
{ expiresIn: SESSION_EXPIRY },
269+
);
270+
271+
return res.json({
272+
token: sessionToken,
273+
user: {
274+
uuid,
275+
email,
276+
role,
277+
},
278+
quota: {
279+
tokenLimit: dbLimit,
280+
totalTokensUsed: dbUsage.total_tokens_used || 0,
281+
},
282+
});
283+
} catch (err: any) {
284+
console.error("verify_new failed:", err);
285+
return res.status(500).json({
286+
error: "Failed to verify authenticated user for LLM",
287+
details: err?.message,
288+
});
289+
}
290+
});
291+
221292
// 2. Chat Endpoint
222293
router.post("/chat", verifySession, async (req, res) => {
223294
const { messages, model } = req.body;
@@ -343,7 +414,8 @@ router.post("/chat", verifySession, async (req, res) => {
343414
if (enableThinking) {
344415
requestOptions.enable_thinking = true;
345416
}
346-
417+
// console.log("LLM Request Options:", requestOptions);
418+
// console.log(`Base URL: ${baseURL}`);
347419
const stream = (await client.chat.completions.create(requestOptions, {
348420
signal: controller.signal,
349421
})) as any;

0 commit comments

Comments
 (0)