Skip to content
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Clicking it opens a chat panel on the right side and sends the following context
- The **cell source code** and its **kernel language**
- Any **error output** produced by the cell (traceback included)
- **Preceding code cells and markdown cell** (up to the previous markdown cell), used as the exercise description
- An optional **reference solution** and **evaluation criteria** stored in the cell metadata (the solution is ROT13-encoded to keep it hidden from students)
- An optional **reference_solution** (multiline string) and **evaluation_criteria** (multiline string) stored in the cell metadata <!-- (the solution is ROT13-encoded to keep it hidden from students) -->

The AI replies using a configurable **system prompt** (`TUTOR.md`) that shapes its pedagogical behavior. By default the extension looks for a `TUTOR.md` file walking up from the notebook's directory to the server root, so different courses or folders can each have their own tutor persona.

Expand Down
9 changes: 4 additions & 5 deletions examples/matplotlib_exercices.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,8 @@
"metadata": {
"deletable": true,
"editable": true,
"evaluation_criteria": "Typo Correction: Correct 'np.linsapce' typo to 'np.linspace'.\nPlot Output: Ensure the Sine and Cosine waves plot is correctly rendered without runtime errors."
,
"reference_solution": "vzcbeg zngcybgyvo.clcybg nf cyg\nvzcbeg ahzcl nf ac\nk = ac.yvafcnpr(0, 2 * ac.cv, 100)\nsvt, nk = cyg.fhocybgf(svtfvmr=(8, 4))\nnk.cybg(k, ac.fva(k), ynory='fva(k)', pbybe='eblnyoyhr')\nnk.cybg(k, ac.pbf(k), ynory='pbf(k)', pbybe='gbzngb', yvarfglyr='--')\nnk.frg_gvgyr('Fvar naq Pbfvar Jnirf')\nnk.frg_kynory('k')\nnk.frg_lynory('l')\nnk.yrtraq()\nnk.tevq(Gehr, nycun=0.3)\ncyg.gvtug_ynlbhg()\ncyg.fubj()",
"evaluation_criteria": "Typo Correction: Correct 'np.linsapce' typo to 'np.linspace'.\nPlot Output: Ensure the Sine and Cosine waves plot is correctly rendered without runtime errors.",
"reference_solution": "import matplotlib.pyplot as plt\nimport numpy as np\nx = np.linspace(0, 2 * np.pi, 100)\nfig, ax = plt.subplots(figsize=(8, 4))\nax.plot(x, np.sin(x), label='sin(x)', color='royalblue')\nax.plot(x, np.cos(x), label='cos(x)', color='tomato', linestyle='--')\nax.set_title('Sine and Cosine Waves')\nax.set_xlabel('x')\nax.set_ylabel('y')\nax.legend()\nax.grid(True, alpha=0.3)\nplt.tight_layout()\nplt.show()",
"slideshow": {
"slide_type": ""
},
Expand Down Expand Up @@ -100,7 +99,7 @@
"deletable": true,
"editable": true,
"evaluation_criteria": "Bar chart creation: Uses ax.bar with categories and values.\nAesthetics & Colors: Applies a different color to each bar.\nValue Labels: Correctly loops over the bars to add text scores above each bar.",
"reference_solution": "pngrtbevrf = ['Clguba', 'WninFpevcg', 'Ehfg', 'Tb', 'Whyvn']\ninyhrf = [85, 72, 58, 45, 30]\nsvt, nk = cyg.fhocybgf(svtfvmr=(8, 4))\npbybef = ['eblnyoyhr', 'gbzngb', 'zrqvhzfrnterra', 'benatr', 'zrqvhzchecyr']\nonef = nk.one(pngrtbevrf, inyhrf, pbybe=pbybef)\nnk.frg_gvgyr('Cebtenzzvat Ynathntr Fpberf')\nnk.frg_lynory('Fpber')\nsbe one va onef:\n liny = one.trg_urvtug()\n nk.grkg(one.trg_k() + one.trg_jvqgu()/2.0, liny + 1, fge(liny), un='pragre', in='obggbz')\ncyg.gvtug_ynlbhg()\ncyg.fubj()",
"reference_solution": "categories = ['Python', 'JavaScript', 'Rust', 'Go', 'Julia']\nvalues = [85, 72, 58, 45, 30]\nfig, ax = plt.subplots(figsize=(8, 4))\ncolors = ['royalblue', 'tomato', 'mediumseagreen', 'orange', 'mediumpurple']\nbars = ax.bar(categories, values, color=colors)\nax.set_title('Programming Language Scores')\nax.set_ylabel('Score')\nfor bar in bars:\n yval = bar.get_height()\n ax.text(bar.get_x() + bar.get_width()/2.0, yval + 1, str(yval), ha='center', va='bottom')\nplt.tight_layout()\nplt.show()",
"slideshow": {
"slide_type": ""
},
Expand Down Expand Up @@ -158,7 +157,7 @@
"deletable": true,
"editable": true,
"evaluation_criteria": "Random data seed: Fails to set random seed 42, generating incorrect data coordinates.\nColorbar & Labels: Missing colorbar, title, grid, or x/y labels on the axes.\nScatter aesthetic parameters: Fails to set alpha=0.6, colormap='viridis', size mapping, or color mapping.",
"reference_solution": "ac.enaqbz.frrq(42)\na = 150\nk = ac.enaqbz.enaqa(a)\nl = ac.enaqbz.enaqa(a)\nfvmrf = ac.enaqbz.havsbez(20, 200, a)\npbybef = ac.enaqbz.enaq(a)\nsvt, nk = cyg.fhocybgf(svtfvmr=(8, 6))\nfpnggre = nk.fpnggre(k, l, p=pbybef, f=fvmrf, pznc='ivevqvf', nycun=0.6)\ncyg.pbybeone(fpnggre, nk=nk, ynory='Pbybe inyhr')\nnk.frg_gvgyr('Enaqbz Fpnggre Cybg')\nnk.frg_kynory('K inyhrf')\nnk.frg_lynory('L inyhrf')\nnk.tevq(Gehr, nycun=0.3)\ncyg.gvtug_ynlbhg()\ncyg.fubj()",
"reference_solution": "np.random.seed(42)\nn = 150\nx = np.random.randn(n)\ny = np.random.randn(n)\nsizes = np.random.uniform(20, 200, n)\ncolors = np.random.rand(n)\nfig, ax = plt.subplots(figsize=(8, 6))\nscatter = ax.scatter(x, y, c=colors, s=sizes, cmap='viridis', alpha=0.6)\nplt.colorbar(scatter, ax=ax, label='Color value')\nax.set_title('Random Scatter Plot')\nax.set_xlabel('X values')\nax.set_ylabel('Y values')\nax.grid(True, alpha=0.3)\nplt.tight_layout()\nplt.show()",
"slideshow": {
"slide_type": ""
},
Expand Down
4 changes: 2 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { infoIcon } from '@jupyterlab/ui-components';

import { clearItem, stopItem } from './components';
import { TUTOR_USER, TutorChatModel } from './model';
import { decodeRot13, isContinuous } from './utils';
import { decodeSolution, isContinuous } from './utils';

const INFO_ICON_BASE_64 = btoa(infoIcon.svgstr);

Expand Down Expand Up @@ -279,7 +279,7 @@ const plugin: JupyterFrontEndPlugin<void> = {
// Retrieve and decode reference_solution from metadata
const rawSolution = cell.model.getMetadata('reference_solution');
const referenceSolution =
typeof rawSolution === 'string' ? decodeRot13(rawSolution) : '';
typeof rawSolution === 'string' ? decodeSolution(rawSolution) : '';

// Retrieve evaluation_criteria from metadata
const evaluationCriteria = cell.model.getMetadata(
Expand Down
15 changes: 9 additions & 6 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ export function isContinuous(numbers: number[]): boolean {
}

/**
* Decodes a ROT13 encoded string.
* Decodes the reference solution.
* No-op for now, but could be a ROT13 decoder.
*/
export function decodeRot13(str: string): string {
return str.replace(/[a-zA-Z]/g, c => {
const base = c <= 'Z' ? 65 : 97;
return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base);
});
export function decodeSolution(str: string): string {
/* TODO: switch to a ROT13 encoded solution ? */
// return str.replace(/[a-zA-Z]/g, c => {
// const base = c <= 'Z' ? 65 : 97;
// return String.fromCharCode(((c.charCodeAt(0) - base + 13) % 26) + base);
// });
return str;
}
Loading