Roadmap to becoming a developer in 2022
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

184 lines
4.8 KiB

import {
generateAiCourseStructure,
readStream,
type AiCourse,
} from '../lib/ai';
import { queryClient } from '../stores/query-client';
import { getAiCourseLimitOptions } from '../queries/ai-course';
type GenerateCourseOptions = {
term: string;
difficulty: string;
slug?: string;
isForce?: boolean;
prompt?: string;
instructions?: string;
goal?: string;
about?: string;
onCourseIdChange?: (courseId: string) => void;
onCourseSlugChange?: (courseSlug: string) => void;
onCourseChange?: (course: AiCourse, rawData: string) => void;
onLoadingChange?: (isLoading: boolean) => void;
onCreatorIdChange?: (creatorId: string) => void;
onError?: (error: string) => void;
src?: string;
};
export async function generateCourse(options: GenerateCourseOptions) {
const {
term,
slug,
difficulty,
onCourseIdChange,
onCourseSlugChange,
onCourseChange,
onLoadingChange,
onError,
onCreatorIdChange,
isForce = false,
prompt,
instructions,
goal,
about,
src = 'search',
} = options;
onLoadingChange?.(true);
onCourseChange?.(
{
title: '',
modules: [],
difficulty: '',
done: [],
},
'',
);
onError?.('');
try {
let response = null;
if (slug && isForce) {
response = await fetch(
`${import.meta.env.PUBLIC_API_URL}/v1-regenerate-ai-course/${slug}`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
credentials: 'include',
body: JSON.stringify({
isForce,
customPrompt: prompt,
}),
},
);
} else {
response = await fetch(
`${import.meta.env.PUBLIC_API_URL}/v1-generate-ai-course`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
keyword: term,
difficulty,
isForce,
customPrompt: prompt,
instructions,
goal,
about,
src,
}),
credentials: 'include',
},
);
}
if (!response.ok) {
const data = await response.json();
console.error(
'Error generating course:',
data?.message || 'Something went wrong',
);
onLoadingChange?.(false);
onError?.(data?.message || 'Something went wrong');
return;
}
const reader = response.body?.getReader();
if (!reader) {
console.error('Failed to get reader from response');
onError?.('Something went wrong');
onLoadingChange?.(false);
return;
}
const COURSE_ID_REGEX = new RegExp('@COURSEID:(\\w+)@');
const COURSE_SLUG_REGEX = new RegExp(/@COURSESLUG:([\w-]+)@/);
const CREATOR_ID_REGEX = new RegExp('@CREATORID:(\\w+)@');
await readStream(reader, {
onStream: (result) => {
if (result.includes('@COURSEID') || result.includes('@COURSESLUG')) {
const courseIdMatch = result.match(COURSE_ID_REGEX);
const courseSlugMatch = result.match(COURSE_SLUG_REGEX);
const creatorIdMatch = result.match(CREATOR_ID_REGEX);
const extractedCourseId = courseIdMatch?.[1] || '';
const extractedCourseSlug = courseSlugMatch?.[1] || '';
const extractedCreatorId = creatorIdMatch?.[1] || '';
if (extractedCourseSlug) {
window.history.replaceState(
{
courseId: extractedCourseId,
courseSlug: extractedCourseSlug,
term,
difficulty,
},
'',
`${origin}/ai/${extractedCourseSlug}`,
);
}
result = result
.replace(COURSE_ID_REGEX, '')
.replace(COURSE_SLUG_REGEX, '')
.replace(CREATOR_ID_REGEX, '');
onCourseIdChange?.(extractedCourseId);
onCourseSlugChange?.(extractedCourseSlug);
onCreatorIdChange?.(extractedCreatorId);
}
try {
const aiCourse = generateAiCourseStructure(result);
onCourseChange?.(
{
...aiCourse,
difficulty: difficulty || '',
},
result,
);
} catch (e) {
console.error('Error parsing streamed course content:', e);
}
},
onStreamEnd: (result) => {
result = result
.replace(COURSE_ID_REGEX, '')
.replace(COURSE_SLUG_REGEX, '')
.replace(CREATOR_ID_REGEX, '');
onLoadingChange?.(false);
queryClient.invalidateQueries(getAiCourseLimitOptions());
},
});
} catch (error: any) {
onError?.(error?.message || 'Something went wrong');
console.error('Error in course generation:', error);
onLoadingChange?.(false);
}
}