Download src/lib/api/learning-state.ts from asnannp/DocDoeAI: direct link, hf CLI and curl.
- Browser
- Download file 21.5 kB
-
https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/api/learning-state.ts
- Command line
-
hf download hf://spaces/asnannp/DocDoeAI/src/lib/api/learning-state.ts
-
curl -L -o learning-state.ts https://huggingface.co/spaces/asnannp/DocDoeAI/resolve/main/src/lib/api/learning-state.ts
21.5 kB
| import { apiDelete, apiFetch } from "@/lib/api/client"; | |
| export type LearningTask = { | |
| id: string; | |
| study_plan_id: string; | |
| daily_plan_id?: string | null; | |
| logical_key?: string | null; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| subject: string | null; | |
| chapter: string | null; | |
| task_type: "lesson" | "revision" | "practice" | "test" | "note" | "upload" | "repair"; | |
| title: string; | |
| status: "pending" | "completed" | "missed" | "skipped"; | |
| scheduled_for: string; | |
| duration_minutes: number; | |
| priority: number; | |
| href: string | null; | |
| mission_id: string | null; | |
| rescheduled_from_id: string | null; | |
| completed_at: string | null; | |
| /** Why DocDoe chose this task — from the adaptive planner, never invented. */ | |
| reason?: string | null; | |
| source?: string | null; | |
| repair_item_id?: string | null; | |
| revision_concept_key?: string | null; | |
| reason_codes?: string[]; | |
| reason_facts?: Record<string, unknown>; | |
| target?: Record<string, unknown>; | |
| }; | |
| export type LearningRevisionItem = { | |
| id: string; | |
| client_item_id: string; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| subject: string | null; | |
| chapter: string | null; | |
| mission_id: string | null; | |
| topic_key: string | null; | |
| title: string; | |
| source_kind: string; | |
| source_ref: string | null; | |
| status: "pending" | "due" | "completed" | "dismissed"; | |
| due_at: string | null; | |
| completed_at: string | null; | |
| created_at: string; | |
| }; | |
| export type TodayPlan = { | |
| id: string; | |
| date: string; | |
| generation: number; | |
| version: string; | |
| status: string; | |
| available_minutes: number; | |
| planned_minutes: number; | |
| completed_minutes: number; | |
| tasks: LearningTask[]; | |
| generated_at: string; | |
| regenerated_at: string | null; | |
| }; | |
| export type MasteryState = | |
| | "not_started" | |
| | "introduced" | |
| | "developing" | |
| | "needs_repair" | |
| | "secure" | |
| | "revision_due" | |
| | "at_risk"; | |
| export type RepairItem = { | |
| id: string; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| concept_key: string; | |
| concept_label: string; | |
| mission_id: string | null; | |
| error_category: string; | |
| diagnosis: string; | |
| recommended_activity: string; | |
| activity_prompt: string; | |
| priority: number; | |
| estimated_minutes: number; | |
| status: "open" | "resolved" | "escalated"; | |
| support_level: number; | |
| failed_attempts: number; | |
| retry_result: string | null; | |
| mastery_recovered: boolean; | |
| created_at: string; | |
| resolved_at: string | null; | |
| }; | |
| export type AcademicTopic = { | |
| topic_key: string; | |
| label: string; | |
| state: MasteryState; | |
| score: number; | |
| confidence: number; | |
| subject: string | null; | |
| chapter: string | null; | |
| next_review_at: string | null; | |
| }; | |
| export type AcademicTuitionSession = { | |
| class_session_id: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| chapter_catalog_id: string | null; | |
| mission_id: string | null; | |
| topic: string | null; | |
| current_step_id: string; | |
| current_step_label: string; | |
| progress_percent: number; | |
| updated_at: string; | |
| href: string; | |
| }; | |
| export type AcademicCourse = { | |
| roadmap_id: string; | |
| topic: string; | |
| current_module: string | null; | |
| completed_topics: number; | |
| total_topics: number; | |
| progress_percent: number; | |
| updated_at: string; | |
| href: string; | |
| }; | |
| export type AcademicChatContext = { | |
| session_id: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| topic: string | null; | |
| source_id: string | null; | |
| source_ref: string | null; | |
| source_label: string | null; | |
| activity_type: string | null; | |
| question_id: string | null; | |
| question_label: string | null; | |
| return_href: string | null; | |
| updated_at: string; | |
| href: string; | |
| }; | |
| export type NextAcademicAction = { | |
| type: | |
| | "COMPLETE_SETUP" | |
| | "CONTINUE_LESSON" | |
| | "CORRECT_MISTAKE" | |
| | "PRACTISE_TOPIC" | |
| | "REVISE_TOPIC" | |
| | "START_TODAYS_PLAN" | |
| | "CONTINUE_COURSE" | |
| | "REVIEW_PYQ" | |
| | "ASK_FOLLOWUP" | |
| | "START_NEXT_LESSON"; | |
| title: string; | |
| reason: string; | |
| action_label: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| topic: string | null; | |
| href: string; | |
| resume_payload: Record<string, unknown>; | |
| urgency: "low" | "normal" | "high" | "urgent"; | |
| estimated_minutes: number; | |
| }; | |
| export type LearningOnboardingResponse = { | |
| profile_id: string; | |
| plan_id: string; | |
| available_study_days: number; | |
| first_task_id: string; | |
| first_task_href: string; | |
| tasks: LearningTask[]; | |
| today_plan?: TodayPlan | null; | |
| }; | |
| export type LearningStateSummary = { | |
| profile: null | { | |
| id: string; | |
| class_level: string; | |
| board: string; | |
| exam_date: string | null; | |
| goal: string; | |
| daily_minutes: number; | |
| preferred_time: string | null; | |
| available_study_days: number; | |
| current_subject_id: string | null; | |
| current_chapter_id: string | null; | |
| current_mission_id: string | null; | |
| onboarding_completed: boolean; | |
| preferences?: Record<string, unknown>; | |
| }; | |
| subjects: Array<{ | |
| id: string; | |
| name: string; | |
| board: string; | |
| class_level: string; | |
| priority: number; | |
| status: string; | |
| }>; | |
| chapters: Array<{ | |
| id: string; | |
| subject_id: string; | |
| catalog_id: string; | |
| title: string; | |
| order_index: number; | |
| importance: number; | |
| estimated_minutes: number; | |
| status: string; | |
| curated: boolean; | |
| }>; | |
| tasks: LearningTask[]; | |
| today_plan?: TodayPlan | null; | |
| lesson_progress: Array<{ | |
| id: string; | |
| chapter_id: string; | |
| chapter_catalog_id: string; | |
| chapter: string; | |
| subject_id: string; | |
| mission_id: string; | |
| status: "not_started" | "in_progress" | "completed"; | |
| progress_percent: number; | |
| current_step: number; | |
| completed_at: string | null; | |
| last_seen_at: string; | |
| }>; | |
| mastery: Array<{ | |
| id: string; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| topic_key: string; | |
| topic_label: string; | |
| score: number; | |
| confidence: number; | |
| attempts_count: number; | |
| last_result: string | null; | |
| next_review_at: string | null; | |
| state: MasteryState; | |
| consecutive_success: number; | |
| error_categories: Record<string, number>; | |
| last_evidence_at: string | null; | |
| }>; | |
| quiz_attempts: Array<{ | |
| id: string; | |
| quiz_id: string | null; | |
| daily_task_id: string | null; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| title: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| score: number; | |
| max_score: number; | |
| answers: Array<Record<string, unknown>>; | |
| corrections: Array<Record<string, unknown>>; | |
| missing_keywords: string[]; | |
| misconceptions: string[]; | |
| completed_at: string; | |
| }>; | |
| resources: Array<{ | |
| id: string; | |
| subject_id: string | null; | |
| chapter_id: string | null; | |
| subject: string | null; | |
| chapter: string | null; | |
| source_id: string | null; | |
| resource_type: string; | |
| title: string; | |
| status: string; | |
| storage_url: string | null; | |
| resource_data: Record<string, unknown>; | |
| created_at: string; | |
| }>; | |
| repair_items: RepairItem[]; | |
| revision_items?: LearningRevisionItem[]; | |
| completed_lessons: number; | |
| completed_quiz_attempts: number; | |
| generated_resources: number; | |
| generated_notes: number; | |
| questions_asked: number; | |
| academic_state: { | |
| class_level: string | null; | |
| board: string | null; | |
| subjects: string[]; | |
| exam_date: string | null; | |
| days_to_exam: number | null; | |
| current_subject: string | null; | |
| current_chapter: string | null; | |
| current_topic: string | null; | |
| last_meaningful_activity: null | { | |
| kind: string; | |
| title: string; | |
| detail: string | null; | |
| occurred_at: string; | |
| href: string; | |
| }; | |
| unfinished_lesson: AcademicTuitionSession | null; | |
| recent_mistakes: Array<{ | |
| repair_id: string; | |
| topic_key: string; | |
| label: string; | |
| error_category: string; | |
| diagnosis: string; | |
| activity_prompt: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| mission_id: string | null; | |
| occurred_at: string; | |
| }>; | |
| weak_topics: AcademicTopic[]; | |
| mastered_topics: AcademicTopic[]; | |
| revision_due: AcademicTopic[]; | |
| active_course: AcademicCourse | null; | |
| active_tuition_session: AcademicTuitionSession | null; | |
| recent_study_chat_context: AcademicChatContext | null; | |
| suggested_notifications: Array<{ | |
| id: string; | |
| title: string; | |
| body: string; | |
| kind: "info" | "success" | "warning" | "action"; | |
| href: string; | |
| created_at: string; | |
| }>; | |
| }; | |
| next_action: NextAcademicAction; | |
| }; | |
| export type StudentBrainSkillReadiness = { | |
| skill: | |
| | "concepts" | |
| | "numericals" | |
| | "derivations" | |
| | "pyqs" | |
| | "formulas" | |
| | "graphs" | |
| | "answer_writing" | |
| | "diagrams"; | |
| label: string; | |
| score: number; | |
| attempts: number; | |
| correct_attempts: number; | |
| evidence_level: "early" | "building" | "reliable"; | |
| last_attempted_at: string; | |
| }; | |
| export type StudentBrainMasteryItem = { | |
| topic_key: string; | |
| topic: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| score: number; | |
| confidence: number; | |
| state: string; | |
| attempts: number; | |
| last_result: string | null; | |
| last_evidence_at: string | null; | |
| next_review_at: string | null; | |
| skill_readiness: StudentBrainSkillReadiness[]; | |
| }; | |
| export type StudentBrainContext = { | |
| schema_version: "student-brain-v1"; | |
| generated_at: string; | |
| focus: { | |
| subject: string | null; | |
| chapter: string | null; | |
| topic: string | null; | |
| available_minutes_today: number | null; | |
| }; | |
| profile: { | |
| class_level: string | null; | |
| board: string | null; | |
| subjects: string[]; | |
| preferred_language: string | null; | |
| learning_preferences: Record<string, string | number | boolean>; | |
| }; | |
| exam: { | |
| name: string | null; | |
| date: string | null; | |
| days_remaining: number | null; | |
| syllabus: string | null; | |
| target_marks: number | null; | |
| total_marks: number | null; | |
| }; | |
| goal: { | |
| description: string | null; | |
| daily_minutes: number | null; | |
| available_minutes_today: number | null; | |
| }; | |
| weakest_topics: StudentBrainMasteryItem[]; | |
| retention_due: Array<{ | |
| topic_key: string; | |
| topic: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| due_at: string; | |
| state: string; | |
| }>; | |
| recent_mistakes: Array<{ | |
| checkpoint_ref: string; | |
| topic_key: string; | |
| topic: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| error_type: string; | |
| error_type_source: "server_classification" | "student_reflection"; | |
| internal_error_category: string; | |
| diagnosis: string | null; | |
| question_type: string | null; | |
| difficulty: string | null; | |
| time_spent_seconds: number | null; | |
| skill_tags: string[]; | |
| attempt_index: number; | |
| occurred_at: string; | |
| }>; | |
| recent_question_attempts: Array<{ | |
| checkpoint_ref: string; | |
| topic_key: string; | |
| topic: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| kind: string; | |
| correct: boolean; | |
| hint_used: boolean; | |
| difficulty: string | null; | |
| time_spent_seconds: number | null; | |
| skill_tags: string[]; | |
| attempt_index: number; | |
| occurred_at: string; | |
| }>; | |
| recent_performance: Array<{ | |
| assessment_ref: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| score: number; | |
| max_score: number; | |
| accuracy_percent: number; | |
| completed_at: string; | |
| }>; | |
| study_history: Array<{ | |
| date: string; | |
| planned_minutes: number; | |
| completed_minutes: number; | |
| completion_percent: number; | |
| subjects: string[]; | |
| }>; | |
| pyq_importance: Array<{ | |
| topic: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| question_count: number; | |
| total_marks: number; | |
| years: number[]; | |
| priority: "high" | "medium" | "normal"; | |
| }>; | |
| resources: Array<{ | |
| kind: "source" | "generated" | "curated_chapter"; | |
| title: string; | |
| resource_type: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| status: string; | |
| }>; | |
| next_action: null | { | |
| task_ref: string; | |
| title: string; | |
| task_type: string; | |
| subject: string | null; | |
| chapter: string | null; | |
| topic: string | null; | |
| chapter_catalog_id: string | null; | |
| mission_id: string | null; | |
| duration_minutes: number; | |
| reason: string | null; | |
| reason_codes: string[]; | |
| href: string | null; | |
| }; | |
| }; | |
| export function saveLearningOnboarding(payload: { | |
| class_level: string; | |
| board: string; | |
| subjects: string[]; | |
| /** Omit or null when the student does not know their exam date yet. */ | |
| exam_date: string | null; | |
| goal: string; | |
| daily_minutes: number; | |
| preferred_time: string | null; | |
| preferences: Record<string, unknown>; | |
| }): Promise<LearningOnboardingResponse> { | |
| return apiFetch<LearningOnboardingResponse>("/learning-state/onboarding", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export function confirmLearningOnboarding(planId: string): Promise<{ | |
| profile_id: string; | |
| plan_id: string; | |
| confirmed_at: string; | |
| onboarding_completed: boolean; | |
| }> { | |
| return apiFetch("/learning-state/onboarding/confirm", { | |
| method: "POST", | |
| body: JSON.stringify({ plan_id: planId }), | |
| }); | |
| } | |
| export async function getLearningState(): Promise<LearningStateSummary> { | |
| const state = await apiFetch<LearningStateSummary>("/learning-state/me"); | |
| // A returning student may open Home before Tuition on a new study day. | |
| // Ensure is account-scoped and idempotent; an existing plan stays unchanged. | |
| if (state.profile?.onboarding_completed && !state.today_plan) { | |
| await ensureTodayPlan(); | |
| return apiFetch<LearningStateSummary>("/learning-state/me"); | |
| } | |
| return state; | |
| } | |
| export function getStudentBrainContext(): Promise<StudentBrainContext> { | |
| return apiFetch<StudentBrainContext>("/learning-state/brain-context"); | |
| } | |
| export function deleteLearningResource(resourceId: string): Promise<void> { | |
| return apiDelete<void>( | |
| `/learning-state/resources/${encodeURIComponent(resourceId)}`, | |
| ); | |
| } | |
| export function setLearningTaskStatus( | |
| taskId: string, | |
| status: "pending" | "completed" | "missed" | "skipped", | |
| reason?: string, | |
| ): Promise<{ | |
| task: LearningTask; | |
| recalculated_tasks: LearningTask[]; | |
| message: string; | |
| }> { | |
| return apiFetch(`/learning-state/tasks/${encodeURIComponent(taskId)}/status`, { | |
| method: "PATCH", | |
| body: JSON.stringify({ status, reason: reason ?? null }), | |
| }); | |
| } | |
| export type LessonProgressResult = { | |
| progress_id: string; | |
| status: "in_progress" | "completed"; | |
| progress_percent: number; | |
| completed_lessons: number; | |
| completed_task_id: string | null; | |
| message: string; | |
| }; | |
| export function saveLearningLessonProgress(payload: { | |
| chapter_catalog_id: string; | |
| mission_id: string; | |
| title: string; | |
| daily_task_id?: string | null; | |
| duration_minutes?: number; | |
| current_step?: number; | |
| progress_percent?: number; | |
| status?: "in_progress" | "completed"; | |
| }): Promise<LessonProgressResult> { | |
| return apiFetch<LessonProgressResult>("/learning-state/lessons/progress", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export type RevisionItemSaveResult = { | |
| item: LearningRevisionItem; | |
| replayed: boolean; | |
| message: string; | |
| }; | |
| export function saveLearningRevisionItem(payload: { | |
| client_item_id: string; | |
| title: string; | |
| chapter_catalog_id: string; | |
| mission_id?: string | null; | |
| topic_key?: string | null; | |
| source_kind: "manual_save" | "lesson_recap"; | |
| source_ref?: string | null; | |
| due_in_days?: number; | |
| }): Promise<RevisionItemSaveResult> { | |
| return apiFetch<RevisionItemSaveResult>("/learning-state/revision-items", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export type AssessmentConsequence = { | |
| attempt_id: string; | |
| mastery_before: number; | |
| mastery_after: number; | |
| revision_task: LearningTask | null; | |
| next_recommended_task_id: string | null; | |
| message: string; | |
| }; | |
| export type LearningAssessmentInput = { | |
| client_attempt_id?: string | null; | |
| title: string; | |
| topic_key: string; | |
| topic_label: string; | |
| score: number; | |
| max_score: number; | |
| subject_id?: string | null; | |
| chapter_id?: string | null; | |
| daily_task_id?: string | null; | |
| quiz_id?: string | null; | |
| mission_id?: string | null; | |
| answers?: Array<Record<string, unknown>>; | |
| corrections?: Array<Record<string, unknown>>; | |
| missing_keywords?: string[]; | |
| misconceptions?: string[]; | |
| mastery_already_recorded?: boolean; | |
| }; | |
| export function recordLearningAssessment( | |
| payload: LearningAssessmentInput, | |
| ): Promise<AssessmentConsequence> { | |
| return apiFetch<AssessmentConsequence>("/learning-state/assessments", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export function adjustLearningPlan(payload: { | |
| target_subject: string; | |
| replace_subject?: string | null; | |
| reason: string; | |
| chapters: string[]; | |
| }): Promise<{ | |
| tasks: LearningTask[]; | |
| unavailable_chapters: string[]; | |
| rescheduled_tasks: number; | |
| message: string; | |
| }> { | |
| return apiFetch("/learning-state/plan-adjustments", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export type CheckpointConsequence = { | |
| checkpoint_id: string; | |
| correct: boolean; | |
| replayed: boolean; | |
| error_category: string | null; | |
| diagnosis: string | null; | |
| recommended_activity: string | null; | |
| activity_prompt: string | null; | |
| repair_status: "created" | "escalated" | "resolved" | "none"; | |
| repair_item: RepairItem | null; | |
| mastery: { | |
| before_score: number; | |
| after_score: number; | |
| before_state: MasteryState; | |
| after_state: MasteryState; | |
| confidence: number; | |
| consecutive_success: number; | |
| next_review_at: string | null; | |
| }; | |
| message: string; | |
| }; | |
| export type CheckpointKind = | |
| | "recall_correct" | |
| | "checkpoint_mcq" | |
| | "checkpoint_short" | |
| | "checkpoint_numerical" | |
| | "checkpoint_diagram" | |
| | "board_answer" | |
| | "transfer_check" | |
| | "revision_recall"; | |
| export function recordLearningCheckpoint(payload: { | |
| client_event_id: string; | |
| concept_key: string; | |
| concept_label: string; | |
| chapter_catalog_id: string; | |
| mission_id: string; | |
| kind: CheckpointKind; | |
| question_id: string; | |
| question_type: "mcq" | "short" | "numerical" | "diagram" | "board"; | |
| prompt?: string; | |
| student_answer: string; | |
| correct_answer?: string; | |
| client_correct?: boolean; | |
| attempt_index?: number; | |
| hint_used?: boolean; | |
| expected_keywords?: string[]; | |
| time_spent_seconds?: number | null; | |
| difficulty?: "easy" | "medium" | "hard" | null; | |
| skill_tags?: string[]; | |
| error_reflection?: { | |
| error_type: "careless_error" | "misread_question" | "time_pressure"; | |
| note?: string | null; | |
| } | null; | |
| subject_id?: string | null; | |
| chapter_id?: string | null; | |
| daily_task_id?: string | null; | |
| }): Promise<CheckpointConsequence> { | |
| return apiFetch<CheckpointConsequence>("/learning-state/checkpoints", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export type PlanTodayResult = { | |
| plan_id: string; | |
| date: string; | |
| generation: number; | |
| version: string; | |
| status: string; | |
| daily_minutes: number; | |
| planned_minutes: number; | |
| tasks: LearningTask[]; | |
| created_task_ids: string[]; | |
| cancelled_task_ids: string[]; | |
| message: string; | |
| }; | |
| export function reconcileTodaysPlan(): Promise<PlanTodayResult> { | |
| return ensureTodayPlan(); | |
| } | |
| export function getTodayPlan(): Promise<PlanTodayResult> { | |
| return apiFetch<PlanTodayResult>("/learning-state/today-plan"); | |
| } | |
| export function ensureTodayPlan(): Promise<PlanTodayResult> { | |
| return apiFetch<PlanTodayResult>("/learning-state/today-plan/ensure", { | |
| method: "POST", | |
| body: JSON.stringify({}), | |
| }); | |
| } | |
| export function replanToday(payload: { | |
| idempotency_key: string; | |
| reason: | |
| | "explicit_replan" | |
| | "daily_minutes_changed" | |
| | "exam_date_changed" | |
| | "content_changed" | |
| | "meaningful_evidence"; | |
| }): Promise<PlanTodayResult> { | |
| return apiFetch<PlanTodayResult>("/learning-state/today-plan/replan", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |
| export type RevisionRecallQuestion = { | |
| question_id: string; | |
| prompt: string; | |
| question_type: "mcq" | "short" | "numerical" | "diagram" | "board"; | |
| options: string[]; | |
| basis: "saved_mistake" | "linked_lesson"; | |
| reason: string; | |
| failure_count: number; | |
| }; | |
| export function getRevisionRecallQuestion(revisionItemId: string): Promise<RevisionRecallQuestion> { | |
| return apiFetch(`/learning-state/revision-items/${encodeURIComponent(revisionItemId)}/question`); | |
| } | |
| export function recordRevisionResult( | |
| revisionItemId: string, | |
| payload: { client_event_id: string; response: string; question_id?: string }, | |
| ): Promise<{ | |
| item: LearningRevisionItem; | |
| evaluation: { | |
| evaluation_version: string; | |
| question_id: string; | |
| correct: boolean; | |
| awarded_marks: number; | |
| max_marks: number; | |
| mistake_categories: string[]; | |
| feedback: string; | |
| }; | |
| replayed: boolean; | |
| message: string; | |
| }> { | |
| return apiFetch( | |
| `/learning-state/revision-items/${encodeURIComponent(revisionItemId)}/result`, | |
| { method: "POST", body: JSON.stringify(payload) }, | |
| ); | |
| } | |
| export function listOpenStudyFollowUps(): Promise<Record<string, unknown>[]> { | |
| return apiFetch("/learning-state/study-followups"); | |
| } | |
| export function createStudyFollowUp(payload: { | |
| client_followup_id: string; | |
| chat_session_id: string; | |
| source_message_id?: string | null; | |
| kind: "practice" | "pyq_review" | "clarification" | "source_review"; | |
| title: string; | |
| subject?: string | null; | |
| chapter?: string | null; | |
| topic?: string | null; | |
| due_at?: string | null; | |
| estimated_minutes?: number; | |
| source_ref?: string | null; | |
| target?: Record<string, unknown>; | |
| }): Promise<Record<string, unknown>> { | |
| return apiFetch("/learning-state/study-followups", { | |
| method: "POST", | |
| body: JSON.stringify(payload), | |
| }); | |
| } | |