From 9585b1a852d28abca9e9237eb39c532d987c0ebf Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 19 Feb 2024 16:01:34 -0600 Subject: [PATCH 1/2] Update PostgreSQL ENVs --- example.env | 2 +- prisma/schema.prisma | 2 +- utils/fetch-lesson.ts | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/example.env b/example.env index 05e3bec..7cc4213 100644 --- a/example.env +++ b/example.env @@ -24,7 +24,7 @@ GOOGLE_CLOUD_PROJECT=my-gcs-project-change-this # -v ${PWD}/db:/var/lib/postgresql/data \ # -p 5432:5432 \ # postgres -DATABASE_URL="postgres://prisma:your_password@localhost:5432/prisma_dev?schema=public" +POSTGRES_URI="postgres://prisma:your_password@localhost:5432/prisma_dev?schema=public" # This is the path to the JSON file you downloaded from Google Cloud # Learn more at # https://cloud.google.com/docs/authentication/application-default-credentials#GAC diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 7f4b7ba..d587099 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,7 +8,7 @@ generator client { datasource db { provider = "postgresql" - url = env("DATABASE_URL") + url = env("POSTGRES_URI") } model Account { diff --git a/utils/fetch-lesson.ts b/utils/fetch-lesson.ts index 8edd055..28f0b89 100644 --- a/utils/fetch-lesson.ts +++ b/utils/fetch-lesson.ts @@ -5,7 +5,6 @@ import path from "path"; import { draw, map, template } from "radash"; import { errorReport } from "./error-report"; import { prismaClient } from "@/server/prisma-client"; -import { timeUntil } from "./time-until"; type LessonType = "listening" | "speaking"; From d26f6c352ee0fa779ebd64a25ea21fe9dd2f31eb Mon Sep 17 00:00:00 2001 From: Rick Carlino Date: Mon, 19 Feb 2024 16:42:37 -0600 Subject: [PATCH 2/2] Allow GPT-4 for approved users --- pages/study.tsx | 21 +++++++++++++-------- server/quiz-evaluators/listening.ts | 8 ++++++-- server/quiz-evaluators/speaking.ts | 18 ++++++++++++++---- server/quiz-evaluators/types.ts | 1 + server/routers/bulk-create-cards.ts | 5 ++++- server/routers/get-next-quizzes.ts | 4 ++-- server/routers/grade-quiz.ts | 1 + server/routers/import-cards.ts | 4 +++- server/routers/rollback-grade.ts | 4 +++- utils/openai.ts | 16 ++++++++++------ 10 files changed, 57 insertions(+), 25 deletions(-) diff --git a/pages/study.tsx b/pages/study.tsx index 1aaa8aa..6b6481f 100644 --- a/pages/study.tsx +++ b/pages/study.tsx @@ -120,10 +120,13 @@ function useQuizState(initialState: State) { } function Study(props: Props) { - const cardsById = props.quizzes.reduce((acc, quiz) => { - acc[quiz.quizId] = quiz; - return acc; - }, {} as Record); + const cardsById = props.quizzes.reduce( + (acc, quiz) => { + acc[quiz.quizId] = quiz; + return acc; + }, + {} as Record, + ); const settings = useUserSettings(); const newState = newQuizState({ cardsById, @@ -199,10 +202,12 @@ function Study(props: Props) { }; failProps.onDiscard = () => { if (f.rollbackData) { - rollbackGrade.mutateAsync({ - id: f.id, - schedulingData: f.rollbackData, - }).then(clear); + rollbackGrade + .mutateAsync({ + id: f.id, + schedulingData: f.rollbackData, + }) + .then(clear); } else { alert("No rollback data found. This is a bug."); } diff --git a/server/quiz-evaluators/listening.ts b/server/quiz-evaluators/listening.ts index a8c1c08..f81d738 100644 --- a/server/quiz-evaluators/listening.ts +++ b/server/quiz-evaluators/listening.ts @@ -10,8 +10,12 @@ export const listening: QuizEvaluator = async (ctx) => { const { userInput, card } = ctx; const { term, definition, langCode } = card; const tplData = { term, definition, langCode }; - const content = template(PROMPT, tplData); - const listeningYN = await yesOrNo(userInput, content); + const question = template(PROMPT, tplData); + const listeningYN = await yesOrNo({ + userInput, + question, + userID: ctx.userID, + }); if (listeningYN.response === "no") { return { diff --git a/server/quiz-evaluators/speaking.ts b/server/quiz-evaluators/speaking.ts index cf0bf45..a63a84c 100644 --- a/server/quiz-evaluators/speaking.ts +++ b/server/quiz-evaluators/speaking.ts @@ -23,29 +23,39 @@ const gradeGrammar = async ( term: string, definition: string, langCode: string, + userID: string, ): Promise => { const tplData = { term, definition, langCode, }; - const content = template(GRAMMAR_PROMPT, tplData); - const grammarYN = await yesOrNo(userInput, content); + const question = template(GRAMMAR_PROMPT, tplData); + const grammarYN = await yesOrNo({ + userInput, + question, + userID, + }); if (grammarYN.response === "no") { return grammarYN; } - const meaningYn = await yesOrNo(userInput, template(MEANING_PROMPT, tplData)); + const meaningYn = await yesOrNo({ + userInput, + question: template(MEANING_PROMPT, tplData), + userID, + }); return meaningYn; }; -export const speaking: QuizEvaluator = async ({ userInput, card }) => { +export const speaking: QuizEvaluator = async ({ userInput, card, userID }) => { const result = await gradeGrammar( userInput, card.term, card.definition, card.langCode, + userID, ); if (result.response === "no") { console.log(`Fail`); diff --git a/server/quiz-evaluators/types.ts b/server/quiz-evaluators/types.ts index 4a52607..8737612 100644 --- a/server/quiz-evaluators/types.ts +++ b/server/quiz-evaluators/types.ts @@ -4,6 +4,7 @@ export type QuizEvaluatorInput = { quiz: Quiz; card: Card; userInput: string; + userID: string; }; export type QuizEvaluatorOutput = { diff --git a/server/routers/bulk-create-cards.ts b/server/routers/bulk-create-cards.ts index 329c4e1..823b68a 100644 --- a/server/routers/bulk-create-cards.ts +++ b/server/routers/bulk-create-cards.ts @@ -7,7 +7,10 @@ export const bulkCreateCards = procedure z.object({ // Koala does not actually support Spanish. // It's a placeholder. - langCode: z.union([z.literal("ko"), z.literal("TODO: Support other langauges.")]), + langCode: z.union([ + z.literal("ko"), + z.literal("TODO: Support other langauges."), + ]), input: z .array( z.object({ diff --git a/server/routers/get-next-quizzes.ts b/server/routers/get-next-quizzes.ts index 497e381..c1a466a 100644 --- a/server/routers/get-next-quizzes.ts +++ b/server/routers/get-next-quizzes.ts @@ -34,8 +34,8 @@ export async function getLessonMeta(userId: string) { lt: currentDate, }, firstReview: { - gt: 0 - } + gt: 0, + }, }, }); diff --git a/server/routers/grade-quiz.ts b/server/routers/grade-quiz.ts index b317ad6..98bbf0e 100644 --- a/server/routers/grade-quiz.ts +++ b/server/routers/grade-quiz.ts @@ -132,6 +132,7 @@ export const gradeQuiz = procedure quiz, card, userInput: transcript.text, + userID: user.id, }); const now = new Date().getTime(); const lastReview = quiz.lastReview; diff --git a/server/routers/import-cards.ts b/server/routers/import-cards.ts index 095c867..1eef767 100644 --- a/server/routers/import-cards.ts +++ b/server/routers/import-cards.ts @@ -113,7 +113,9 @@ export async function setGrade( ...calculateSchedulingData(quiz, grade, now), }, }; - console.log(`(${data.data.id}) Update grade. Next review: ${timeUntil(data.data.nextReview)}`); + console.log( + `(${data.data.id}) Update grade. Next review: ${timeUntil(data.data.nextReview)}`, + ); await prismaClient.quiz.update(data); } diff --git a/server/routers/rollback-grade.ts b/server/routers/rollback-grade.ts index cfc0f42..a8d1b5f 100644 --- a/server/routers/rollback-grade.ts +++ b/server/routers/rollback-grade.ts @@ -36,6 +36,8 @@ export const rollbackGrade = procedure lapses: Math.max(quiz.lapses - 1, 0), }, }; - console.log(`Rollback grade. Next review: ${timeUntil(data.data.nextReview)}`) + console.log( + `Rollback grade. Next review: ${timeUntil(data.data.nextReview)}`, + ); prismaClient.quiz.update(data); }); diff --git a/utils/openai.ts b/utils/openai.ts index 476c99d..94e42c8 100644 --- a/utils/openai.ts +++ b/utils/openai.ts @@ -1,6 +1,7 @@ import OpenAI from "openai"; import { ChatCompletionCreateParamsNonStreaming } from "openai/resources"; import { errorReport } from "./error-report"; +import { isApprovedUser } from "./is-approved-user"; const apiKey = process.env.OPENAI_API_KEY; @@ -54,11 +55,13 @@ const YES_OR_NO_FUNCTION = { }; export type YesOrNo = { response: "yes" | "no"; whyNot?: string }; - -export const yesOrNo = async ( - userInput: string, - question: string, -): Promise => { +export type YesOrNoInput = { + userInput: string; + question: string; + userID: string; +}; +export const yesOrNo = async (input: YesOrNoInput): Promise => { + const { userInput, question, userID } = input; console.log([userInput, question]); const grammarResp = await gptCall({ messages: [ @@ -71,7 +74,7 @@ export const yesOrNo = async ( content: question, }, ], - model: "gpt-3.5-turbo", + model: isApprovedUser(userID) ? "gpt-4-turbo-preview" : "gpt-3.5-turbo", tools: [ { type: "function", @@ -80,6 +83,7 @@ export const yesOrNo = async ( ], max_tokens: 300, temperature: 0.7, + user: userID, tool_choice: { type: "function", function: { name: "yes_or_no" } }, }); const jsonString =