mirror of
https://github.com/jcreek/AutomatedAssessmentFeedbackAgent.git
synced 2026-07-12 18:43:49 +00:00
feat(*): Require adding a description of the task when uploading
This commit is contained in:
+13
-8
@@ -5,8 +5,16 @@ const OPENAI_ENDPOINT = process.env['AZURE_OPENAI_ENDPOINT'];
|
|||||||
const OPENAI_DEPLOYMENT = process.env['AZURE_OPENAI_DEPLOYMENT'] || 'gpt-4';
|
const OPENAI_DEPLOYMENT = process.env['AZURE_OPENAI_DEPLOYMENT'] || 'gpt-4';
|
||||||
|
|
||||||
// Prompt template for grading and feedback
|
// Prompt template for grading and feedback
|
||||||
export function buildGradingPrompt(submission: string) {
|
export function buildGradingPrompt(submission: string, task: string) {
|
||||||
return `You are an expert secondary school teacher and AI assessment agent. Assess the following student submission using the following steps:
|
return `You are an expert secondary school teacher and AI assessment agent. Assess the following student submission in the context of the assignment/task provided.
|
||||||
|
|
||||||
|
TASK/ASSIGNMENT:
|
||||||
|
${task}
|
||||||
|
|
||||||
|
STUDENT SUBMISSION:
|
||||||
|
${submission}
|
||||||
|
|
||||||
|
Follow these steps:
|
||||||
1. Grade the work out of 10, using clear, objective criteria.
|
1. Grade the work out of 10, using clear, objective criteria.
|
||||||
2. Identify specific strengths, referencing the success criteria.
|
2. Identify specific strengths, referencing the success criteria.
|
||||||
3. Identify misconceptions or areas for improvement, using formative assessment language.
|
3. Identify misconceptions or areas for improvement, using formative assessment language.
|
||||||
@@ -18,9 +26,6 @@ export function buildGradingPrompt(submission: string) {
|
|||||||
6. Suggest to the teacher one way to support this student in the next lesson.
|
6. Suggest to the teacher one way to support this student in the next lesson.
|
||||||
7. Show your reasoning step by step (chain-of-thought).
|
7. Show your reasoning step by step (chain-of-thought).
|
||||||
|
|
||||||
STUDENT SUBMISSION:
|
|
||||||
${submission}
|
|
||||||
|
|
||||||
RESPONSE FORMAT:
|
RESPONSE FORMAT:
|
||||||
Grade: <number>/10
|
Grade: <number>/10
|
||||||
Strengths: <text>
|
Strengths: <text>
|
||||||
@@ -31,12 +36,12 @@ Teacher Suggestion: <text>
|
|||||||
Reasoning: <step-by-step explanation>`;
|
Reasoning: <step-by-step explanation>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function gradeWithOpenAI(submission: string): Promise<OpenAIResponse> {
|
export async function gradeWithOpenAI(submission: string, task: string): Promise<OpenAIResponse> {
|
||||||
if (!OPENAI_API_KEY || !OPENAI_ENDPOINT) {
|
if (!OPENAI_API_KEY || !OPENAI_ENDPOINT) {
|
||||||
// Fallback for local/demo mode
|
// Fallback for local/demo mode
|
||||||
return {
|
return {
|
||||||
grade: '7/10',
|
grade: '7/10',
|
||||||
strengths: 'Clear argument and good evidence.',
|
strengths: 'Clear argument and good evidence (in response to the task: ' + task + ").",
|
||||||
areas_for_improvement: 'Needs deeper analysis and more examples.',
|
areas_for_improvement: 'Needs deeper analysis and more examples.',
|
||||||
individualized_activity: 'Write a paragraph expanding on your main point and provide two additional examples to support your argument.',
|
individualized_activity: 'Write a paragraph expanding on your main point and provide two additional examples to support your argument.',
|
||||||
reflection_question: 'What was the most challenging part of this assignment for you, and why?',
|
reflection_question: 'What was the most challenging part of this assignment for you, and why?',
|
||||||
@@ -45,7 +50,7 @@ export async function gradeWithOpenAI(submission: string): Promise<OpenAIRespons
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const prompt = buildGradingPrompt(submission);
|
const prompt = buildGradingPrompt(submission, task);
|
||||||
|
|
||||||
const res = await fetch(`${OPENAI_ENDPOINT}/openai/deployments/${OPENAI_DEPLOYMENT}/chat/completions?api-version=2024-02-15-preview`, {
|
const res = await fetch(`${OPENAI_ENDPOINT}/openai/deployments/${OPENAI_DEPLOYMENT}/chat/completions?api-version=2024-02-15-preview`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -7,8 +7,13 @@ export const POST: RequestHandler = async (event: RequestEvent) => {
|
|||||||
const data = await event.request.formData();
|
const data = await event.request.formData();
|
||||||
const file = data.get('file');
|
const file = data.get('file');
|
||||||
const text = data.get('text');
|
const text = data.get('text');
|
||||||
|
const task = data.get('task');
|
||||||
let submission = '';
|
let submission = '';
|
||||||
|
|
||||||
|
if (!task || typeof task !== 'string' || !task.trim()) {
|
||||||
|
return new Response(JSON.stringify({ error: 'Task/assignment description is required.' }), { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
if (file && typeof file === 'object' && 'arrayBuffer' in file && 'name' in file) {
|
if (file && typeof file === 'object' && 'arrayBuffer' in file && 'name' in file) {
|
||||||
// Only support .txt for now for hackathon speed
|
// Only support .txt for now for hackathon speed
|
||||||
const fileObj = file as File;
|
const fileObj = file as File;
|
||||||
@@ -24,11 +29,12 @@ export const POST: RequestHandler = async (event: RequestEvent) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call the AI grading agent
|
// Call the AI grading agent
|
||||||
const aiResult = await gradeWithOpenAI(submission);
|
const aiResult = await gradeWithOpenAI(submission, task.trim());
|
||||||
|
|
||||||
return new Response(JSON.stringify({
|
return new Response(JSON.stringify({
|
||||||
success: true,
|
success: true,
|
||||||
feedback: `Grade: ${aiResult.grade}\nStrengths: ${aiResult.strengths}\nWeaknesses: ${aiResult.weaknesses}\nIndividualized Activity: ${aiResult.individualized_activity}\nReasoning: ${aiResult.reasoning}`,
|
task: task.trim(),
|
||||||
|
feedback: `Grade: ${aiResult.grade}\nStrengths: ${aiResult.strengths}\nAreas for Improvement: ${aiResult.areas_for_improvement}\nIndividualized Activity: ${aiResult.individualized_activity}\nReasoning: ${aiResult.reasoning}`,
|
||||||
...aiResult
|
...aiResult
|
||||||
}), {
|
}), {
|
||||||
status: 200,
|
status: 200,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { onMount } from 'svelte';
|
|||||||
|
|
||||||
let file: File | null = null;
|
let file: File | null = null;
|
||||||
let textInput = '';
|
let textInput = '';
|
||||||
|
let taskDescription = '';
|
||||||
let submitting = false;
|
let submitting = false;
|
||||||
let successMsg = '';
|
let successMsg = '';
|
||||||
let errorMsg = '';
|
let errorMsg = '';
|
||||||
@@ -33,7 +34,13 @@ async function handleSubmit(event: Event) {
|
|||||||
successMsg = '';
|
successMsg = '';
|
||||||
errorMsg = '';
|
errorMsg = '';
|
||||||
|
|
||||||
|
if (!taskDescription.trim()) {
|
||||||
|
errorMsg = 'Please provide a description of the task or assignment.';
|
||||||
|
submitting = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
|
formData.append('task', taskDescription.trim());
|
||||||
if (file) {
|
if (file) {
|
||||||
formData.append('file', file);
|
formData.append('file', file);
|
||||||
} else if (textInput.trim().length > 0) {
|
} else if (textInput.trim().length > 0) {
|
||||||
@@ -52,6 +59,7 @@ async function handleSubmit(event: Event) {
|
|||||||
window.location.assign('/results');
|
window.location.assign('/results');
|
||||||
file = null;
|
file = null;
|
||||||
textInput = '';
|
textInput = '';
|
||||||
|
taskDescription = '';
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
errorMsg = result.error || 'An error occurred.';
|
errorMsg = result.error || 'An error occurred.';
|
||||||
@@ -68,43 +76,44 @@ async function handleSubmit(event: Event) {
|
|||||||
<section class="max-w-xl mx-auto py-12">
|
<section class="max-w-xl mx-auto py-12">
|
||||||
<h1 class="text-2xl font-bold mb-4">Upload Student Submissions</h1>
|
<h1 class="text-2xl font-bold mb-4">Upload Student Submissions</h1>
|
||||||
<p class="mb-6">Upload student assignments for instant AI-powered assessment and feedback.</p>
|
<p class="mb-6">Upload student assignments for instant AI-powered assessment and feedback.</p>
|
||||||
<form on:submit|preventDefault={handleSubmit} class="space-y-6">
|
<form on:submit|preventDefault={handleSubmit} class="bg-white shadow rounded p-6 max-w-xl mx-auto mt-12">
|
||||||
<div>
|
<h1 class="text-2xl font-bold mb-4">Upload Student Submission</h1>
|
||||||
<label for="file" class="block font-semibold mb-1">Upload a file (PDF, DOCX, TXT):</label>
|
<div class="mb-4">
|
||||||
|
<label class="block font-semibold mb-1" for="taskDescription">Task/Assignment Description <span class="text-red-500">*</span></label>
|
||||||
|
<textarea id="taskDescription" rows="3" class="w-full border p-2 rounded" bind:value={taskDescription} required placeholder="Describe the assignment, question, or task the student was responding to..."></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="block font-semibold mb-1" for="file">Upload a file (PDF, DOCX, TXT):</label>
|
||||||
<input
|
<input
|
||||||
id="file"
|
id="file"
|
||||||
type="file"
|
type="file"
|
||||||
accept=".pdf,.doc,.docx,.txt"
|
accept=".pdf,.doc,.docx,.txt"
|
||||||
class="block w-full border rounded p-2"
|
class="block w-full border p-2 rounded"
|
||||||
on:change={handleFileChange}
|
on:change={handleFileChange}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
/>
|
/>
|
||||||
|
<span class="text-gray-500 text-sm">Or paste the student's submission below.</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-center text-gray-500">or</div>
|
<div class="mb-4">
|
||||||
<div>
|
<label class="block font-semibold mb-1" for="textInput">Paste assignment text:</label>
|
||||||
<label for="textInput" class="block font-semibold mb-1">Paste assignment text:</label>
|
|
||||||
<textarea
|
<textarea
|
||||||
id="textInput"
|
id="textInput"
|
||||||
rows="6"
|
rows="5"
|
||||||
class="block w-full border rounded p-2"
|
class="w-full border p-2 rounded"
|
||||||
bind:value={textInput}
|
bind:value={textInput}
|
||||||
on:input={handleTextChange}
|
on:input={handleTextChange}
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
placeholder="Paste or type student answer here..."
|
placeholder="Paste or type student answer here..."
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 w-full mt-4" disabled={submitting || (!file && textInput.trim().length === 0)}>
|
||||||
|
{submitting ? 'Uploading...' : 'Submit'}
|
||||||
|
</button>
|
||||||
{#if errorMsg}
|
{#if errorMsg}
|
||||||
<div class="text-red-600 font-medium">{errorMsg}</div>
|
<div class="mt-4 text-red-600">{errorMsg}</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if successMsg}
|
{#if successMsg}
|
||||||
<div class="text-green-600 font-medium">{successMsg}</div>
|
<div class="mt-4 text-green-600">{successMsg}</div>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
|
||||||
class="btn btn-primary mt-4 w-full bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-4 rounded disabled:opacity-60"
|
|
||||||
type="submit"
|
|
||||||
disabled={submitting || (!file && textInput.trim().length === 0)}
|
|
||||||
>
|
|
||||||
{submitting ? 'Uploading...' : 'Upload'}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
Reference in New Issue
Block a user