Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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