mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-09-02 13:52:46 +02:00
Improve fine-tuning
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
import { SearchIcon, WandIcon } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { cn } from '../../lib/classname';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { showLoginPopup } from '../../lib/popup';
|
||||
import { UserCoursesList } from './UserCoursesList';
|
||||
import { FineTuneCourse } from './FineTuneCourse';
|
||||
import {
|
||||
getCourseFineTuneData,
|
||||
getLastSessionId,
|
||||
storeFineTuneData,
|
||||
} from '../../lib/ai';
|
||||
|
||||
export const difficultyLevels = [
|
||||
'beginner',
|
||||
@@ -19,6 +24,27 @@ export function AICourse(props: AICourseProps) {
|
||||
const [keyword, setKeyword] = useState('');
|
||||
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) => {
|
||||
if (e.key === 'Enter' && keyword.trim()) {
|
||||
onSubmit();
|
||||
@@ -31,7 +57,15 @@ export function AICourse(props: AICourseProps) {
|
||||
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 (
|
||||
@@ -99,7 +133,16 @@ export function AICourse(props: AICourseProps) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<FineTuneCourse />
|
||||
<FineTuneCourse
|
||||
hasFineTuneData={hasFineTuneData}
|
||||
setHasFineTuneData={setHasFineTuneData}
|
||||
about={about}
|
||||
goal={goal}
|
||||
customInstructions={customInstructions}
|
||||
setAbout={setAbout}
|
||||
setGoal={setGoal}
|
||||
setCustomInstructions={setCustomInstructions}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
|
@@ -14,7 +14,7 @@ function Question(props: QuestionProps) {
|
||||
|
||||
return (
|
||||
<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>
|
||||
<textarea
|
||||
@@ -28,26 +28,46 @@ function Question(props: QuestionProps) {
|
||||
);
|
||||
}
|
||||
|
||||
export function FineTuneCourse() {
|
||||
const [isFineTuning, setIsFineTuning] = useState(false);
|
||||
type FineTuneCourseProps = {
|
||||
hasFineTuneData: boolean;
|
||||
about: string;
|
||||
goal: string;
|
||||
customInstructions: string;
|
||||
|
||||
const [about, setAbout] = useState('');
|
||||
const [goal, setGoal] = useState('');
|
||||
const [customInstructions, setCustomInstructions] = useState('');
|
||||
setHasFineTuneData: (hasMetadata: boolean) => void;
|
||||
setAbout: (about: string) => void;
|
||||
setGoal: (goal: string) => void;
|
||||
setCustomInstructions: (customInstructions: string) => void;
|
||||
};
|
||||
|
||||
export function FineTuneCourse(props: FineTuneCourseProps) {
|
||||
const {
|
||||
about,
|
||||
goal,
|
||||
customInstructions,
|
||||
hasFineTuneData,
|
||||
setAbout,
|
||||
setGoal,
|
||||
setCustomInstructions,
|
||||
setHasFineTuneData,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col overflow-hidden rounded-lg border border-gray-200 transition-all">
|
||||
<label
|
||||
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',
|
||||
isFineTuning && 'bg-gray-100',
|
||||
hasFineTuneData && 'bg-gray-100',
|
||||
)}
|
||||
>
|
||||
<input
|
||||
id="fine-tune-checkbox"
|
||||
type="checkbox"
|
||||
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){' '}
|
||||
<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>
|
||||
</label>
|
||||
|
||||
{isFineTuning && (
|
||||
{hasFineTuneData && (
|
||||
<div className="mt-0 flex flex-col">
|
||||
<Question
|
||||
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}
|
||||
onChange={setAbout}
|
||||
autoFocus={true}
|
||||
/>
|
||||
<Question
|
||||
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}
|
||||
onChange={setGoal}
|
||||
/>
|
||||
<Question
|
||||
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}
|
||||
onChange={setCustomInstructions}
|
||||
/>
|
||||
|
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { getUrlParams } from '../../lib/browser';
|
||||
import { isLoggedIn } from '../../lib/jwt';
|
||||
import { type AiCourse } from '../../lib/ai';
|
||||
import { getCourseFineTuneData, type AiCourse } from '../../lib/ai';
|
||||
import { AICourseContent } from './AICourseContent';
|
||||
import { generateCourse } from '../../helper/generate-ai-course';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
@@ -13,6 +13,10 @@ type GenerateAICourseProps = {};
|
||||
export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
const [term, setTerm] = 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 [error, setError] = useState('');
|
||||
@@ -54,16 +58,47 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
|
||||
setTerm(paramsTerm);
|
||||
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]);
|
||||
|
||||
const handleGenerateCourse = async (options: {
|
||||
term: string;
|
||||
difficulty: string;
|
||||
instructions?: string;
|
||||
goal?: string;
|
||||
about?: string;
|
||||
isForce?: boolean;
|
||||
prompt?: string;
|
||||
}) => {
|
||||
const { term, difficulty, isForce, prompt } = options;
|
||||
const { term, difficulty, isForce, prompt, instructions, goal, about } =
|
||||
options;
|
||||
|
||||
if (!isLoggedIn()) {
|
||||
window.location.href = '/ai-tutor';
|
||||
@@ -79,6 +114,9 @@ export function GenerateAICourse(props: GenerateAICourseProps) {
|
||||
onCourseChange: setCourse,
|
||||
onLoadingChange: setIsLoading,
|
||||
onError: setError,
|
||||
instructions,
|
||||
goal,
|
||||
about,
|
||||
isForce,
|
||||
prompt,
|
||||
});
|
||||
|
@@ -12,6 +12,9 @@ type GenerateCourseOptions = {
|
||||
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;
|
||||
@@ -31,6 +34,9 @@ export async function generateCourse(options: GenerateCourseOptions) {
|
||||
onError,
|
||||
isForce = false,
|
||||
prompt,
|
||||
instructions,
|
||||
goal,
|
||||
about,
|
||||
} = options;
|
||||
|
||||
onLoadingChange?.(true);
|
||||
@@ -76,6 +82,9 @@ export async function generateCourse(options: GenerateCourseOptions) {
|
||||
difficulty,
|
||||
isForce,
|
||||
customPrompt: prompt,
|
||||
instructions,
|
||||
goal,
|
||||
about,
|
||||
}),
|
||||
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);
|
||||
|
||||
export async function readAIRoadmapStream(
|
||||
|
Reference in New Issue
Block a user