mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 05:42:41 +02:00
Improve fine-tuning
This commit is contained in:
@@ -1,10 +1,15 @@
|
|||||||
import { SearchIcon, WandIcon } from 'lucide-react';
|
import { SearchIcon, WandIcon } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
import { showLoginPopup } from '../../lib/popup';
|
import { showLoginPopup } from '../../lib/popup';
|
||||||
import { UserCoursesList } from './UserCoursesList';
|
import { UserCoursesList } from './UserCoursesList';
|
||||||
import { FineTuneCourse } from './FineTuneCourse';
|
import { FineTuneCourse } from './FineTuneCourse';
|
||||||
|
import {
|
||||||
|
getCourseFineTuneData,
|
||||||
|
getLastSessionId,
|
||||||
|
storeFineTuneData,
|
||||||
|
} from '../../lib/ai';
|
||||||
|
|
||||||
export const difficultyLevels = [
|
export const difficultyLevels = [
|
||||||
'beginner',
|
'beginner',
|
||||||
@@ -19,6 +24,27 @@ export function AICourse(props: AICourseProps) {
|
|||||||
const [keyword, setKeyword] = useState('');
|
const [keyword, setKeyword] = useState('');
|
||||||
const [difficulty, setDifficulty] = useState<DifficultyLevel>('beginner');
|
const [difficulty, setDifficulty] = useState<DifficultyLevel>('beginner');
|
||||||
|
|
||||||
|
const [hasFineTuneData, setHasFineTuneData] = useState(false);
|
||||||
|
const [about, setAbout] = useState('');
|
||||||
|
const [goal, setGoal] = useState('');
|
||||||
|
const [customInstructions, setCustomInstructions] = useState('');
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const lastSessionId = getLastSessionId();
|
||||||
|
if (!lastSessionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fineTuneData = getCourseFineTuneData(lastSessionId);
|
||||||
|
if (!fineTuneData) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// setAbout(fineTuneData.about);
|
||||||
|
// setGoal(fineTuneData.goal);
|
||||||
|
// setCustomInstructions(fineTuneData.customInstructions);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Enter' && keyword.trim()) {
|
if (e.key === 'Enter' && keyword.trim()) {
|
||||||
onSubmit();
|
onSubmit();
|
||||||
@@ -31,7 +57,15 @@ export function AICourse(props: AICourseProps) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = `/ai-tutor/search?term=${encodeURIComponent(keyword)}&difficulty=${difficulty}`;
|
const sessionId = hasFineTuneData
|
||||||
|
? storeFineTuneData({
|
||||||
|
about,
|
||||||
|
goal,
|
||||||
|
customInstructions,
|
||||||
|
})
|
||||||
|
: '';
|
||||||
|
|
||||||
|
window.location.href = `/ai-tutor/search?term=${encodeURIComponent(keyword)}&difficulty=${difficulty}&id=${sessionId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -99,7 +133,16 @@ export function AICourse(props: AICourseProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FineTuneCourse />
|
<FineTuneCourse
|
||||||
|
hasFineTuneData={hasFineTuneData}
|
||||||
|
setHasFineTuneData={setHasFineTuneData}
|
||||||
|
about={about}
|
||||||
|
goal={goal}
|
||||||
|
customInstructions={customInstructions}
|
||||||
|
setAbout={setAbout}
|
||||||
|
setGoal={setGoal}
|
||||||
|
setCustomInstructions={setCustomInstructions}
|
||||||
|
/>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@@ -14,7 +14,7 @@ function Question(props: QuestionProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<label className="bg-gray-100 border-y px-4 py-2.5 text-sm font-medium text-gray-700">
|
<label className="border-y bg-gray-100 px-4 py-2.5 text-sm font-medium text-gray-700">
|
||||||
{label}
|
{label}
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@@ -28,26 +28,46 @@ function Question(props: QuestionProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FineTuneCourse() {
|
type FineTuneCourseProps = {
|
||||||
const [isFineTuning, setIsFineTuning] = useState(false);
|
hasFineTuneData: boolean;
|
||||||
|
about: string;
|
||||||
|
goal: string;
|
||||||
|
customInstructions: string;
|
||||||
|
|
||||||
const [about, setAbout] = useState('');
|
setHasFineTuneData: (hasMetadata: boolean) => void;
|
||||||
const [goal, setGoal] = useState('');
|
setAbout: (about: string) => void;
|
||||||
const [customInstructions, setCustomInstructions] = useState('');
|
setGoal: (goal: string) => void;
|
||||||
|
setCustomInstructions: (customInstructions: string) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function FineTuneCourse(props: FineTuneCourseProps) {
|
||||||
|
const {
|
||||||
|
about,
|
||||||
|
goal,
|
||||||
|
customInstructions,
|
||||||
|
hasFineTuneData,
|
||||||
|
setAbout,
|
||||||
|
setGoal,
|
||||||
|
setCustomInstructions,
|
||||||
|
setHasFineTuneData,
|
||||||
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col overflow-hidden rounded-lg border border-gray-200 transition-all">
|
<div className="flex flex-col overflow-hidden rounded-lg border border-gray-200 transition-all">
|
||||||
<label
|
<label
|
||||||
className={cn(
|
className={cn(
|
||||||
'group flex cursor-pointer select-none flex-row items-center gap-2.5 px-4 py-3 text-left text-gray-500 transition-colors hover:bg-gray-100 focus:outline-none',
|
'group flex cursor-pointer select-none flex-row items-center gap-2.5 px-4 py-3 text-left text-gray-500 transition-colors hover:bg-gray-100 focus:outline-none',
|
||||||
isFineTuning && 'bg-gray-100',
|
hasFineTuneData && 'bg-gray-100',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="fine-tune-checkbox"
|
id="fine-tune-checkbox"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
className="h-4 w-4 group-hover:fill-current"
|
className="h-4 w-4 group-hover:fill-current"
|
||||||
onChange={() => setIsFineTuning(!isFineTuning)}
|
checked={hasFineTuneData}
|
||||||
|
onChange={() => {
|
||||||
|
setHasFineTuneData(!hasFineTuneData);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
Tell us more to tailor the course (optional){' '}
|
Tell us more to tailor the course (optional){' '}
|
||||||
<span className="ml-auto rounded-md bg-gray-400 px-2 py-0.5 text-xs text-white">
|
<span className="ml-auto rounded-md bg-gray-400 px-2 py-0.5 text-xs text-white">
|
||||||
@@ -55,24 +75,24 @@ export function FineTuneCourse() {
|
|||||||
</span>
|
</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
{isFineTuning && (
|
{hasFineTuneData && (
|
||||||
<div className="mt-0 flex flex-col">
|
<div className="mt-0 flex flex-col">
|
||||||
<Question
|
<Question
|
||||||
label="Tell us about your self"
|
label="Tell us about your self"
|
||||||
placeholder="e.g. I have a background in marketing and I already have some basic knowledge of coding."
|
placeholder="e.g. I am a frontend developer and have good knowledge of HTML, CSS, and JavaScript."
|
||||||
value={about}
|
value={about}
|
||||||
onChange={setAbout}
|
onChange={setAbout}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
/>
|
/>
|
||||||
<Question
|
<Question
|
||||||
label="What is your goal with this course?"
|
label="What is your goal with this course?"
|
||||||
placeholder="e.g. I want to learn about advanced topics with focus on practical examples."
|
placeholder="e.g. I want to be able to build Node.js APIs with Express.js and MongoDB."
|
||||||
value={goal}
|
value={goal}
|
||||||
onChange={setGoal}
|
onChange={setGoal}
|
||||||
/>
|
/>
|
||||||
<Question
|
<Question
|
||||||
label="Custom Instructions (Optional)"
|
label="Custom Instructions (Optional)"
|
||||||
placeholder="Give instructions to the AI as if you were giving them to a friend."
|
placeholder="Give additional instructions to the AI as if you were giving them to a friend."
|
||||||
value={customInstructions}
|
value={customInstructions}
|
||||||
onChange={setCustomInstructions}
|
onChange={setCustomInstructions}
|
||||||
/>
|
/>
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { getUrlParams } from '../../lib/browser';
|
import { getUrlParams } from '../../lib/browser';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
import { type AiCourse } from '../../lib/ai';
|
import { getCourseFineTuneData, type AiCourse } from '../../lib/ai';
|
||||||
import { AICourseContent } from './AICourseContent';
|
import { AICourseContent } from './AICourseContent';
|
||||||
import { generateCourse } from '../../helper/generate-ai-course';
|
import { generateCourse } from '../../helper/generate-ai-course';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
@@ -13,6 +13,10 @@ type GenerateAICourseProps = {};
|
|||||||
export function GenerateAICourse(props: GenerateAICourseProps) {
|
export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||||
const [term, setTerm] = useState('');
|
const [term, setTerm] = useState('');
|
||||||
const [difficulty, setDifficulty] = useState('');
|
const [difficulty, setDifficulty] = useState('');
|
||||||
|
const [sessionId, setSessionId] = useState('');
|
||||||
|
const [goal, setGoal] = useState('');
|
||||||
|
const [about, setAbout] = useState('');
|
||||||
|
const [customInstructions, setCustomInstructions] = useState('');
|
||||||
|
|
||||||
const [isLoading, setIsLoading] = useState(true);
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
@@ -54,16 +58,47 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
|||||||
|
|
||||||
setTerm(paramsTerm);
|
setTerm(paramsTerm);
|
||||||
setDifficulty(paramsDifficulty);
|
setDifficulty(paramsDifficulty);
|
||||||
handleGenerateCourse({ term: paramsTerm, difficulty: paramsDifficulty });
|
|
||||||
|
const sessionId = params?.id;
|
||||||
|
setSessionId(sessionId);
|
||||||
|
|
||||||
|
let paramsGoal = '';
|
||||||
|
let paramsAbout = '';
|
||||||
|
let paramsCustomInstructions = '';
|
||||||
|
|
||||||
|
if (sessionId) {
|
||||||
|
const fineTuneData = getCourseFineTuneData(sessionId);
|
||||||
|
if (fineTuneData) {
|
||||||
|
paramsGoal = fineTuneData.goal;
|
||||||
|
paramsAbout = fineTuneData.about;
|
||||||
|
paramsCustomInstructions = fineTuneData.customInstructions;
|
||||||
|
|
||||||
|
setGoal(paramsGoal);
|
||||||
|
setAbout(paramsAbout);
|
||||||
|
setCustomInstructions(paramsCustomInstructions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleGenerateCourse({
|
||||||
|
term: paramsTerm,
|
||||||
|
difficulty: paramsDifficulty,
|
||||||
|
instructions: paramsCustomInstructions,
|
||||||
|
goal: paramsGoal,
|
||||||
|
about: paramsAbout,
|
||||||
|
});
|
||||||
}, [term, difficulty]);
|
}, [term, difficulty]);
|
||||||
|
|
||||||
const handleGenerateCourse = async (options: {
|
const handleGenerateCourse = async (options: {
|
||||||
term: string;
|
term: string;
|
||||||
difficulty: string;
|
difficulty: string;
|
||||||
|
instructions?: string;
|
||||||
|
goal?: string;
|
||||||
|
about?: string;
|
||||||
isForce?: boolean;
|
isForce?: boolean;
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { term, difficulty, isForce, prompt } = options;
|
const { term, difficulty, isForce, prompt, instructions, goal, about } =
|
||||||
|
options;
|
||||||
|
|
||||||
if (!isLoggedIn()) {
|
if (!isLoggedIn()) {
|
||||||
window.location.href = '/ai-tutor';
|
window.location.href = '/ai-tutor';
|
||||||
@@ -79,6 +114,9 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
|||||||
onCourseChange: setCourse,
|
onCourseChange: setCourse,
|
||||||
onLoadingChange: setIsLoading,
|
onLoadingChange: setIsLoading,
|
||||||
onError: setError,
|
onError: setError,
|
||||||
|
instructions,
|
||||||
|
goal,
|
||||||
|
about,
|
||||||
isForce,
|
isForce,
|
||||||
prompt,
|
prompt,
|
||||||
});
|
});
|
||||||
|
@@ -12,6 +12,9 @@ type GenerateCourseOptions = {
|
|||||||
slug?: string;
|
slug?: string;
|
||||||
isForce?: boolean;
|
isForce?: boolean;
|
||||||
prompt?: string;
|
prompt?: string;
|
||||||
|
instructions?: string;
|
||||||
|
goal?: string;
|
||||||
|
about?: string;
|
||||||
onCourseIdChange?: (courseId: string) => void;
|
onCourseIdChange?: (courseId: string) => void;
|
||||||
onCourseSlugChange?: (courseSlug: string) => void;
|
onCourseSlugChange?: (courseSlug: string) => void;
|
||||||
onCourseChange?: (course: AiCourse, rawData: string) => void;
|
onCourseChange?: (course: AiCourse, rawData: string) => void;
|
||||||
@@ -31,6 +34,9 @@ export async function generateCourse(options: GenerateCourseOptions) {
|
|||||||
onError,
|
onError,
|
||||||
isForce = false,
|
isForce = false,
|
||||||
prompt,
|
prompt,
|
||||||
|
instructions,
|
||||||
|
goal,
|
||||||
|
about,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
onLoadingChange?.(true);
|
onLoadingChange?.(true);
|
||||||
@@ -76,6 +82,9 @@ export async function generateCourse(options: GenerateCourseOptions) {
|
|||||||
difficulty,
|
difficulty,
|
||||||
isForce,
|
isForce,
|
||||||
customPrompt: prompt,
|
customPrompt: prompt,
|
||||||
|
instructions,
|
||||||
|
goal,
|
||||||
|
about,
|
||||||
}),
|
}),
|
||||||
credentials: 'include',
|
credentials: 'include',
|
||||||
},
|
},
|
||||||
|
@@ -55,6 +55,45 @@ export function generateAiCourseStructure(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CourseFineTuneData = {
|
||||||
|
about: string;
|
||||||
|
goal: string;
|
||||||
|
customInstructions: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function storeFineTuneData(meta: CourseFineTuneData) {
|
||||||
|
const sessionId = Date.now().toString();
|
||||||
|
|
||||||
|
sessionStorage.setItem(sessionId, JSON.stringify(meta));
|
||||||
|
sessionStorage.setItem('lastSessionId', sessionId);
|
||||||
|
|
||||||
|
return sessionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCourseFineTuneData(
|
||||||
|
sessionId: string,
|
||||||
|
): CourseFineTuneData | null {
|
||||||
|
const meta = sessionStorage.getItem(sessionId);
|
||||||
|
if (!meta) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.parse(meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLastSessionId(): string | null {
|
||||||
|
return sessionStorage.getItem('lastSessionId');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearFineTuneData() {
|
||||||
|
const sessionId = getLastSessionId();
|
||||||
|
if (sessionId) {
|
||||||
|
sessionStorage.removeItem(sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionStorage.removeItem('lastSessionId');
|
||||||
|
}
|
||||||
|
|
||||||
const NEW_LINE = '\n'.charCodeAt(0);
|
const NEW_LINE = '\n'.charCodeAt(0);
|
||||||
|
|
||||||
export async function readAIRoadmapStream(
|
export async function readAIRoadmapStream(
|
||||||
|
Reference in New Issue
Block a user