Skip to content

Commit

Permalink
added splitting of MLquiz generated quizzes if more than one are gene…
Browse files Browse the repository at this point in the history
…rated, padding from aila generated questions and placeholders if not
  • Loading branch information
gclomax committed Nov 29, 2024
1 parent 53c901c commit 0738636
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { aiLogger } from "@oakai/logger";

import type {
AilaRagRelevantLesson,
LooseLessonPlan,
Expand All @@ -17,6 +19,7 @@ import { testRatingSchema } from "../rerankers/RerankerStructuredOutputSchema";
import { TestSchemaReranker } from "../rerankers/SchemaReranker";
import { SimpleQuizSelector } from "../selectors/SimpleQuizSelector";

const log = aiLogger("aila:quiz");
export abstract class BaseFullQuizService implements FullQuizService {
public abstract quizSelector: QuizSelector<BaseType>;
public abstract quizReranker: AilaQuizReranker<typeof BaseSchema>;
Expand Down
74 changes: 70 additions & 4 deletions packages/aila/src/core/quiz/generators/MLQuizGenerator.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
// ML-based Quiz Generator
import { aiLogger } from "@oakai/logger";

import type { JsonPatchDocument } from "../../../protocol/jsonPatchProtocol";
import type { Quiz, QuizQuestion } from "../../../protocol/schema";
import type { Quiz, QuizPath, QuizQuestion } from "../../../protocol/schema";
import type { LooseLessonPlan } from "../../../protocol/schema";
import type { CustomHit } from "../interfaces";
import { BaseQuizGenerator } from "./BaseQuizGenerator";

const log = aiLogger("aila:quiz");
export class MLQuizGenerator extends BaseQuizGenerator {
private async unpackAndSearch(
lessonPlan: LooseLessonPlan,
Expand All @@ -15,6 +18,58 @@ export class MLQuizGenerator extends BaseQuizGenerator {
return results.hits;
}

// Our Rag system may retrieve N Questions. We split them into chunks of 6 to conform with the schema. If we have less than 6 questions we pad with questions from the appropriate section of the lesson plan.
// If there are no questions for padding, we pad with empty questions.
private splitQuestionsIntoSixAndPad(
lessonPlan: LooseLessonPlan,
quizQuestions: QuizQuestion[],
quizType: QuizPath,
): QuizQuestion[][] {
const quizQuestions2DArray: QuizQuestion[][] = [];
log.info(
`MLQuizGenerator: Splitting ${quizQuestions.length} questions into chunks of 6`,
);
const chunkSize = 6;

const questionsForPadding =
quizType === "/starterQuiz"
? lessonPlan.starterQuiz
: lessonPlan.exitQuiz;

const backupQuestion: QuizQuestion = {
question: " ",
answers: [" ", " ", " "],
distractors: [" ", " ", " "],
};
// Split questions into chunks of 6
for (let i = 0; i < quizQuestions.length; i += chunkSize) {
const chunk = quizQuestions.slice(i, i + chunkSize);

// If the last chunk has less than 6 questions, pad it with questions from lessonPlan
if (chunk.length < chunkSize && i + chunkSize >= quizQuestions.length) {
const remainingCount = chunkSize - chunk.length;

if (questionsForPadding) {
const paddingQuestions =
questionsForPadding
?.filter(
(q): q is QuizQuestion =>
!!q?.question && !!q?.answers && !!q?.distractors,
)
.slice(0, remainingCount) ||
Array(remainingCount).fill(backupQuestion);
chunk.push(...paddingQuestions);
} else {
const paddingQuestions = Array(remainingCount).fill(backupQuestion);
chunk.push(...paddingQuestions);
}
}
quizQuestions2DArray.push(chunk);
}

return quizQuestions2DArray;
}

private async generateMathsQuizML(
lessonPlan: LooseLessonPlan,
): Promise<QuizQuestion[]> {
Expand All @@ -23,7 +78,8 @@ export class MLQuizGenerator extends BaseQuizGenerator {
const customIds = await this.rerankAndExtractCustomIds(hits, qq);
const quizQuestions = await this.retrieveAndProcessQuestions(customIds);
// This should return an array of questions - sometimes there are more than six questions.
return quizQuestions.slice(0, 6);
// TODO: GCLOMAX - make this return multiples of six.
return quizQuestions;
}

// TODO: GCLOMAX - Change for starter and exit quizzes.
Expand All @@ -32,15 +88,25 @@ export class MLQuizGenerator extends BaseQuizGenerator {
): Promise<Quiz[]> {
const quiz: QuizQuestion[] = await this.generateMathsQuizML(lessonPlan);
// Now we make the quiz into a 2D array
const quiz2DArray = [quiz];
const quiz2DArray = this.splitQuestionsIntoSixAndPad(
lessonPlan,
quiz,
"/starterQuiz",
);
log.info(`MLGenerator: Generated ${quiz2DArray.length} starter Quizzes`);
return quiz2DArray;
}
public async generateMathsExitQuizPatch(
lessonPlan: LooseLessonPlan,
): Promise<Quiz[]> {
const quiz: QuizQuestion[] = await this.generateMathsQuizML(lessonPlan);
// Now we make the quiz into a 2D array
const quiz2DArray = [quiz];
const quiz2DArray = this.splitQuestionsIntoSixAndPad(
lessonPlan,
quiz,
"/exitQuiz",
);
log.info(`MLGenerator: Generated ${quiz2DArray.length} exit questions`);
return quiz2DArray;
}
}
2 changes: 2 additions & 0 deletions packages/aila/src/core/quiz/rerankers/AilaQuizReranker.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { aiLogger } from "@oakai/logger";
import type { z } from "zod";

import type { QuizPath, QuizQuestion } from "../../../protocol/schema";
Expand All @@ -12,6 +13,7 @@ import { processArray } from "../apiCallingUtils";
import { withRandomDelay } from "../apiCallingUtils";
import type { AilaQuizReranker } from "../interfaces";

const log = aiLogger("aila:quiz");
export abstract class BasedOnRagAilaQuizReranker<T extends typeof BaseSchema>
implements AilaQuizReranker<T>
{
Expand Down

0 comments on commit 0738636

Please sign in to comment.