Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 74 additions & 35 deletions app/classrooms/[classroomId]/augment/AugmentNotes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import { Input } from "@shared/components/ui/input";
import { FileText, Upload } from "lucide-react";
import { toast } from "sonner";

import pdfToText from "react-pdftotext";

import NotesViewer from "./NotesViewer";

export default function AugmentComponent({
// TODO: remove for classroomId and setIsProcessing once we actually implement augments
// eslint-disable-next-line @typescript-eslint/no-unused-vars
classroomId,
}: {
classroomId: string;
Expand All @@ -19,15 +21,22 @@ export default function AugmentComponent({
const [file, setFile] = useState<File | null>(null);
const inputFile = useRef<HTMLInputElement>(null);

const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = e.target.files?.[0];
const [notesContent, setNotesContent] = useState<string[]>([]);

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const selectedFile = event.target.files?.[0];
if (!selectedFile) return;

// Double-check that file passed by user is markdown or pdf
const fileExtension = selectedFile.name.split(".").at(-1)?.toLowerCase();
if (fileExtension !== "pdf" && fileExtension !== "md") {
if (
fileExtension !== "pdf" &&
fileExtension !== "md" &&
fileExtension !== "txt"
) {
toast.error("Invalid file format", {
description: "Please upload a Markdown (.md) or PDF (.pdf) file",
description:
"Please upload a Markdown (.md), PDF (.pdf), or TXT (.txt) file",
});
return;
}
Expand All @@ -36,48 +45,78 @@ export default function AugmentComponent({
};

const handleUpload = async () => {
// TODO
if (file != null) {
const fileExtension = file.name.split(".").at(-1)?.toLowerCase();

let splitByLines: string[] = [];

// if txt or md
if (fileExtension == "txt" || fileExtension == "md") {
const endText: string = await file.text();
const cleanedText = endText.replaceAll("\r", "");
splitByLines = cleanedText.split("\n");

// if pdf
} else if (fileExtension == "pdf") {
splitByLines = await pdfToText(file)
.then((text) => {
return text.split(" ");
})
.catch((error) => {
console.error("Failed to extract text from pdf", error);
return [""];
});
}

setNotesContent(splitByLines);
}
};

return (
<div className="flex flex-col items-center justify-center gap-4 p-8">
<h1 className="text-3xl font-bold">Augment Notes</h1>

<p className="text-muted-foreground">
Upload your notes to get AI-powered enhancements and improvements
</p>
<div className="flex items-center gap-2">
<Input
type="file"
onChange={handleFileChange}
ref={inputFile}
className="hidden"
accept=".md,.pdf"
/>
<Button
variant="outline"
size="lg"
className="cursor-pointer"
disabled={isProcessing}
onClick={() => inputFile.current?.click()}
>
{isProcessing ? (
<Upload className="mr-2 h-4 w-4 animate-spin" />
) : (
<FileText className="mr-2 h-4 w-4" />
)}
Upload Notes
</Button>
{file && (

{notesContent.length == 0 ? (
<div className="flex items-center gap-2">
<Input
type="file"
onChange={handleFileChange}
ref={inputFile}
className="hidden"
accept=".md,.pdf,.txt"
/>
<Button
variant="default"
variant="outline"
size="lg"
onClick={handleUpload}
className="cursor-pointer"
disabled={isProcessing}
onClick={() => inputFile.current?.click()}
>
Process Notes
{isProcessing ? (
<Upload className="mr-2 h-4 w-4 animate-spin" />
) : (
<FileText className="mr-2 h-4 w-4" />
)}
Upload Notes
</Button>
)}
</div>
{file && (
<Button
variant="default"
size="lg"
onClick={handleUpload}
disabled={isProcessing}
>
Process Notes
</Button>
)}
</div>
) : (
<NotesViewer notesContent={notesContent} classroomId={classroomId} />
)}
</div>
);
}
152 changes: 152 additions & 0 deletions app/classrooms/[classroomId]/augment/AugumentActions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
"use server";

import { createClient } from "@shared/utils/supabase/server";
import {
ChatClientWithSession,
createChatClient,
sendMessage,
} from "@shared/lib/ragflow/chat/chat-client";

import { getUserAndClassroomData } from "@shared/lib/userContext/contextFetcher";

import { AugmentConfigTemplate } from "@shared/lib/ragflow/chat/chat-configs";

// import { revalidatePath } from "next/cache";
// import {
// ChatClientWithSession,
// createChatClient,
// deleteSession,
// sendMessage,
// } from "@shared/lib/ragflow/chat/chat-client";
// import { createDatasetClient } from "@shared/lib/ragflow/dataset-client";

export async function createNotesClient(classroomId: string) {
//create a session for that set of notes

//first get the dataset ID
const supabase = await createClient();
const { data, error } = await supabase
.from("Classrooms")
.select()
.eq("id", Number(classroomId))
.single();

if (error) {
console.error("Error fetching classroom data:", error);
throw new Error("Error fetching classroom data");
}

const datasetId: string = data?.ragflow_dataset_id || "";

// console.log("datasetId", datasetId);
// console.log("classroomId", classroomId);

// const params = { dataset_ids: [datasetId], name: datasetId };

// const res = await fetch(`${process.env.RAGFLOW_API_URL}/v1/chats`, {
// method: "POST", // or 'PUT'
// headers: {
// Authorization: `Bearer ${process.env.RAGFLOW_API_KEY}`,
// "Content-Type": "application/json",
// },
// body: JSON.stringify(params),
// });

// console.log("res", res);

const userAndClassData = await getUserAndClassroomData();

const resClient = await createChatClient(
{
...AugmentConfigTemplate,
associatedClassroomName: "Classroom",
primaryKeyValuesAssistant: [{ key: "id", value: classroomId }],
primaryKeyValuesSession: [
{ key: "classroom_id", value: classroomId },
{ key: "user_id", value: userAndClassData?.userData.id },
],
datasets: [datasetId],
}
// classroomInfo.chat_assistant_id
);

if (!resClient.client) {
console.error("Client is null");
throw new Error("Invalid chat client instance");
}

if (!("sessionId" in resClient.client)) {
console.error("Client not of correct type");
throw new Error("Invalid chat client instance: Missing sessionId");
}
const chatClient: ChatClientWithSession = resClient.client;

// console.log(
// "chatClient",
// chatClient.clientConfig.modelSettings.promptSettings
// );

// console.log("datasetId", datasetId);

return chatClient;
}

export async function reviseNotesLine(
chatClient: ChatClientWithSession,
passedLine: string
) {
const passMessage: string = `Below is a line of text notes taken by a student.
Please augment these notes with you comments and revise them primarily based on accuracy.
Only change the parts you want to revise.
Do not rewrite the notes, simply make small edits for correctness.
Do not make style and diction revisions.
Return the notes exactly as given if they are already correct.
If they notes are accurate, leave them exactly as is.
Also, please do not use any markdown formatting.
${passedLine}
`;

const messageResponse = await sendMessage(chatClient, passMessage);

if (!messageResponse.ragflowCallSuccess) {
console.error("Error sending message");
throw new Error("Error sending message");
}

const revisedNotes: string = messageResponse.response
.replace("Notes: ", "")
.replaceAll("##0$$\n", "");
// console.log("revisedNotes", revisedNotes);

const reason: string = await getAugmentReason(
chatClient,
passedLine,
revisedNotes
);

return [revisedNotes, reason];
}

export async function getAugmentReason(
chatClient: ChatClientWithSession,
originalNotes: string,
augmentedNotes: string
) {
const passMessage: string = `In a previous message, you augmented the following notes:
"${originalNotes}"
to
"${augmentedNotes}"

Can you explain why you made this change?
Please do not use any markdown formatting in your response.
`;

const messageResponse = await sendMessage(chatClient, passMessage);

if (!messageResponse.ragflowCallSuccess) {
console.error("Error getting explanation");
throw new Error("Error getting explanation");
}

return messageResponse.response;
}
Loading