Download src/lib/api/tuition-brain.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 4.57 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/api/tuition-brain.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/api/tuition-brain.ts
-
curl -L -o tuition-brain.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/api/tuition-brain.ts
4.57 kB
| import { apiPost } from "@/lib/api/client"; | |
| import { shouldUseDemoData } from "@/lib/api/mode"; | |
| import { | |
| deterministicTuitionPlan, | |
| type TuitionBrainMission, | |
| type TuitionBrainPlan, | |
| type TuitionBrainProfileInput, | |
| } from "@/components/tuition/chapter-dashboard/tuitionBrainPlan"; | |
| /** | |
| * The real tuition brain client. | |
| * | |
| * `planTodaysTuition` is what the UI calls: it asks the backend LLM to reason | |
| * over the student's profile + their REAL curated missions (restate what they | |
| * need, pick today's focus, why), and falls back to the deterministic plan when | |
| * logged out or the backend/LLM is unavailable. Either way the recommendation | |
| * is always a real mission — the backend validates the LLM pick against the | |
| * candidates, and the fallback picks from them directly. | |
| */ | |
| type BrainMissionWire = { | |
| chapter_id: string; | |
| chapter_title: string; | |
| mission_id: string; | |
| mission_title: string; | |
| completed: boolean; | |
| importance: number; | |
| }; | |
| type BrainPlanWire = { | |
| restatement: string; | |
| focus_reason: string; | |
| recommended_chapter_id: string | null; | |
| recommended_mission_id: string | null; | |
| study_tip: string; | |
| grounded: boolean; | |
| is_fallback: boolean; | |
| }; | |
| type BrainRequestWire = { | |
| profile: { | |
| class_level: string; | |
| board: string; | |
| subject: string; | |
| goal: string | null; | |
| daily_minutes: number | null; | |
| exam_date: string | null; | |
| weak_topics: string[]; | |
| low_mastery_topics: string[]; | |
| recent_mistakes: string[]; | |
| unfinished_mission_keys: string[]; | |
| revision_due_mission_keys: string[]; | |
| days_until_exam: number | null; | |
| language: string; | |
| }; | |
| candidate_missions: BrainMissionWire[]; | |
| }; | |
| function toWireMissions(missions: TuitionBrainMission[]): BrainMissionWire[] { | |
| return missions.slice(0, 80).map((m) => ({ | |
| chapter_id: m.chapterId, | |
| chapter_title: m.chapterTitle, | |
| mission_id: m.missionId, | |
| mission_title: m.missionTitle, | |
| completed: m.completed, | |
| importance: m.importance ?? 1, | |
| })); | |
| } | |
| function fromWirePlan(wire: BrainPlanWire): TuitionBrainPlan { | |
| return { | |
| restatement: wire.restatement, | |
| focusReason: wire.focus_reason, | |
| recommendedChapterId: wire.recommended_chapter_id, | |
| recommendedMissionId: wire.recommended_mission_id, | |
| studyTip: wire.study_tip, | |
| grounded: wire.grounded, | |
| isFallback: wire.is_fallback, | |
| }; | |
| } | |
| /** | |
| * Best-effort remote call. Returns null on failure (backend down, offline, | |
| * over budget) so the caller falls back to the deterministic plan. | |
| * | |
| * No session gate: the /tuition-brain/plan endpoint serves anonymous beta | |
| * students too (bounded by the backend's daily provider budget), so the | |
| * login-less beta flow can still reach the real brain. | |
| */ | |
| export async function requestTuitionPlanRemote( | |
| profile: TuitionBrainProfileInput, | |
| missions: TuitionBrainMission[], | |
| ): Promise<TuitionBrainPlan | null> { | |
| if (shouldUseDemoData()) return null; | |
| try { | |
| const body: BrainRequestWire = { | |
| profile: { | |
| class_level: profile.classLevel, | |
| board: profile.board, | |
| subject: profile.subject, | |
| goal: profile.goal ?? null, | |
| daily_minutes: profile.dailyMinutes ?? null, | |
| exam_date: profile.examDate ?? null, | |
| weak_topics: profile.weakTopics, | |
| low_mastery_topics: profile.lowMasteryTopics ?? [], | |
| recent_mistakes: profile.recentMistakes ?? [], | |
| unfinished_mission_keys: profile.unfinishedMissionKeys ?? [], | |
| revision_due_mission_keys: profile.revisionDueMissionKeys ?? [], | |
| days_until_exam: profile.daysUntilExam ?? null, | |
| language: profile.language ?? "English", | |
| }, | |
| candidate_missions: toWireMissions(missions), | |
| }; | |
| const wire = await apiPost<BrainPlanWire, BrainRequestWire>("/tuition-brain/plan", body); | |
| // Guard: never trust a recommendation that isn't one of the missions we sent. | |
| if (wire.recommended_mission_id) { | |
| const isReal = missions.some( | |
| (m) => m.chapterId === wire.recommended_chapter_id && m.missionId === wire.recommended_mission_id, | |
| ); | |
| if (!isReal) return null; | |
| } | |
| return fromWirePlan(wire); | |
| } catch { | |
| return null; | |
| } | |
| } | |
| /** The UI entry point: real brain when possible, deterministic otherwise. */ | |
| export async function planTodaysTuition( | |
| profile: TuitionBrainProfileInput, | |
| missions: TuitionBrainMission[], | |
| ): Promise<TuitionBrainPlan> { | |
| const remote = await requestTuitionPlanRemote(profile, missions); | |
| return remote ?? deterministicTuitionPlan(profile, missions); | |
| } | |
| export type { TuitionBrainMission, TuitionBrainPlan, TuitionBrainProfileInput }; | |