Address review comments

This commit is contained in:
Filip Sodić
2025-04-28 22:41:20 +02:00
parent b381af389d
commit e3588fae5e

View File

@ -15,11 +15,11 @@ import { SubscriptionStatus } from '../payment/plans';
import { ensureArgsSchemaOrThrowHttpError } from '../server/validation'; import { ensureArgsSchemaOrThrowHttpError } from '../server/validation';
const openAi = getOpenAi(); const openAi = getOpenAi();
function getOpenAi(): OpenAI | null { function getOpenAi(): OpenAI {
if (process.env.OPENAI_API_KEY) { if (process.env.OPENAI_API_KEY) {
return new OpenAI({ apiKey: process.env.OPENAI_API_KEY }); return new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
} else { } else {
return null; throw new Error('OpenAI API key is not set');
} }
} }
@ -38,12 +38,11 @@ export const generateGptResponse: GenerateGptResponse<GenerateGptResponseInput,
throw new HttpError(401, 'Only authenticated users are allowed to perform this operation'); throw new HttpError(401, 'Only authenticated users are allowed to perform this operation');
} }
const { hours } = ensureArgsSchemaOrThrowHttpError(generateGptResponseInputSchema, rawArgs);
if (!isEligibleForResponse(context.user)) { if (!isEligibleForResponse(context.user)) {
throw new HttpError(402, 'User has not paid or is out of credits'); throw new HttpError(402, 'User has not paid or is out of credits');
} }
const { hours } = ensureArgsSchemaOrThrowHttpError(generateGptResponseInputSchema, rawArgs);
const tasks = await context.entities.Task.findMany({ const tasks = await context.entities.Task.findMany({
where: { where: {
user: { user: {
@ -53,16 +52,19 @@ export const generateGptResponse: GenerateGptResponse<GenerateGptResponseInput,
}); });
console.log('Calling open AI api'); console.log('Calling open AI api');
const dailyPlanJson = await getDailyPlanFromGpt(tasks, hours); const generatedSchedule = await generateScheduleWithGpt(tasks, hours);
if (dailyPlanJson === null) { if (generatedSchedule === null) {
throw new HttpError(500, 'Encountered a problem in communication with OpenAI'); throw new HttpError(500, 'Encountered a problem in communication with OpenAI');
} }
// TODO: Do I need a try catch now that I'm saving a response and // We decrement the credits after using up tokens to get a daily plan
// decrementing credits in a transaction? // from Chat GPT.
//
// NOTE: I changed this up, first I do the request, and then I decrement // This way, users don't feel cheated if something goes wrong.
// credits. Is that dangerous? Protecting from it could be too complicated // On the flipside, users can theoretically abuse this and spend more
// credits than they have, but the damage should be pretty limited.
//
// Think about which option you prefer for you and edit the code accordingly.
const decrementCredit = context.entities.User.update({ const decrementCredit = context.entities.User.update({
where: { id: context.user.id }, where: { id: context.user.id },
data: { data: {
@ -75,15 +77,14 @@ export const generateGptResponse: GenerateGptResponse<GenerateGptResponseInput,
const createResponse = context.entities.GptResponse.create({ const createResponse = context.entities.GptResponse.create({
data: { data: {
user: { connect: { id: context.user.id } }, user: { connect: { id: context.user.id } },
content: dailyPlanJson, content: JSON.stringify(generatedSchedule),
}, },
}); });
console.log('Decrementing credits and saving response'); console.log('Decrementing credits and saving response');
prisma.$transaction([decrementCredit, createResponse]); prisma.$transaction([decrementCredit, createResponse]);
// TODO: Can this ever fail? return generatedSchedule;
return JSON.parse(dailyPlanJson);
}; };
function isEligibleForResponse(user: User) { function isEligibleForResponse(user: User) {
@ -205,11 +206,7 @@ export const getAllTasksByUser: GetAllTasksByUser<void, Task[]> = async (_args,
}; };
//#endregion //#endregion
async function getDailyPlanFromGpt(tasks: Task[], hours: string): Promise<string | null> { async function generateScheduleWithGpt(tasks: Task[], hours: string): Promise<GeneratedSchedule | null> {
if (openAi === null) {
return null;
}
const parsedTasks = tasks.map(({ description, time }) => ({ const parsedTasks = tasks.map(({ description, time }) => ({
description, description,
time, time,
@ -294,5 +291,5 @@ async function getDailyPlanFromGpt(tasks: Task[], hours: string): Promise<string
}); });
const gptResponse = completion?.choices[0]?.message?.tool_calls?.[0]?.function.arguments; const gptResponse = completion?.choices[0]?.message?.tool_calls?.[0]?.function.arguments;
return gptResponse ?? null; return gptResponse !== undefined ? JSON.parse(gptResponse) : null;
} }