Skip to content

Commit

Permalink
feat(seeders): generate review message, and about with groq
Browse files Browse the repository at this point in the history
  • Loading branch information
Veirt committed Dec 10, 2024
1 parent 293442a commit 12ecc02
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 55 deletions.
46 changes: 46 additions & 0 deletions src/common/groq.seeder.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Groq } from "groq-sdk";

export async function retryOperation(
operation: () => Promise<any>,
retries = 30,
) {
for (let i = 0; i < retries; i++) {
try {
return await operation();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000));
console.log(`Retry ${i + 1}/${retries}`);
}
}
}

export const generateSampleData = async (prompt: string) => {
const groq = new Groq({
apiKey: process.env["GROQ_KEY"],
});

const chatCompletion = await groq.chat.completions.create({
messages: [
{
role: "user",
content: prompt,
},
],
response_format: { type: "json_object" },
model: "llama3-8b-8192",
});

// Parse the response
const result = chatCompletion.choices[0].message.content;

let json;
try {
json = JSON.parse(result!);
} catch (error) {
throw new Error("Failed to parse response as JSON");
}

console.log(json);
return json;
};
1 change: 1 addition & 0 deletions src/module/order/order.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export class OrderRepository {
const query = this.db
.select({
id: orders.id,
name: tutoriesTable.name,
status: orders.status,
sessionTime: orders.sessionTime,
estimatedEndTime: orders.estimatedEndTime,
Expand Down
33 changes: 30 additions & 3 deletions src/module/review/review.seeder.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { generateSampleData, retryOperation } from "@/common/groq.seeder";
import { container } from "@/container";
import { faker } from "@faker-js/faker";

const orderRepository = container.orderRepository;
const reviewRepository = container.reviewRepository;

export const seedReviews = async () => {
function prompt(rating: number, category: string, title: string) {
return `You are a learner that is learning category ${category}.
You are learning from a tutor about ${title}, your rating is ${rating} out of 5. Explain your experience with the tutor.
The JSON schema should include
{
"message": "string (min 10 characters, max 1000 characters)",
}`;
}

export const seedReviews = async ({ generateWithGroq = false }) => {
const orders = await orderRepository.getOrders({});

if (orders.length === 0) {
Expand All @@ -13,10 +23,27 @@ export const seedReviews = async () => {

console.log(`Seeding reviews with ${orders.length} data...`);
for (let i = 0; i < orders.length; i++) {
const rating = faker.number.int({ min: 1, max: 5 });
const { message } = generateWithGroq
? await retryOperation(async () => {
const result = await generateSampleData(
prompt(rating, orders[i].categoryName, orders[i].name),
);

if (result.message.length < 10 || result.message.length > 1000) {
throw new Error(
"Message length must be between 10 and 1000 characters",
);
}

return result;
})
: faker.lorem.sentence();

await reviewRepository.createReview({
orderId: orders[i].id,
rating: faker.number.int({ min: 1, max: 5 }),
message: faker.lorem.sentence(),
rating,
message,
});
}
};
89 changes: 38 additions & 51 deletions src/module/tutories/tutories.seeder.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,22 @@
import { generateSampleData, retryOperation } from "@/common/groq.seeder";
import { container } from "@/container";
import { Tutories } from "@/types";
import { faker } from "@faker-js/faker";
import Groq from "groq-sdk";

const categoryRepository = container.categoryRepository;
const tutorRepository = container.tutorRepository;
const tutoriesRepository = container.tutoriesRepository;

async function retryOperation(operation: () => Promise<any>, retries = 10) {
for (let i = 0; i < retries; i++) {
try {
return await operation();
} catch (error) {
if (i === retries - 1) throw error;
await new Promise((resolve) => setTimeout(resolve, 1000 * i));
console.log(`Retry ${i + 1}/${retries}`);
}
}
}

const prompt = (category: string) => {
return `You are a tutor that is teaching category ${category}. The name is the title/headline on what specific topic you teach, keep it short and simple but descriptive. The JSON schema should include
const prompt = (tutorName: string, category: string) => {
return `You are a tutor. Your name is ${tutorName} that is teaching category ${category}. You need to make title/headline on what specific topic you teach, keep it very short and simple but descriptive.
For aboutYou, you should also tell about yourself in that particular topic, what makes you unique, what is your experience, and why should someone choose you.
The JSON schema should include
{
"name": "string (max 30 characters)",
"title": "string (max 30 characters)",
"aboutYou": "string (min 10 characters, max 1000 characters)",
"teachingMethodology": "string (must be detailed, min 10 characters, max 1000 characters)",
}`;
};
const generateNameAndTeachingMethod = async (categoryName: string) => {
const groq = new Groq({
apiKey: process.env["GROQ_KEY"],
});
const chatCompletion = await groq.chat.completions.create({
messages: [
{
role: "user",
content: prompt(categoryName),
},
],
response_format: { type: "json_object" },
model: "llama3-8b-8192",
});

const result = chatCompletion.choices[0].message.content;
const json = JSON.parse(result!);
if (!json || !json.name || !json.teachingMethodology)
throw new Error("Result is empty");

console.log(json.name);
if (json.name.length > 30) {
throw new Error("Name should be less than 30 characters");
}

return json;
};

export const seedTutories = async ({ generateWithGroq = false }) => {
const [categoryExists, tutorsExists] = await Promise.all([
Expand All @@ -64,7 +28,6 @@ export const seedTutories = async ({ generateWithGroq = false }) => {
throw new Error("Tutors or categories not found");
}

// Fetch required data
const tutors = await tutorRepository.getAllTutors();

const tutories: Tutories[] = [];
Expand All @@ -76,21 +39,45 @@ export const seedTutories = async ({ generateWithGroq = false }) => {
);
const randomCategory = faker.helpers.arrayElement(categories);

const { name, teachingMethodology } = generateWithGroq
? await retryOperation(() =>
generateNameAndTeachingMethod(randomCategory.name),
)
const { title, aboutYou, teachingMethodology } = generateWithGroq
? await retryOperation(async () => {
const result = await generateSampleData(
prompt(tutor.name, randomCategory.name),
);

if (result.title.length > 30) {
throw new Error("Title length exceeds 30 characters");
}

if (result.aboutYou.length < 10 || result.aboutYou.length > 1000) {
throw new Error(
"AboutYou length should be between 10 and 1000 characters",
);
}

if (
result.teachingMethodology.length < 10 ||
result.teachingMethodology.length > 1000
) {
throw new Error(
"TeachingMethodology length should be between 10 and 1000 characters",
);
}

return result;
})
: {
name: faker.lorem.words({ min: 1, max: 2 }),
title: faker.lorem.words({ min: 1, max: 2 }),
aboutYou: faker.lorem.paragraph(),
teachingMethodology: faker.lorem.paragraph(),
};

tutories.push({
name,
name: title,
tutorId: tutor.id,
categoryId: randomCategory.id,
createdAt: new Date(),
aboutYou: faker.lorem.paragraph(),
aboutYou,
teachingMethodology,
hourlyRate: faker.helpers.arrayElement([50000, 100000, 150000, 200000]),
typeLesson: faker.helpers.arrayElement(["online", "offline", "both"]),
Expand Down
2 changes: 1 addition & 1 deletion src/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const runSeeder = async () => {
await seedTutories({ generateWithGroq: true });
await assignTutorProfilePictures();
await seedOrders();
await seedReviews();
await seedReviews({ generateWithGroq: true });
};

runSeeder();

0 comments on commit 12ecc02

Please sign in to comment.