Skip to content

CP3108 SR AI-powered marking #3126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
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
4 changes: 4 additions & 0 deletions src/commons/application/types/SessionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export type SessionState = {
readonly enableAchievements?: boolean;
readonly enableSourcecast?: boolean;
readonly enableStories?: boolean;
readonly enableLlmGrading?: boolean;
readonly llmApiKey?: string;
readonly sourceChapter?: Chapter;
readonly sourceVariant?: Variant;
readonly moduleHelpText?: string;
Expand Down Expand Up @@ -105,10 +107,12 @@ export type CourseConfiguration = {
enableAchievements: boolean;
enableSourcecast: boolean;
enableStories: boolean;
enableLlmGrading?: boolean;
sourceChapter: Chapter;
sourceVariant: Variant;
moduleHelpText: string;
assetsPrefix: string;
llmApiKey?: string;
};

export type AdminPanelCourseRegistration = {
Expand Down
2 changes: 2 additions & 0 deletions src/commons/assessment/AssessmentTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export interface IProgrammingQuestion extends BaseQuestion {
prepend: string;
postpend: string;
solutionTemplate: string;
llm_prompt?: string | null;
testcases: Testcase[];
testcasesPrivate?: Testcase[]; // For mission control
type: 'programming';
Expand Down Expand Up @@ -279,6 +280,7 @@ export const programmingTemplate = (): IProgrammingQuestion => {
prepend: '',
solutionTemplate: '//This is a mock solution template',
postpend: '',
llm_prompt: null,
testcases: [],
testcasesPrivate: [],
type: 'programming',
Expand Down
37 changes: 35 additions & 2 deletions src/commons/dropdown/DropdownCreateCourse.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ const DropdownCreateCourse: React.FC<Props> = props => {
enableAchievements: true,
enableSourcecast: true,
enableStories: false,
enableLlmGrading: false,
sourceChapter: Chapter.SOURCE_1,
sourceVariant: Variant.DEFAULT,
moduleHelpText: ''
moduleHelpText: '',
llmApiKey: ''
});

const [courseHelpTextSelectedTab, setCourseHelpTextSelectedTab] =
Expand Down Expand Up @@ -222,7 +224,8 @@ const DropdownCreateCourse: React.FC<Props> = props => {
})
}
/>

</div>
<div>
<Switch
checked={courseConfig.enableStories}
inline
Expand All @@ -234,6 +237,18 @@ const DropdownCreateCourse: React.FC<Props> = props => {
})
}
/>

