From be8566385b52033746c43391351d6ea47c887596 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Mon, 27 Jan 2025 13:25:51 +0800
Subject: [PATCH 01/28] Add Grading comment selector basic UI
---
.../subcomponents/GradingCommentSelector.tsx | 26 +++++++++++++++++
.../grading/subcomponents/GradingEditor.tsx | 6 ++++
src/pages/playground/Playground.tsx | 6 +++-
src/styles/_workspace.scss | 29 +++++++++++++++++++
4 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
mode change 100755 => 100644 src/styles/_workspace.scss
diff --git a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
new file mode 100644
index 0000000000..4abc4a5dae
--- /dev/null
+++ b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
@@ -0,0 +1,26 @@
+import React from "react";
+
+type Props = {
+ comments: string[],
+ setEditor: React.Dispatch>
+};
+
+const GradingCommentSelector : React.FC = (prop) => {
+ return (
+
+
+ Comment Suggestions:
+
+ {prop.comments.map(el => {
+ return
{prop.setEditor(el)}}
+ >
+ {el}
+
+ })}
+
+ )
+}
+
+export default GradingCommentSelector
\ No newline at end of file
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 3c162606dd..e121f28987 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -25,6 +25,7 @@ import {
showWarningMessage
} from '../../../../commons/utils/notifications/NotificationsHelper';
import { convertParamToInt } from '../../../../commons/utils/ParamParseHelper';
+import GradingCommentSelector from './GradingCommentSelector';
type GradingSaveFunction = (
submissionId: number,
@@ -303,6 +304,11 @@ const GradingEditor: React.FC = props => {
+
+
= props => {
) : (
+
-
+
+
+ {false && }
);
};
diff --git a/src/styles/_workspace.scss b/src/styles/_workspace.scss
old mode 100755
new mode 100644
index 63af80872f..7f941dc539
--- a/src/styles/_workspace.scss
+++ b/src/styles/_workspace.scss
@@ -340,6 +340,35 @@ $code-color-notification: #f9f0d7;
}
}
+ .grading-comment-selector {
+ text-align: center;
+ display: flex !important;
+ justify-content: center;
+ flex-direction: column;
+ font-size: 0.875rem;
+ border-radius: 4px;
+ background-color: $cadet-color-1;
+ margin-top: 4px;
+ margin-bottom: 4px;
+ padding: 8px;
+ }
+
+ .grading-comment-selector-title {
+ margin-bottom: 8px;
+ }
+
+ .grading-comment-selector-item {
+ margin: 4px;
+ padding: 4px;
+ background-color: $cadet-color-2;
+ border: 1px solid $cadet-color-3;
+ cursor: pointer;
+
+ &:hover {
+ background-color: $cadet-color-3;
+ }
+ }
+
.react-mde-parent {
margin-bottom: 12px;
}
From 05b449aefedaf6ce4f733a6b8694c7c1f094b5fd Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Tue, 4 Feb 2025 11:39:20 +0800
Subject: [PATCH 02/28] Connect to backend
---
src/commons/sagas/RequestsSaga.ts | 20 +++++++++++++
.../subcomponents/GradingCommentSelector.tsx | 29 ++++++++++---------
.../grading/subcomponents/GradingEditor.tsx | 23 ++++++++++++++-
src/pages/playground/Playground.tsx | 5 +---
4 files changed, 58 insertions(+), 19 deletions(-)
diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts
index 29f88df173..3ad0378394 100644
--- a/src/commons/sagas/RequestsSaga.ts
+++ b/src/commons/sagas/RequestsSaga.ts
@@ -1331,6 +1331,26 @@ 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();
+};
+
+
+
/**
* GET /courses/{courseId}/admin/users
*/
diff --git a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
index 4abc4a5dae..3a1a867cc3 100644
--- a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
@@ -6,21 +6,22 @@ type Props = {
};
const GradingCommentSelector : React.FC = (prop) => {
- return (
-
-
- Comment Suggestions:
-
- {prop.comments.map(el => {
- return
{prop.setEditor(el)}}
- >
- {el}
+
+ return (
+
+
+ Comment Suggestions:
- })}
-
- )
+ {prop.comments.map(el => {
+ return
{prop.setEditor(el)}}
+ >
+ {el}
+
+ })}
+
+ )
}
export default GradingCommentSelector
\ No newline at end of file
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index e121f28987..f1d5c14456 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -27,6 +27,9 @@ import {
import { convertParamToInt } from '../../../../commons/utils/ParamParseHelper';
import GradingCommentSelector from './GradingCommentSelector';
+import { useTokens } from 'src/commons/utils/Hooks';
+import { postGenerateComments } from '../../../../commons/sagas/RequestsSaga';
+
type GradingSaveFunction = (
submissionId: number,
questionId: number,
@@ -52,6 +55,7 @@ const gradingEditorButtonClass = 'grading-editor-button';
const GradingEditor: React.FC
= props => {
const dispatch = useDispatch();
+ const tokens = useTokens();
const { handleGradingSave, handleGradingSaveAndContinue, handleReautogradeAnswer } = useMemo(
() =>
({
@@ -102,6 +106,15 @@ const GradingEditor: React.FC = props => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.submissionId, props.questionId]);
+ const getCommentSuggestions = async () => {
+ const resp = await postGenerateComments(
+ tokens,props.submissionId,props.questionId
+ )
+ return resp
+ }
+
+ const [suggestions, setSuggestions] = useState([])
+
const makeInitialState = () => {
setXpAdjustmentInput(props.xpAdjustment.toString());
setEditorValue(props.comments);
@@ -307,8 +320,16 @@ const GradingEditor: React.FC = props => {
+ {
+ const resp = await getCommentSuggestions();
+ console.log(resp!.comments)
+ setSuggestions(resp!.comments);
+ }}>
+ Get comments
+
= props => {
) : (
-
-
- {false && }
+
);
};
From 780a269f033f39b24bf449b9f27ace694ac29e47 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Tue, 4 Feb 2025 11:40:22 +0800
Subject: [PATCH 03/28] Content changes not found but are showing up as changed
---
.husky/pre-push | 0
.tool-versions | 1 +
scripts/coverage-fix.sh | 0
scripts/test-coveralls.sh | 0
scripts/test.sh | 0
src/styles/_sourcecast.scss | 0
src/styles/_sourcereel.scss | 0
src/styles/index.scss | 0
8 files changed, 1 insertion(+)
mode change 100755 => 100644 .husky/pre-push
create mode 100644 .tool-versions
mode change 100755 => 100644 scripts/coverage-fix.sh
mode change 100755 => 100644 scripts/test-coveralls.sh
mode change 100755 => 100644 scripts/test.sh
mode change 100755 => 100644 src/styles/_sourcecast.scss
mode change 100755 => 100644 src/styles/_sourcereel.scss
mode change 100755 => 100644 src/styles/index.scss
diff --git a/.husky/pre-push b/.husky/pre-push
old mode 100755
new mode 100644
diff --git a/.tool-versions b/.tool-versions
new file mode 100644
index 0000000000..1a3e61bfce
--- /dev/null
+++ b/.tool-versions
@@ -0,0 +1 @@
+nodejs v20.18.1
diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh
old mode 100755
new mode 100644
diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh
old mode 100755
new mode 100644
diff --git a/scripts/test.sh b/scripts/test.sh
old mode 100755
new mode 100644
diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss
old mode 100755
new mode 100644
diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss
old mode 100755
new mode 100644
diff --git a/src/styles/index.scss b/src/styles/index.scss
old mode 100755
new mode 100644
From 3ae34a09b68d464ef6dc9d55a18551ad832946c7 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Tue, 18 Mar 2025 13:56:11 +0800
Subject: [PATCH 04/28] Hide generate comment button if no LLM prompt
---
src/commons/assessment/AssessmentTypes.ts | 2 ++
src/commons/sagas/RequestsSaga.ts | 1 +
.../grading/subcomponents/GradingEditor.tsx | 28 +++++++++++--------
.../subcomponents/GradingWorkspace.tsx | 2 ++
4 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/commons/assessment/AssessmentTypes.ts b/src/commons/assessment/AssessmentTypes.ts
index c3d5cecbf4..9cc37f8c52 100644
--- a/src/commons/assessment/AssessmentTypes.ts
+++ b/src/commons/assessment/AssessmentTypes.ts
@@ -119,6 +119,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';
@@ -277,6 +278,7 @@ export const programmingTemplate = (): IProgrammingQuestion => {
prepend: '',
solutionTemplate: '//This is a mock solution template',
postpend: '',
+ llm_prompt: null,
testcases: [],
testcasesPrivate: [],
type: 'programming',
diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts
index 3ad0378394..582c2b2fcf 100644
--- a/src/commons/sagas/RequestsSaga.ts
+++ b/src/commons/sagas/RequestsSaga.ts
@@ -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
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index f1d5c14456..654ac49b55 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -46,6 +46,7 @@ type Props = {
maxXp: number;
studentNames: string[];
studentUsernames: string[];
+ is_llm: boolean;
comments: string;
graderName?: string;
gradedAt?: string;
@@ -318,18 +319,21 @@ const GradingEditor: React.FC = props => {
-
- {
- const resp = await getCommentSuggestions();
- console.log(resp!.comments)
- setSuggestions(resp!.comments);
- }}>
- Get comments
-
+ {props.is_llm &&
+
+
+ {
+ const resp = await getCommentSuggestions();
+ console.log(resp!.comments)
+ setSuggestions(resp!.comments);
+ }}>
+ Get comments
+
+
}
= props => {
? [grading!.answers[questionId].student.username]
: grading!.answers[questionId].team!.map(member => member.username)
}
+ is_llm={grading!.answers[questionId].question.type == "programming" &&
+ !!grading!.answers[questionId].question.llm_prompt}
comments={grading!.answers[questionId].grade.comments ?? ''}
graderName={
grading!.answers[questionId].grade.grader
From b41843a2f9e0fb773735a0e298d94b54dcc18a70 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Tue, 18 Mar 2025 15:42:17 +0800
Subject: [PATCH 05/28] Input field for API key during Course creation
---
src/commons/application/types/SessionTypes.ts | 1 +
src/commons/dropdown/DropdownCreateCourse.tsx | 20 ++++++++++++++++++-
2 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index 20d4bb4402..feec4a273e 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -109,6 +109,7 @@ export type CourseConfiguration = {
sourceVariant: Variant;
moduleHelpText: string;
assetsPrefix: string;
+ llmAPIKey?: string;
};
export type AdminPanelCourseRegistration = {
diff --git a/src/commons/dropdown/DropdownCreateCourse.tsx b/src/commons/dropdown/DropdownCreateCourse.tsx
index 2d37ce7eb9..c5311fcbbe 100644
--- a/src/commons/dropdown/DropdownCreateCourse.tsx
+++ b/src/commons/dropdown/DropdownCreateCourse.tsx
@@ -42,7 +42,8 @@ const DropdownCreateCourse: React.FC = props => {
enableStories: false,
sourceChapter: Chapter.SOURCE_1,
sourceVariant: Variant.DEFAULT,
- moduleHelpText: ''
+ moduleHelpText: '',
+ llmAPIKey: ''
});
const [courseHelpTextSelectedTab, setCourseHelpTextSelectedTab] =
@@ -273,6 +274,23 @@ const DropdownCreateCourse: React.FC = props => {
fill
/>
+
+
+ setCourseConfig({
+ ...courseConfig,
+ llmAPIKey: e.target.value
+ })
+ }
+ />
+
From 05b1a9eb575d07c4066445a827d820f53d95c904 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Thu, 20 Mar 2025 11:24:30 +0800
Subject: [PATCH 06/28] Add input field for enabling LLM Grading
---
src/commons/application/types/SessionTypes.ts | 1 +
src/commons/dropdown/DropdownCreateCourse.tsx | 16 +++++++++++++++-
2 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index feec4a273e..5dd12eccba 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -105,6 +105,7 @@ export type CourseConfiguration = {
enableAchievements: boolean;
enableSourcecast: boolean;
enableStories: boolean;
+ enableLlmGrading?: boolean;
sourceChapter: Chapter;
sourceVariant: Variant;
moduleHelpText: string;
diff --git a/src/commons/dropdown/DropdownCreateCourse.tsx b/src/commons/dropdown/DropdownCreateCourse.tsx
index c5311fcbbe..c83d222a55 100644
--- a/src/commons/dropdown/DropdownCreateCourse.tsx
+++ b/src/commons/dropdown/DropdownCreateCourse.tsx
@@ -40,6 +40,7 @@ const DropdownCreateCourse: React.FC = props => {
enableAchievements: true,
enableSourcecast: true,
enableStories: false,
+ enableLlmGrading: false,
sourceChapter: Chapter.SOURCE_1,
sourceVariant: Variant.DEFAULT,
moduleHelpText: '',
@@ -223,7 +224,8 @@ const DropdownCreateCourse: React.FC = props => {
})
}
/>
-
+
+
= props => {
})
}
/>
+
+
+ setCourseConfig({
+ ...courseConfig,
+ enableLlmGrading: (e.target as HTMLInputElement).checked
+ })
+ }
+ />
From 2d541157bc1d6f3ab17da2b9f76b3d7f70c79289 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Thu, 20 Mar 2025 11:43:47 +0800
Subject: [PATCH 07/28] Disable comment selector if llm grading not enabled
---
src/commons/application/types/SessionTypes.ts | 1 +
src/pages/academy/grading/subcomponents/GradingWorkspace.tsx | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index 5dd12eccba..ff60f8660f 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -39,6 +39,7 @@ export type SessionState = {
readonly enableAchievements?: boolean;
readonly enableSourcecast?: boolean;
readonly enableStories?: boolean;
+ readonly enableLlmGrading?: boolean;
readonly sourceChapter?: Chapter;
readonly sourceVariant?: Variant;
readonly moduleHelpText?: string;
diff --git a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
index e1d5622b9b..d1b1486687 100644
--- a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
@@ -62,6 +62,7 @@ const GradingWorkspace: React.FC = props => {
const grading = useTypedSelector(state => state.session.gradings[props.submissionId]);
const courseId = useTypedSelector(state => state.session.courseId);
+ const llm_grading = useTypedSelector(state => state.session.enableLlmGrading);
const {
autogradingResults,
isFolderModeEnabled,
@@ -304,7 +305,7 @@ const GradingWorkspace: React.FC = props => {
? [grading!.answers[questionId].student.username]
: grading!.answers[questionId].team!.map(member => member.username)
}
- is_llm={grading!.answers[questionId].question.type == "programming" &&
+ is_llm={!!llm_grading && grading!.answers[questionId].question.type == "programming" &&
!!grading!.answers[questionId].question.llm_prompt}
comments={grading!.answers[questionId].grade.comments ?? ''}
graderName={
From 928633d857970c77ce89a3c20fd35ffd550f95c6 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Wed, 26 Mar 2025 15:31:57 +0800
Subject: [PATCH 08/28] Added LLM API Key input and integration with backend
---
src/commons/application/types/SessionTypes.ts | 2 +-
src/commons/dropdown/DropdownCreateCourse.tsx | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index ff60f8660f..5498dcda1f 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -111,7 +111,7 @@ export type CourseConfiguration = {
sourceVariant: Variant;
moduleHelpText: string;
assetsPrefix: string;
- llmAPIKey?: string;
+ llmApiKey?: string;
};
export type AdminPanelCourseRegistration = {
diff --git a/src/commons/dropdown/DropdownCreateCourse.tsx b/src/commons/dropdown/DropdownCreateCourse.tsx
index c83d222a55..5a3fa6aa73 100644
--- a/src/commons/dropdown/DropdownCreateCourse.tsx
+++ b/src/commons/dropdown/DropdownCreateCourse.tsx
@@ -44,7 +44,7 @@ const DropdownCreateCourse: React.FC = props => {
sourceChapter: Chapter.SOURCE_1,
sourceVariant: Variant.DEFAULT,
moduleHelpText: '',
- llmAPIKey: ''
+ llmApiKey: ''
});
const [courseHelpTextSelectedTab, setCourseHelpTextSelectedTab] =
@@ -292,15 +292,15 @@ const DropdownCreateCourse: React.FC = props => {
helperText="API Key for LLM comment generation for grading. Will not be enabled if not provided"
label={'LLM API Key'}
labelInfo="(optional)"
- labelFor="llmAPIKey"
+ labelFor="llmApiKey"
>
setCourseConfig({
...courseConfig,
- llmAPIKey: e.target.value
+ llmApiKey: e.target.value
})
}
/>
From 73703b1a6df6ad9855de21a631907bcd89754d06 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Wed, 26 Mar 2025 15:53:28 +0800
Subject: [PATCH 09/28] Add to the course editing panel for llm grading and api
key
---
src/pages/academy/adminPanel/AdminPanel.tsx | 9 ++++--
.../subcomponents/CourseConfigPanel.tsx | 31 ++++++++++++++++++-
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/src/pages/academy/adminPanel/AdminPanel.tsx b/src/pages/academy/adminPanel/AdminPanel.tsx
index d60edb1647..ee000f96b7 100644
--- a/src/pages/academy/adminPanel/AdminPanel.tsx
+++ b/src/pages/academy/adminPanel/AdminPanel.tsx
@@ -29,7 +29,9 @@ const defaultCourseConfig: UpdateCourseConfiguration = {
enableAchievements: true,
enableSourcecast: true,
enableStories: false,
- moduleHelpText: ''
+ enableLlmGrading: false,
+ moduleHelpText: '',
+ llmApiKey: ''
};
const AdminPanel: React.FC = () => {
@@ -62,7 +64,9 @@ const AdminPanel: React.FC = () => {
enableAchievements: session.enableAchievements,
enableSourcecast: session.enableSourcecast,
enableStories: session.enableStories,
- moduleHelpText: session.moduleHelpText
+ enableLlmGrading: session.enableLlmGrading,
+ moduleHelpText: session.moduleHelpText,
+ llmApiKey: ''
});
}, [
session.courseName,
@@ -71,6 +75,7 @@ const AdminPanel: React.FC = () => {
session.enableGame,
session.enableSourcecast,
session.enableStories,
+ session.enableLlmGrading,
session.moduleHelpText,
session.viewable
]);
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index 023c659038..61ebad75b3 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -38,7 +38,9 @@ const CourseConfigPanel: React.FC = props => {
enableAchievements,
enableSourcecast,
enableStories,
- moduleHelpText
+ enableLlmGrading,
+ moduleHelpText,
+ llmApiKey
} = props.courseConfiguration;
const writePanel = (
@@ -133,6 +135,23 @@ const CourseConfigPanel: React.FC = props => {
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.WRITE && writePanel}
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.PREVIEW && previewPanel}
+
+
+ props.setCourseConfiguration({
+ ...props.courseConfiguration,
+ llmApiKey: e.target.value
+ })
+ }
+ />
+
{!isMobileBreakpoint && }
@@ -186,6 +205,16 @@ const CourseConfigPanel: React.FC
= props => {
})
}
/>
+
+ props.setCourseConfiguration({
+ ...props.courseConfiguration,
+ enableLlmGrading: (e.target as HTMLInputElement).checked
+ })
+ }
+ />
From 730cb35786f556733f4889b3a40fbf2ca4488901 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Sat, 5 Apr 2025 22:17:24 +0800
Subject: [PATCH 10/28] Update localStorage.ts
Fix redux state not saving issue
---
src/pages/localStorage.ts | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/pages/localStorage.ts b/src/pages/localStorage.ts
index 0f9cd6d2d3..583ebb9ed5 100644
--- a/src/pages/localStorage.ts
+++ b/src/pages/localStorage.ts
@@ -69,6 +69,7 @@ export const saveState = (state: OverallState) => {
enableAchievements: state.session.enableAchievements,
enableSourcecast: state.session.enableSourcecast,
enableStories: state.session.enableStories,
+ enableLlmGrading: state.session.enableLlmGrading,
moduleHelpText: state.session.moduleHelpText,
assetsPrefix: state.session.assetsPrefix,
assessmentConfigurations: state.session.assessmentConfigurations,
From fb4f15d32f5a5d6e31612caaefe1877a9b915cfd Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Sun, 6 Apr 2025 15:53:44 +0800
Subject: [PATCH 11/28] Add save chosen and final comment feature
Comment selector can now handle selecting multiple suggestions
---
src/commons/sagas/RequestsSaga.ts | 26 ++++++++++++++
.../subcomponents/GradingCommentSelector.tsx | 4 +--
.../grading/subcomponents/GradingEditor.tsx | 34 +++++++++++++++++--
3 files changed, 59 insertions(+), 5 deletions(-)
diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts
index 582c2b2fcf..b942e393fe 100644
--- a/src/commons/sagas/RequestsSaga.ts
+++ b/src/commons/sagas/RequestsSaga.ts
@@ -1350,7 +1350,33 @@ export const postGenerateComments = async (
return await resp.json();
};
+export const saveFinalComment = async (
+ tokens: Tokens,
+ submission_id: integer,
+ question_id: integer,
+ comment: string
+): Promise => {
+ 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 => {
+ 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
diff --git a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
index 3a1a867cc3..ddcbe5462c 100644
--- a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
@@ -2,7 +2,7 @@ import React from "react";
type Props = {
comments: string[],
- setEditor: React.Dispatch>
+ onSelect: (comment: string) => void
};
const GradingCommentSelector : React.FC = (prop) => {
@@ -15,7 +15,7 @@ const GradingCommentSelector : React.FC = (prop) => {
{prop.comments.map(el => {
return {prop.setEditor(el)}}
+ onClick={() => {prop.onSelect(el)}}
>
{el}
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 654ac49b55..9a1e53768a 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -29,6 +29,8 @@ import GradingCommentSelector from './GradingCommentSelector';
import { useTokens } from 'src/commons/utils/Hooks';
import { postGenerateComments } from '../../../../commons/sagas/RequestsSaga';
+import { saveFinalComment } from '../../../../commons/sagas/RequestsSaga';
+import { saveChosenComments } from '../../../../commons/sagas/RequestsSaga';
type GradingSaveFunction = (
submissionId: number,
@@ -109,13 +111,38 @@ const GradingEditor: React.FC = props => {
const getCommentSuggestions = async () => {
const resp = await postGenerateComments(
- tokens,props.submissionId,props.questionId
+ tokens, props.submissionId, props.questionId
)
return resp
}
+ const onSelectGeneratedComments = (comment: string) => {
+ if (!selectedSuggestions.includes(comment)) {
+ setSelectedSuggestions([comment, ...selectedSuggestions])
+ }
+
+ setEditorValue(editorValue + comment)
+ }
+
+ const postSaveFinalComment = async (comment : string) => {
+ const resp = await saveFinalComment(
+ tokens, props.submissionId, props.questionId, comment
+ )
+ return resp
+ }
+
+ const postSaveChosenComments = async (comments : string[]) => {
+ const resp = await saveChosenComments(
+ tokens, props.submissionId, props.questionId, comments
+ )
+
+ return resp
+ }
+
const [suggestions, setSuggestions] = useState([])
+ const [selectedSuggestions, setSelectedSuggestions] = useState([])
+
const makeInitialState = () => {
setXpAdjustmentInput(props.xpAdjustment.toString());
setEditorValue(props.comments);
@@ -144,6 +171,8 @@ const GradingEditor: React.FC = props => {
() => {
const newXpAdjustmentInput = convertParamToInt(xpAdjustmentInput || undefined) || undefined;
const xp = props.initialXp + (newXpAdjustmentInput || 0);
+ postSaveFinalComment(editorValue);
+ postSaveChosenComments(selectedSuggestions);
if (xp < 0 || xp > props.maxXp) {
showWarningMessage(
`XP ${xp.toString()} is out of bounds. Maximum xp is ${props.maxXp.toString()}.`
@@ -322,13 +351,12 @@ const GradingEditor: React.FC = props => {
{props.is_llm &&
{
const resp = await getCommentSuggestions();
- console.log(resp!.comments)
setSuggestions(resp!.comments);
}}>
Get comments
From 44a583ee1d8c125318f5dbac652c2f87bd98c8b8 Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Sun, 6 Apr 2025 16:11:14 +0800
Subject: [PATCH 12/28] Add Spinner when generating comments
---
.../subcomponents/GradingCommentSelector.tsx | 15 +++++++++++----
.../grading/subcomponents/GradingEditor.tsx | 5 ++++-
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
index ddcbe5462c..11cb5df942 100644
--- a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
@@ -1,25 +1,32 @@
+import { NonIdealState, Spinner } from "@blueprintjs/core";
import React from "react";
type Props = {
comments: string[],
+ isLoading: boolean,
onSelect: (comment: string) => void
};
-const GradingCommentSelector : React.FC = (prop) => {
+const GradingCommentSelector : React.FC = (props) => {
return (
Comment Suggestions:
- {prop.comments.map(el => {
+
+ {props.isLoading
+ ?
}/>
+ :
{props.comments.map(el => {
return
{prop.onSelect(el)}}
+ onClick={() => {props.onSelect(el)}}
>
{el}
- })}
+ })}
}
+
+
)
}
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 9a1e53768a..990d6e3f9d 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -140,8 +140,8 @@ const GradingEditor: React.FC = props => {
}
const [suggestions, setSuggestions] = useState([])
-
const [selectedSuggestions, setSelectedSuggestions] = useState([])
+ const [hasClickedGenerate, setHasClickedGenerate] = useState(false)
const makeInitialState = () => {
setXpAdjustmentInput(props.xpAdjustment.toString());
@@ -352,11 +352,14 @@ const GradingEditor: React.FC = props => {
{
+ setHasClickedGenerate(true)
const resp = await getCommentSuggestions();
+ setHasClickedGenerate(false)
setSuggestions(resp!.comments);
}}>
Get comments
From 580644a6d30c4282e110fbc79381c9f627e3c6ed Mon Sep 17 00:00:00 2001
From: Eugene Oh Yun Zheng
Date: Sun, 6 Apr 2025 18:16:36 +0800
Subject: [PATCH 13/28] Update to show API Key as password field
---
src/commons/application/types/SessionTypes.ts | 1 +
src/commons/dropdown/DropdownCreateCourse.tsx | 1 +
src/pages/academy/adminPanel/AdminPanel.tsx | 2 +-
.../academy/adminPanel/subcomponents/CourseConfigPanel.tsx | 1 +
src/pages/academy/grading/subcomponents/GradingWorkspace.tsx | 3 +--
src/pages/localStorage.ts | 1 +
6 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index 5498dcda1f..08388daf0f 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -40,6 +40,7 @@ export type SessionState = {
readonly enableSourcecast?: boolean;
readonly enableStories?: boolean;
readonly enableLlmGrading?: boolean;
+ readonly llmApiKey?: string;
readonly sourceChapter?: Chapter;
readonly sourceVariant?: Variant;
readonly moduleHelpText?: string;
diff --git a/src/commons/dropdown/DropdownCreateCourse.tsx b/src/commons/dropdown/DropdownCreateCourse.tsx
index 5a3fa6aa73..88d5018b52 100644
--- a/src/commons/dropdown/DropdownCreateCourse.tsx
+++ b/src/commons/dropdown/DropdownCreateCourse.tsx
@@ -296,6 +296,7 @@ const DropdownCreateCourse: React.FC = props => {
>
setCourseConfig({
diff --git a/src/pages/academy/adminPanel/AdminPanel.tsx b/src/pages/academy/adminPanel/AdminPanel.tsx
index ee000f96b7..80921fab60 100644
--- a/src/pages/academy/adminPanel/AdminPanel.tsx
+++ b/src/pages/academy/adminPanel/AdminPanel.tsx
@@ -66,7 +66,7 @@ const AdminPanel: React.FC = () => {
enableStories: session.enableStories,
enableLlmGrading: session.enableLlmGrading,
moduleHelpText: session.moduleHelpText,
- llmApiKey: ''
+ llmApiKey: session.llmApiKey
});
}, [
session.courseName,
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index 61ebad75b3..421b895605 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -143,6 +143,7 @@ const CourseConfigPanel: React.FC = props => {
>
props.setCourseConfiguration({
diff --git a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
index d1b1486687..7c3adb98a6 100644
--- a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
@@ -305,8 +305,7 @@ const GradingWorkspace: React.FC = props => {
? [grading!.answers[questionId].student.username]
: grading!.answers[questionId].team!.map(member => member.username)
}
- is_llm={!!llm_grading && grading!.answers[questionId].question.type == "programming" &&
- !!grading!.answers[questionId].question.llm_prompt}
+ is_llm={!!llm_grading && grading!.answers[questionId].question.type == "programming"}
comments={grading!.answers[questionId].grade.comments ?? ''}
graderName={
grading!.answers[questionId].grade.grader
diff --git a/src/pages/localStorage.ts b/src/pages/localStorage.ts
index 583ebb9ed5..66363b269c 100644
--- a/src/pages/localStorage.ts
+++ b/src/pages/localStorage.ts
@@ -70,6 +70,7 @@ export const saveState = (state: OverallState) => {
enableSourcecast: state.session.enableSourcecast,
enableStories: state.session.enableStories,
enableLlmGrading: state.session.enableLlmGrading,
+ llmApiKey: state.session.llmApiKey,
moduleHelpText: state.session.moduleHelpText,
assetsPrefix: state.session.assetsPrefix,
assessmentConfigurations: state.session.assessmentConfigurations,
From a0512bd3c1090021b517d911ec686bf08ff018e4 Mon Sep 17 00:00:00 2001
From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
Date: Fri, 18 Apr 2025 03:15:53 +0800
Subject: [PATCH 14/28] Fix format
---
src/commons/sagas/RequestsSaga.ts | 48 ++++++----
.../subcomponents/GradingCommentSelector.tsx | 58 +++++++------
.../grading/subcomponents/GradingEditor.tsx | 87 +++++++++----------
.../subcomponents/GradingWorkspace.tsx | 2 +-
src/pages/playground/Playground.tsx | 1 -
5 files changed, 103 insertions(+), 93 deletions(-)
diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts
index b942e393fe..840a564ab8 100644
--- a/src/commons/sagas/RequestsSaga.ts
+++ b/src/commons/sagas/RequestsSaga.ts
@@ -1339,10 +1339,14 @@ 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
- });
+): Promise<{ comments: string[] } | null> => {
+ const resp = await request(
+ `${courseId()}/admin/generate-comments/${submission_id}/${question_id}`,
+ 'POST',
+ {
+ ...tokens
+ }
+ );
if (!resp || !resp.ok) {
return null;
}
@@ -1356,13 +1360,17 @@ export const saveFinalComment = async (
question_id: integer,
comment: string
): Promise => {
- const resp = await request(`${courseId()}/admin/save-final-comment/${submission_id}/${question_id}`, 'POST', {
- body: {"comment": comment},
- ...tokens
- })
-
- return resp
-}
+ 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,
@@ -1370,13 +1378,17 @@ export const saveChosenComments = async (
question_id: integer,
comments: string[]
): Promise => {
- const resp = await request(`${courseId()}/admin/save-chosen-comments/${submission_id}/${question_id}`, 'POST', {
- body: {"comments": comments},
- ...tokens
- })
-
- return resp
-}
+ 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
diff --git a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
index 11cb5df942..6c22964a79 100644
--- a/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingCommentSelector.tsx
@@ -1,34 +1,38 @@
-import { NonIdealState, Spinner } from "@blueprintjs/core";
-import React from "react";
+import { NonIdealState, Spinner } from '@blueprintjs/core';
+import React from 'react';
type Props = {
- comments: string[],
- isLoading: boolean,
- onSelect: (comment: string) => void
+ comments: string[];
+ isLoading: boolean;
+ onSelect: (comment: string) => void;
};
-const GradingCommentSelector : React.FC = (props) => {
+const GradingCommentSelector: React.FC = props => {
+ return (
+
+
Comment Suggestions:
- return (
-
-
- Comment Suggestions:
-
-
- {props.isLoading
- ?
}/>
- :
{props.comments.map(el => {
- return
{props.onSelect(el)}}
- >
- {el}
-
- })}
}
-
-
+ {props.isLoading ? (
+
} />
+ ) : (
+
+ {' '}
+ {props.comments.map(el => {
+ return (
+
{
+ props.onSelect(el);
+ }}
+ >
+ {el}
+
+ );
+ })}{' '}
- )
-}
+ )}
+
+ );
+};
-export default GradingCommentSelector
\ No newline at end of file
+export default GradingCommentSelector;
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 990d6e3f9d..542ab7ab8b 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -13,11 +13,15 @@ import { IconNames } from '@blueprintjs/icons';
import React, { useEffect, useMemo, useState } from 'react';
import ReactMde, { ReactMdeProps } from 'react-mde';
import { useDispatch } from 'react-redux';
+import { useTokens } from 'src/commons/utils/Hooks';
import SessionActions from '../../../../commons/application/actions/SessionActions';
import ControlButton from '../../../../commons/ControlButton';
import Markdown from '../../../../commons/Markdown';
import { Prompt } from '../../../../commons/ReactRouterPrompt';
+import { postGenerateComments } from '../../../../commons/sagas/RequestsSaga';
+import { saveFinalComment } from '../../../../commons/sagas/RequestsSaga';
+import { saveChosenComments } from '../../../../commons/sagas/RequestsSaga';
import { getPrettyDate } from '../../../../commons/utils/DateHelper';
import { showSimpleConfirmDialog } from '../../../../commons/utils/DialogHelper';
import {
@@ -27,11 +31,6 @@ import {
import { convertParamToInt } from '../../../../commons/utils/ParamParseHelper';
import GradingCommentSelector from './GradingCommentSelector';
-import { useTokens } from 'src/commons/utils/Hooks';
-import { postGenerateComments } from '../../../../commons/sagas/RequestsSaga';
-import { saveFinalComment } from '../../../../commons/sagas/RequestsSaga';
-import { saveChosenComments } from '../../../../commons/sagas/RequestsSaga';
-
type GradingSaveFunction = (
submissionId: number,
questionId: number,
@@ -110,38 +109,32 @@ const GradingEditor: React.FC
= props => {
}, [props.submissionId, props.questionId]);
const getCommentSuggestions = async () => {
- const resp = await postGenerateComments(
- tokens, props.submissionId, props.questionId
- )
- return resp
- }
+ const resp = await postGenerateComments(tokens, props.submissionId, props.questionId);
+ return resp;
+ };
const onSelectGeneratedComments = (comment: string) => {
if (!selectedSuggestions.includes(comment)) {
- setSelectedSuggestions([comment, ...selectedSuggestions])
+ setSelectedSuggestions([comment, ...selectedSuggestions]);
}
- setEditorValue(editorValue + comment)
- }
+ setEditorValue(editorValue + comment);
+ };
- const postSaveFinalComment = async (comment : string) => {
- const resp = await saveFinalComment(
- tokens, props.submissionId, props.questionId, comment
- )
- return resp
- }
+ const postSaveFinalComment = async (comment: string) => {
+ const resp = await saveFinalComment(tokens, props.submissionId, props.questionId, comment);
+ return resp;
+ };
- const postSaveChosenComments = async (comments : string[]) => {
- const resp = await saveChosenComments(
- tokens, props.submissionId, props.questionId, comments
- )
+ const postSaveChosenComments = async (comments: string[]) => {
+ const resp = await saveChosenComments(tokens, props.submissionId, props.questionId, comments);
- return resp
- }
+ return resp;
+ };
- const [suggestions, setSuggestions] = useState([])
- const [selectedSuggestions, setSelectedSuggestions] = useState([])
- const [hasClickedGenerate, setHasClickedGenerate] = useState(false)
+ const [suggestions, setSuggestions] = useState([]);
+ const [selectedSuggestions, setSelectedSuggestions] = useState([]);
+ const [hasClickedGenerate, setHasClickedGenerate] = useState(false);
const makeInitialState = () => {
setXpAdjustmentInput(props.xpAdjustment.toString());
@@ -347,24 +340,26 @@ const GradingEditor: React.FC = props => {
-
- {props.is_llm &&
-
-
- {
- setHasClickedGenerate(true)
- const resp = await getCommentSuggestions();
- setHasClickedGenerate(false)
- setSuggestions(resp!.comments);
- }}>
- Get comments
-
-
}
+
+ {props.is_llm && (
+
+
+ {
+ setHasClickedGenerate(true);
+ const resp = await getCommentSuggestions();
+ setHasClickedGenerate(false);
+ setSuggestions(resp!.comments);
+ }}
+ >
+ Get comments
+
+
+ )}
= props => {
? [grading!.answers[questionId].student.username]
: grading!.answers[questionId].team!.map(member => member.username)
}
- is_llm={!!llm_grading && grading!.answers[questionId].question.type == "programming"}
+ is_llm={!!llm_grading && grading!.answers[questionId].question.type == 'programming'}
comments={grading!.answers[questionId].grade.comments ?? ''}
graderName={
grading!.answers[questionId].grade.grader
diff --git a/src/pages/playground/Playground.tsx b/src/pages/playground/Playground.tsx
index 01664a13cb..58aeabeebd 100644
--- a/src/pages/playground/Playground.tsx
+++ b/src/pages/playground/Playground.tsx
@@ -1035,7 +1035,6 @@ const Playground: React.FC = props => {
) : (
-
From b3f5ca7d693ed451e6c992b6537063df5f26e6ce Mon Sep 17 00:00:00 2001
From: Richard Dominick <34370238+RichDom2185@users.noreply.github.com>
Date: Fri, 18 Apr 2025 03:24:20 +0800
Subject: [PATCH 15/28] Revert "Content changes not found but are showing up as
changed"
This reverts commit 780a269f033f39b24bf449b9f27ace694ac29e47.
---
.husky/pre-push | 0
.tool-versions | 1 -
scripts/coverage-fix.sh | 0
scripts/test-coveralls.sh | 0
scripts/test.sh | 0
src/styles/_sourcecast.scss | 0
src/styles/_sourcereel.scss | 0
src/styles/index.scss | 0
8 files changed, 1 deletion(-)
mode change 100644 => 100755 .husky/pre-push
delete mode 100644 .tool-versions
mode change 100644 => 100755 scripts/coverage-fix.sh
mode change 100644 => 100755 scripts/test-coveralls.sh
mode change 100644 => 100755 scripts/test.sh
mode change 100644 => 100755 src/styles/_sourcecast.scss
mode change 100644 => 100755 src/styles/_sourcereel.scss
mode change 100644 => 100755 src/styles/index.scss
diff --git a/.husky/pre-push b/.husky/pre-push
old mode 100644
new mode 100755
diff --git a/.tool-versions b/.tool-versions
deleted file mode 100644
index 1a3e61bfce..0000000000
--- a/.tool-versions
+++ /dev/null
@@ -1 +0,0 @@
-nodejs v20.18.1
diff --git a/scripts/coverage-fix.sh b/scripts/coverage-fix.sh
old mode 100644
new mode 100755
diff --git a/scripts/test-coveralls.sh b/scripts/test-coveralls.sh
old mode 100644
new mode 100755
diff --git a/scripts/test.sh b/scripts/test.sh
old mode 100644
new mode 100755
diff --git a/src/styles/_sourcecast.scss b/src/styles/_sourcecast.scss
old mode 100644
new mode 100755
diff --git a/src/styles/_sourcereel.scss b/src/styles/_sourcereel.scss
old mode 100644
new mode 100755
diff --git a/src/styles/index.scss b/src/styles/index.scss
old mode 100644
new mode 100755
From aa3bfd8be44d1c720e1bf95381d1d902f3d05d9d Mon Sep 17 00:00:00 2001
From: tkaixiang
Date: Sun, 28 Sep 2025 18:37:54 +0800
Subject: [PATCH 16/28] Edit frontend to load previous AI comments by default
---
src/commons/sagas/RequestsSaga.ts | 3 ++-
src/features/grading/GradingTypes.ts | 1 +
src/pages/academy/grading/subcomponents/GradingEditor.tsx | 2 ++
src/pages/academy/grading/subcomponents/GradingWorkspace.tsx | 1 +
4 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/commons/sagas/RequestsSaga.ts b/src/commons/sagas/RequestsSaga.ts
index 840a564ab8..f59bf6a4eb 100644
--- a/src/commons/sagas/RequestsSaga.ts
+++ b/src/commons/sagas/RequestsSaga.ts
@@ -895,7 +895,8 @@ export const getGrading = async (
xp: grade.xp,
xpAdjustment: grade.xpAdjustment,
comments: grade.comments
- }
+ },
+ ai_comments: gradingQuestion.ai_comments?.response.split('|||') || []
} as GradingQuestion;
if (gradingQuestion.grade.grader !== null) {
diff --git a/src/features/grading/GradingTypes.ts b/src/features/grading/GradingTypes.ts
index 04c1364c19..a3af86b973 100644
--- a/src/features/grading/GradingTypes.ts
+++ b/src/features/grading/GradingTypes.ts
@@ -189,6 +189,7 @@ export type GradingQuestion = {
};
gradedAt?: string;
};
+ ai_comments?: string[];
};
/**
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 542ab7ab8b..3046c8c9de 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -51,6 +51,7 @@ type Props = {
comments: string;
graderName?: string;
gradedAt?: string;
+ ai_comments?: string[];
};
const gradingEditorButtonClass = 'grading-editor-button';
@@ -141,6 +142,7 @@ const GradingEditor: React.FC = props => {
setEditorValue(props.comments);
setSelectedTab('write');
setCurrentlySaving(false);
+ setSuggestions(props.ai_comments || []);
};
/**
diff --git a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
index 1b9f4b03b3..992fb53c88 100644
--- a/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingWorkspace.tsx
@@ -289,6 +289,7 @@ const GradingWorkspace: React.FC = props => {
/* Render an editor with the xp given to the current question. */
body: (
Date: Tue, 30 Sep 2025 16:48:27 +0800
Subject: [PATCH 17/28] Moves api key, api url, llm model and course prompt to
course level
---
src/commons/application/types/SessionTypes.ts | 6 +
src/pages/academy/adminPanel/AdminPanel.tsx | 16 ++-
.../subcomponents/CourseConfigPanel.tsx | 108 +++++++++++++++---
.../grading/subcomponents/GradingEditor.tsx | 2 +-
src/pages/localStorage.ts | 1 +
src/styles/_academy.scss | 14 +++
6 files changed, 124 insertions(+), 23 deletions(-)
diff --git a/src/commons/application/types/SessionTypes.ts b/src/commons/application/types/SessionTypes.ts
index 08388daf0f..1b73f54893 100644
--- a/src/commons/application/types/SessionTypes.ts
+++ b/src/commons/application/types/SessionTypes.ts
@@ -41,6 +41,9 @@ export type SessionState = {
readonly enableStories?: boolean;
readonly enableLlmGrading?: boolean;
readonly llmApiKey?: string;
+ readonly llmModel?: string;
+ readonly llmApiUrl?: string;
+ readonly llmCourseLevelPrompt?: string;
readonly sourceChapter?: Chapter;
readonly sourceVariant?: Variant;
readonly moduleHelpText?: string;
@@ -113,6 +116,9 @@ export type CourseConfiguration = {
moduleHelpText: string;
assetsPrefix: string;
llmApiKey?: string;
+ llmModel?: string;
+ llmApiUrl?: string;
+ llmCourseLevelPrompt?: string;
};
export type AdminPanelCourseRegistration = {
diff --git a/src/pages/academy/adminPanel/AdminPanel.tsx b/src/pages/academy/adminPanel/AdminPanel.tsx
index 80921fab60..48b0f739ce 100644
--- a/src/pages/academy/adminPanel/AdminPanel.tsx
+++ b/src/pages/academy/adminPanel/AdminPanel.tsx
@@ -31,7 +31,10 @@ const defaultCourseConfig: UpdateCourseConfiguration = {
enableStories: false,
enableLlmGrading: false,
moduleHelpText: '',
- llmApiKey: ''
+ llmApiKey: '',
+ llmModel: '',
+ llmApiUrl: '',
+ llmCourseLevelPrompt: ''
};
const AdminPanel: React.FC = () => {
@@ -66,7 +69,10 @@ const AdminPanel: React.FC = () => {
enableStories: session.enableStories,
enableLlmGrading: session.enableLlmGrading,
moduleHelpText: session.moduleHelpText,
- llmApiKey: session.llmApiKey
+ llmApiKey: session.llmApiKey,
+ llmModel: session.llmModel,
+ llmApiUrl: session.llmApiUrl,
+ llmCourseLevelPrompt: session.llmCourseLevelPrompt
});
}, [
session.courseName,
@@ -77,7 +83,11 @@ const AdminPanel: React.FC = () => {
session.enableStories,
session.enableLlmGrading,
session.moduleHelpText,
- session.viewable
+ session.viewable,
+ session.llmApiKey,
+ session.llmModel,
+ session.llmApiUrl,
+ session.llmCourseLevelPrompt
]);
const tableRef = useRef(null);
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index 421b895605..9cc70e753b 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -40,7 +40,10 @@ const CourseConfigPanel: React.FC = props => {
enableStories,
enableLlmGrading,
moduleHelpText,
- llmApiKey
+ llmApiKey,
+ llmModel,
+ llmApiUrl,
+ llmCourseLevelPrompt
} = props.courseConfiguration;
const writePanel = (
@@ -135,24 +138,91 @@ const CourseConfigPanel: React.FC = props => {
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.WRITE && writePanel}
{courseHelpTextSelectedTab === CourseHelpTextEditorTab.PREVIEW && previewPanel}
-
-
- props.setCourseConfiguration({
- ...props.courseConfiguration,
- llmApiKey: e.target.value
- })
- }
- />
-
+ {enableLlmGrading && (
+
+
+
LLM Grading Configuration
+
+
+ props.setCourseConfiguration({
+ ...props.courseConfiguration,
+ llmModel: e.target.value
+ })
+ }
+ />
+
+
+
+ props.setCourseConfiguration({
+ ...props.courseConfiguration,
+ llmApiUrl: e.target.value
+ })
+ }
+ />
+
+
+
+ props.setCourseConfiguration({
+ ...props.courseConfiguration,
+ llmApiKey: e.target.value
+ })
+ }
+ />
+
+
+ Please enter the LLM prompt for this course. This is treated as the System
+ Prompt.
+
+ }
+ inline={true}
+ label="LLM Course Prompt"
+ labelFor="llmCoursePrompt"
+ >
+
+
+ )}
{!isMobileBreakpoint && }
diff --git a/src/pages/academy/grading/subcomponents/GradingEditor.tsx b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
index 3046c8c9de..3c81e54816 100644
--- a/src/pages/academy/grading/subcomponents/GradingEditor.tsx
+++ b/src/pages/academy/grading/subcomponents/GradingEditor.tsx
@@ -355,7 +355,7 @@ const GradingEditor: React.FC
= props => {
setHasClickedGenerate(true);
const resp = await getCommentSuggestions();
setHasClickedGenerate(false);
- setSuggestions(resp!.comments);
+ setSuggestions(resp ? resp.comments : []);
}}
>
Get comments
diff --git a/src/pages/localStorage.ts b/src/pages/localStorage.ts
index 66363b269c..adfd93e031 100644
--- a/src/pages/localStorage.ts
+++ b/src/pages/localStorage.ts
@@ -71,6 +71,7 @@ export const saveState = (state: OverallState) => {
enableStories: state.session.enableStories,
enableLlmGrading: state.session.enableLlmGrading,
llmApiKey: state.session.llmApiKey,
+ llmCourseLevelPrompt: state.session.llmCourseLevelPrompt,
moduleHelpText: state.session.moduleHelpText,
assetsPrefix: state.session.assetsPrefix,
assessmentConfigurations: state.session.assessmentConfigurations,
diff --git a/src/styles/_academy.scss b/src/styles/_academy.scss
index 6cdbcb3557..2624693713 100644
--- a/src/styles/_academy.scss
+++ b/src/styles/_academy.scss
@@ -364,6 +364,20 @@
}
}
+ .llm-grading-config {
+ .bp5-form-group {
+ display: flex;
+ justify-content: space-between;
+ .bp5-label {
+ margin-right: 10px;
+ width: 180px;
+ }
+ .bp5-form-content {
+ width: 100%;
+ }
+ }
+ }
+
.booleans {
display: flex;
flex-direction: column;
From 27fac47841c3180c6326cefb424875cf6a7e393f Mon Sep 17 00:00:00 2001
From: tkaixiang
Date: Tue, 7 Oct 2025 12:46:01 +0800
Subject: [PATCH 18/28] Add tooltip about formatting instructions
---
.../subcomponents/CourseConfigPanel.tsx | 34 +++++++++++++++++++
src/styles/_academy.scss | 10 +++++-
2 files changed, 43 insertions(+), 1 deletion(-)
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index 9cc70e753b..d027bc644b 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -3,12 +3,16 @@ import {
FormGroup,
H2,
H3,
+ Icon,
InputGroup,
+ Popover,
+ Position,
Switch,
Tab,
Tabs,
TextArea
} from '@blueprintjs/core';
+import { IconNames } from '@blueprintjs/icons';
import React from 'react';
import { useResponsive } from 'src/commons/utils/Hooks';
@@ -201,6 +205,36 @@ const CourseConfigPanel: React.FC = props => {
Please enter the LLM prompt for this course. This is treated as the System
Prompt.
+
+
+ This prompt is prepended to every single LLM Grading prompt as part of
+ the System Prompt
+
+
+
+ Note: You must specify the return format of the comments so that
+ comments can be parsed correctly. An example is given below:
+
+
+
+ Your output must include only the comment suggestions, separated
+ exclusively by triple pipes ("|||") with no spaces before or after the
+ pipes, and without any additional formatting, bullet points, or extra
+ text.
+
+
+ For example: "This is a good answer.|||This is a bad answer.|||This is a
+ great.
+
+
+ }
+ interactionKind="hover"
+ position={Position.TOP}
+ >
+
+
}
inline={true}
diff --git a/src/styles/_academy.scss b/src/styles/_academy.scss
index 2624693713..f47e6db63e 100644
--- a/src/styles/_academy.scss
+++ b/src/styles/_academy.scss
@@ -376,13 +376,21 @@
width: 100%;
}
}
+
+ .llm-course-prompt-icon {
+ margin-left: 5px;
+ vertical-align: text-bottom;
+ cursor: help;
+ }
}
.booleans {
display: flex;
flex-direction: column;
- justify-content: space-evenly;
align-items: flex-start;
+ label {
+ margin-top: 1.5rem;
+ }
}
}
}
From 09376be8e4f087055d4894a024862bc0546043ee Mon Sep 17 00:00:00 2001
From: tkaixiang
Date: Tue, 7 Oct 2025 12:46:19 +0800
Subject: [PATCH 19/28] style
---
.../academy/adminPanel/subcomponents/CourseConfigPanel.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index d027bc644b..72da597f3d 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -219,8 +219,8 @@ const CourseConfigPanel: React.FC = props => {
- Your output must include only the comment suggestions, separated
- exclusively by triple pipes ("|||") with no spaces before or after the
+ Your output must include only the comment suggestions, separated exc
+ lusively by triple pipes ("|||") with no spaces before or after the
pipes, and without any additional formatting, bullet points, or extra
text.
From 412bfd80fe158259cef2d8a1f5d9b92e0e9cb44a Mon Sep 17 00:00:00 2001
From: tkaixiang
Date: Tue, 7 Oct 2025 17:19:13 +0800
Subject: [PATCH 20/28] Add view "composed prompt" in the grading editor view
---
src/features/grading/GradingTypes.ts | 3 +
.../subcomponents/CourseConfigPanel.tsx | 2 +-
.../subcomponents/GradingCommentSelector.tsx | 32 ++++---
.../grading/subcomponents/GradingEditor.tsx | 96 +++++++++++++++----
.../subcomponents/GradingWorkspace.tsx | 11 +++
src/styles/_academy.scss | 18 ++++
src/styles/_workspace.scss | 2 +-
7 files changed, 132 insertions(+), 32 deletions(-)
diff --git a/src/features/grading/GradingTypes.ts b/src/features/grading/GradingTypes.ts
index a3af86b973..10f08f4e5d 100644
--- a/src/features/grading/GradingTypes.ts
+++ b/src/features/grading/GradingTypes.ts
@@ -94,6 +94,7 @@ export type GradingAssessment = {
summaryLong: string;
summaryShort: string;
title: string;
+ llm_assessment_prompt: string | null;
};
export type GradingQuery = {
@@ -189,6 +190,8 @@ export type GradingQuestion = {
};
gradedAt?: string;
};
+ autogradingResults: AutogradingResult[];
+ autoGradingStatus: string;
ai_comments?: string[];
};
diff --git a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
index 72da597f3d..4951574caf 100644
--- a/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
+++ b/src/pages/academy/adminPanel/subcomponents/CourseConfigPanel.tsx
@@ -243,7 +243,7 @@ const CourseConfigPanel: React.FC = props => {
>