<Switch
checked={courseConfig.enableLlmGrading}
inline
label="Enable LLM Grading"
onChange={e =>
setCourseConfig({
...courseConfig,
enableLlmGrading: (e.target as HTMLInputElement).checked
})
}
/>
</div>
</div>
<div>
Expand Down Expand Up @@ -273,6 +288,24 @@ const DropdownCreateCourse: React.FC<Props> = props => {
fill
/>
</FormGroup>
<FormGroup
helperText="API Key for LLM comment generation for grading. Will not be enabled if not provided"
label={'LLM API Key'}
labelInfo="(optional)"
labelFor="llmApiKey"
>
<InputGroup
id="llmApiKey"
type="password"
value={courseConfig.llmApiKey}
onChange={e =>
setCourseConfig({
...courseConfig,
llmApiKey: e.target.value
})
}
/>
</FormGroup>
</div>
<div className="create-course-button-container">
<Button text="Create Course" onClick={submitHandler} />
Expand Down
59 changes: 59 additions & 0 deletions src/commons/sagas/RequestsSaga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -884,6 +884,7 @@ export const getGrading = async (
solutionTemplate: question.solutionTemplate,
prepend: question.prepend || '',
postpend: question.postpend || '',
llm_prompt: question.llm_prompt || null,
testcases: question.testcases || [],
type: question.type as QuestionType,
maxXp: question.maxXp
Expand Down Expand Up @@ -1331,6 +1332,64 @@ export const removeAssessmentConfig = async (
return resp;
};

/**
* POST /courses/{courseId}/admin/generate-comments/{submissionId}/{questionId}
*/
export const postGenerateComments = async (
tokens: Tokens,
submission_id: integer,
question_id: integer
): Promise<{ comments: string[] } | null> => {
const resp = await request(
`${courseId()}/admin/generate-comments/${submission_id}/${question_id}`,
'POST',
{
...tokens
}
);
if (!resp || !resp.ok) {
return null;
}

return await resp.json();
};

export const saveFinalComment = async (
tokens: Tokens,
submission_id: integer,
question_id: integer,
comment: string
): Promise<Response | null> => {
const resp = await request(
`${courseId()}/admin/save-final-comment/${submission_id}/${question_id}`,
'POST',
{
body: { comment: comment },
...tokens
}
);

return resp;
};

export const saveChosenComments = async (
tokens: Tokens,
submission_id: integer,
question_id: integer,
comments: string[]
): Promise<Response | null> => {
const resp = await request(
`${courseId()}/admin/save-chosen-comments/${submission_id}/${question_id}`,
'POST',
{
body: { comments: comments },
...tokens
}
);

return resp;
};

/**
* GET /courses/{courseId}/admin/users
*/
Expand Down
9 changes: 7 additions & 2 deletions src/pages/academy/adminPanel/AdminPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
enableAchievements: true,
enableSourcecast: true,
enableStories: false,
moduleHelpText: ''
enableLlmGrading: false,
moduleHelpText: '',
llmApiKey: ''
};

const AdminPanel: React.FC = () => {
Expand Down Expand Up @@ -62,15 +64,18 @@
enableAchievements: session.enableAchievements,
enableSourcecast: session.enableSourcecast,
enableStories: session.enableStories,
moduleHelpText: session.moduleHelpText
enableLlmGrading: session.enableLlmGrading,
moduleHelpText: session.moduleHelpText,
llmApiKey: session.llmApiKey
});
}, [

Check warning on line 71 in src/pages/academy/adminPanel/AdminPanel.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

React Hook useEffect has a missing dependency: 'session.llmApiKey'. Either include it or remove the dependency array
session.courseName,
session.courseShortName,
session.enableAchievements,
session.enableGame,
session.enableSourcecast,
session.enableStories,
session.enableLlmGrading,
session.moduleHelpText,
session.viewable
]);
Expand Down
32 changes: 31 additions & 1 deletion src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@ const CourseConfigPanel: React.FC<Props> = props => {
enableAchievements,
enableSourcecast,
enableStories,
moduleHelpText
enableLlmGrading,
moduleHelpText,
llmApiKey
} = props.courseConfiguration;

const writePanel = (
Expand Down Expand Up @@ -133,6 +135,24 @@ const CourseConfigPanel: React.FC<Props> = props => {
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.WRITE && writePanel}
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.PREVIEW && previewPanel}
</FormGroup>
<FormGroup
helperText="Please enter the LLM API Key. This will be used for LLM Grading if enabled."
inline={true}
label="LLM API Key"
labelFor="llmApiKey"
>
<InputGroup
id="llmApiKey"
type="password"
defaultValue={llmApiKey}
onChange={e =>
props.setCourseConfiguration({
...props.courseConfiguration,
llmApiKey: e.target.value
})
}
/>
</FormGroup>
</div>
{!isMobileBreakpoint && <Divider />}
<div className="booleans">
Expand Down Expand Up @@ -186,6 +206,16 @@ const CourseConfigPanel: React.FC<Props> = props => {
})
}
/>
<Switch
checked={enableLlmGrading}
label="Enable LLM Grading"
onChange={e =>
props.setCourseConfiguration({
...props.courseConfiguration,
enableLlmGrading: (e.target as HTMLInputElement).checked
})
}
/>
</div>
</div>
</div>
Expand Down
38 changes: 38 additions & 0 deletions src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { NonIdealState, Spinner } from '@blueprintjs/core';
import React from 'react';

type Props = {
comments: string[];
isLoading: boolean;
onSelect: (comment: string) => void;
};

const GradingCommentSelector: React.FC<Props> = props => {
return (
<div className="grading-comment-selector">
<div className="grading-comment-selector-title">Comment Suggestions:</div>

{props.isLoading ? (
<NonIdealState icon={<Spinner />} />
) : (
<div>
{' '}
{props.comments.map(el => {
return (
<div
className="grading-comment-selector-item"
onClick={() => {
props.onSelect(el);
}}
>
{el}
</div>
);
})}{' '}
</div>
)}
</div>
);
};

export default GradingCommentSelector;
Loading
Loading