-
{selected?.title}
+
+
+ {selected?.title}
+
- {selected?.context}
+
+
+ {selected?.context}
+
+
- {selected?.objectives.map((item) => {item})}
+ {selected?.objectives.map(
+ (item) => (
+
+ {item}
+
+ )
+ )}
-
-=======
- {activeTab === 'learn' && (
-
-
-
-
-
{selected?.title}
-
- {selected?.context}
-
- {selected?.objectives.map((item) => {item})}
-
-
- {/* NLP Actions */}
-
-
-
-
-
-
- {/* Learning Form - Uses /sessions */}
-
->>>>>>> upstream/main:client/src/main.jsx
-
- {/* NLP Result Display */}
- {nlpResult && (
-
-
π€ NLP Analysis
-
-
Detected Concepts: {nlpResult.concepts?.join(', ') || 'None'}
-
Explanation: {nlpResult.explanation}
-
-
- )}
-
-
-
-
-
AI Mentor Output
-
- {!activeResult ? : }
-
-
- )}
+
+
+
- // In main.jsx, update the Learning Path tab section
-
-{activeTab === 'path' && (
-
-
-
-)}
- {activeTab === 'code' && (
-
-
-
-
-
Python Code Editor
-
-
-
-
-
-
+
+ AI Mentor Output
+
-
- )}
- {/* Dashboard Section - Always Visible */}
+ {!activeResult ? (
+
+ ) : (
+
+ )}
+
+
+
-
Learner Analytics
-
+
+
+
+
+ Learner Analytics
+
+
+
+
+
-
Roadmap
-
+
+
+
+
+ Roadmap
+
+
+
+
+
-
Recent Sessions
-
+
+
+
+
+ Recent Sessions
+
+
+
+
@@ -584,88 +572,386 @@ function EmptyResult() {
return (
-
Submit reasoning to see abstraction mapping, Python code, prompt feedback, and misconception signals.
+
+
+ Submit reasoning to see
+ abstraction mapping, Python code,
+ prompt feedback, misconception
+ signals, and adaptive AI mentoring.
+
);
}
function Result({ result }) {
+ const mentorResponse =
+ result.mentorResponse || '';
+
+ const sections = [
+ {
+ key: 'UNDERSTOOD:',
+ title: 'What you understood'
+ },
+ {
+ key: 'GAP:',
+ title: 'Your learning gap'
+ },
+ {
+ key: 'PYTHON CONNECTION:',
+ title: 'Python connection'
+ },
+ {
+ key: 'HISTORY CONNECTION:',
+ title: 'Your learning history'
+ },
+ {
+ key: 'LEARNER PROFILE INSIGHT:',
+ title: 'Your progress'
+ },
+ {
+ key: 'NEXT QUESTION:',
+ title: 'π― Next question'
+ }
+ ];
+
+ function getMentorSection(key) {
+ const start =
+ mentorResponse.indexOf(key);
+
+ if (start === -1) {
+ return '';
+ }
+
+ const contentStart =
+ start + key.length;
+
+ const nextSection =
+ sections
+ .map((section) =>
+ mentorResponse.indexOf(
+ section.key,
+ contentStart
+ )
+ )
+ .filter(
+ (index) => index !== -1
+ )
+ .sort((a, b) => a - b)[0];
+
+ return mentorResponse
+ .slice(
+ contentStart,
+ nextSection || undefined
+ )
+ .trim();
+ }
+
return (
-
{result.promptScore}Prompt maturity
+
+
+ {result.promptScore}
+
+
+
+ Prompt maturity
+
+
+
- {result.abstractionMap.map((item) => (
-
- {item.pattern}
- {item.pythonConcept}
- {item.explanation}
-
- ))}
+ {result.abstractionMap.map(
+ (item) => (
+
+
+ {item.pattern}
+
+
+
+ {item.pythonConcept}
+
+
+
+ {item.explanation}
+
+
+ )
+ )}
+
-
Generated Python
-
{result.generatedCode}
-
{result.codeExplanation}
+
+
+ Generated Python
+
+
+
+ {result.generatedCode}
+
+
+
+ {result.codeExplanation}
+
+
- {result.promptFeedback.map((item) => - {item}
)}
+ {result.promptFeedback.map(
+ (item) => (
+ -
+ {item}
+
+ )
+ )}
+
{result.misconceptions.length > 0 && (
-
Misconception watch
- {result.misconceptions.map((item) =>
{item}
)}
+
+ Misconception watch
+
+
+ {result.misconceptions.map(
+ (item) => (
+
+ {item}
+
+ )
+ )}
+
+ )}
+
+ {result.mentorResponse && (
+
+
+
+
+
+ Gemini Mentor
+
+
+
+
+ {sections.map(
+ (section) => {
+ const content =
+ getMentorSection(
+ section.key
+ );
+
+ if (!content) {
+ return null;
+ }
+
+ return (
+
+
+ {section.title}
+
+
+
+ {content}
+
+
+ );
+ }
+ )}
+
+
+ )}
+
+ {result.mentorContext
+ ?.adaptiveStrategy && (
+
+
+ Mentoring Strategy
+
+
+
+
+ Mode:
+ {' '}
+ {
+ result.mentorContext
+ .adaptiveStrategy
+ .mode
+ }
+
+
+
+ {
+ result.mentorContext
+ .adaptiveStrategy
+ .instruction ||
+ result.mentorContext
+ .adaptiveStrategy
+ .reason
+ }
+
+
+ )}
+
+ {result.mentorContext
+ ?.adaptiveDifficulty && (
+
+
+ Adaptive Difficulty
+
+
+
+
+ Level:
+ {' '}
+ {
+ result.mentorContext
+ .adaptiveDifficulty
+ .level
+ }
+
+
+
+ {
+ result.mentorContext
+ .adaptiveDifficulty
+ .reason
+ }
+
)}
);
}
-function Analytics({ analytics }) {
- const concepts = Object.entries(analytics?.conceptCounts || {});
+function Analytics({
+ analytics
+}) {
+ const concepts =
+ Object.entries(
+ analytics?.conceptCounts || {}
+ );
+
return (
- {concepts.length ? concepts.map(([name, count]) => (
-
- {name}
-
- {count}
-
- )) :
No learning sessions yet.
}
+ {concepts.length
+ ? concepts.map(
+ ([name, count]) => (
+
+
+ {name}
+
+
+
+
+
+ {count}
+
+
+ )
+ )
+ : (
+
+ No learning sessions yet.
+
+ )}
);
}
-function Roadmap({ roadmap }) {
+function Roadmap({
+ roadmap
+}) {
return (
- {roadmap.map((phase) => (
-
- {phase.phase}
-
-
{phase.title}
-
{phase.summary}
-
{phase.items.join(' / ')}
-
-
- ))}
+ {roadmap.map(
+ (phase) => (
+
+
+ {phase.phase}
+
+
+
+
+ {phase.title}
+
+
+
+ {phase.summary}
+
+
+
+ {phase.items.join(
+ ' / '
+ )}
+
+
+
+ )
+ )}
);
}
-function SessionList({ sessions }) {
+function SessionList({
+ sessions
+}) {
return (
- {sessions.length ? sessions.slice(0, 6).map((session) => (
-
-
-
- {session.scenario?.title}
- {session.masterySignals?.join(' / ') || 'In progress'}
-
-
- )) :
No sessions yet.
}
+ {sessions.length
+ ? sessions
+ .slice(0, 6)
+ .map(
+ (session) => (
+
+
+
+
+
+ {
+ session
+ .scenario
+ ?.title
+ }
+
+
+
+ {
+ session
+ .masterySignals
+ .join(' / ')
+ }
+
+
+
+ )
+ )
+ : (
+
+ No sessions yet.
+
+ )}
);
}
-createRoot(document.getElementById('root')).render(
);
\ No newline at end of file
+createRoot(
+ document.getElementById('root')
+).render(
+
+);
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/package-lock.json b/summership-26-prs/Vanisha Tiwari/server/package-lock.json
index 1d1a0b6f..10ac37f2 100644
--- a/summership-26-prs/Vanisha Tiwari/server/package-lock.json
+++ b/summership-26-prs/Vanisha Tiwari/server/package-lock.json
@@ -8,6 +8,7 @@
"name": "pybe-server",
"version": "1.0.0",
"dependencies": {
+ "@google/genai": "^2.18.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
@@ -24,15 +25,6 @@
"node": ">=18.0.0"
}
},
- "..": {
- "name": "pybe-mern-app",
- "version": "1.0.0",
- "extraneous": true,
- "dependencies": {
- "concurrently": "^9.1.2"
- },
- "devDependencies": {}
- },
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -660,9 +652,9 @@
"license": "MIT"
},
"node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
- "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -718,6 +710,30 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
+ "node_modules/@google/genai": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.18.0.tgz",
+ "integrity": "sha512-uy9gWVTAZXuA/2tld0QJl/QNiGEn4QOmfX4PiRgZQmeFQRQhojpD/Gf41SPnBJmjEnT83a+h4AdU1HHYaYvVDw==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "google-auth-library": "^10.3.0",
+ "p-retry": "^4.6.2",
+ "protobufjs": "^7.5.4",
+ "ws": "^8.18.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@modelcontextprotocol/sdk": "^1.25.2"
+ },
+ "peerDependenciesMeta": {
+ "@modelcontextprotocol/sdk": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@humanwhocodes/config-array": {
"version": "0.13.0",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
@@ -742,9 +758,9 @@
"license": "MIT"
},
"node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
- "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -854,9 +870,9 @@
}
},
"node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
- "version": "3.15.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.1.tgz",
- "integrity": "sha512-S99WuO3HlhO3XN41EtYUNl9zzXjoJx7QvmipxsJVxtCBT0YHEFy+iOJhjSvrmV12nYhWpZaM8lPHkJm0yUMbag==",
+ "version": "3.15.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.2.tgz",
+ "integrity": "sha512-6EuL879VkRA+1Cz578mKMiKvjPNEuk6+r1JaFzoSWejZmtf7xWbIyw1e3KkxlkzTIt9Taw6JBhEppG7utc1P+w==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -1254,9 +1270,9 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz",
+ "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==",
"dev": true,
"license": "MIT"
},
@@ -1272,9 +1288,9 @@
}
},
"node_modules/@mongodb-js/saslprep": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.5.0.tgz",
- "integrity": "sha512-Hk1SKJCMcCos38+vqDnZzlIo4XRj9yCGzYkjB4LcqpeXRIYfia1UWTz+VrueLxoU+uSRJzgkufxoRZg8gi52YA==",
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.5.4.tgz",
+ "integrity": "sha512-05UC0jQsjKAOuXQ0H9Ud9vUTJpZIg+n/FinpR30tI5I8pY2inTfPOZ5OF/cg3Ce/N9MoD1xhRCeOsJtuTbFYlw==",
"license": "MIT",
"dependencies": {
"sparse-bitfield": "^3.0.3"
@@ -1318,6 +1334,63 @@
"node": ">= 8"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
+ "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
+ "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
+ "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz",
+ "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/@redis/bloom": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz",
@@ -1493,15 +1566,20 @@
}
},
"node_modules/@types/node": {
- "version": "26.2.0",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-26.2.0.tgz",
- "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==",
- "dev": true,
+ "version": "26.3.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-26.3.0.tgz",
+ "integrity": "sha512-L3fgrnchriRC2ExBflb8j4uZZURHZfQsmQeyVzhjcHW4kkwVyo8/0h1B2MVzMTrYUJYu6G7EWs14hW/L9putqw==",
"license": "MIT",
"dependencies": {
"undici-types": "~8.3.0"
}
},
+ "node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "license": "MIT"
+ },
"node_modules/@types/stack-utils": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
@@ -1542,9 +1620,9 @@
"license": "MIT"
},
"node_modules/@ungap/structured-clone": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
- "integrity": "sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz",
+ "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==",
"dev": true,
"license": "ISC"
},
@@ -1602,6 +1680,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/ajv": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz",
@@ -1843,10 +1930,30 @@
"node": "18 || 20 || >=22"
}
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/baseline-browser-mapping": {
- "version": "2.11.18",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.18.tgz",
- "integrity": "sha512-1iEmLEYSiE1SeBoAfPo/Mnx3PzfzHUkDK61ASkCpuk3YXugYLH5DYK1SzqV55F8FMI6s0F+/tCP7Polz1QRjxw==",
+ "version": "2.11.23",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.23.tgz",
+ "integrity": "sha512-le521dGVfxM7yRX0EikCoSz+rOK+hHzdDt/E7mG1jOJB/6WAAUuwVroLwaB7ApaUsz5Q0kFlDXLSA9MheUIfRQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -1870,6 +1977,15 @@
"version": "5.1.2",
"license": "MIT"
},
+ "node_modules/bignumber.js": {
+ "version": "9.3.1",
+ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz",
+ "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"dev": true,
@@ -1926,9 +2042,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.28.8",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
- "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
+ "version": "4.28.9",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.9.tgz",
+ "integrity": "sha512-EWazOblFYUvlGZcfGhPUPmYh3nikUxBVb+y9MJun5f3hBi812X+8MSQTujLBtgK3cf51fJWbWfOjyeO954d+Eg==",
"dev": true,
"funding": [
{
@@ -1946,11 +2062,11 @@
],
"license": "MIT",
"dependencies": {
- "baseline-browser-mapping": "^2.11.12",
- "caniuse-lite": "^1.0.30001809",
- "electron-to-chromium": "^1.5.402",
- "node-releases": "^2.0.53",
- "update-browserslist-db": "^1.3.0"
+ "baseline-browser-mapping": "^2.11.20",
+ "caniuse-lite": "^1.0.30001810",
+ "electron-to-chromium": "^1.5.420",
+ "node-releases": "^2.0.54",
+ "update-browserslist-db": "^1.3.2"
},
"bin": {
"browserslist": "cli.js"
@@ -1978,6 +2094,12 @@
"node": ">=16.20.1"
}
},
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -2038,9 +2160,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001809",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz",
- "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==",
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
"dev": true,
"funding": [
{
@@ -2260,8 +2382,6 @@
},
"node_modules/cors": {
"version": "2.8.6",
- "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
- "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
@@ -2312,6 +2432,15 @@
"node": ">= 8"
}
},
+ "node_modules/data-uri-to-buffer": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
+ "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
"node_modules/debug": {
"version": "2.6.9",
"license": "MIT",
@@ -2401,8 +2530,6 @@
},
"node_modules/dotenv": {
"version": "16.6.1",
- "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
- "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
"license": "BSD-2-Clause",
"engines": {
"node": ">=12"
@@ -2423,14 +2550,23 @@
"node": ">= 0.4"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.412",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.412.tgz",
- "integrity": "sha512-z4rMe3esBzlzovKHj4gxJnsCGZRK5l4baUvm+gCGJBPE+gsyUMKsuU9tnEUtI1dOebXz1ytAPGjvXhmQ7rIPwA==",
+ "version": "1.5.428",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.428.tgz",
+ "integrity": "sha512-1JxbaFJj1bRKurj1uY3l4xxpU9kOUAUjcIgApj0qu1Pao5GhoIWI8iL0BeMYJ2njig1hBx0A7eKD9VGjH9wlHw==",
"dev": true,
"license": "ISC"
},
@@ -2617,9 +2753,9 @@
"license": "MIT"
},
"node_modules/eslint/node_modules/brace-expansion": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
- "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -2815,8 +2951,6 @@
},
"node_modules/express": {
"version": "4.22.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
- "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
@@ -2859,6 +2993,12 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2881,9 +3021,9 @@
"license": "MIT"
},
"node_modules/fastq": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
- "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz",
+ "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -2900,6 +3040,29 @@
"bser": "2.1.1"
}
},
+ "node_modules/fetch-blob": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz",
+ "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "node-domexception": "^1.0.0",
+ "web-streams-polyfill": "^3.0.3"
+ },
+ "engines": {
+ "node": "^12.20 || >= 14.13"
+ }
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -2979,6 +3142,18 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/formdata-polyfill": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
+ "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==",
+ "license": "MIT",
+ "dependencies": {
+ "fetch-blob": "^3.1.2"
+ },
+ "engines": {
+ "node": ">=12.20.0"
+ }
+ },
"node_modules/forwarded": {
"version": "0.2.0",
"license": "MIT",
@@ -3022,6 +3197,34 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gaxios": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.3.1.tgz",
+ "integrity": "sha512-kB3rzJV7d9juLZh8/56QTXCwQfxyhdOMdyYk1HdQKFtF8TJTDTZQJtixWIwXdE9Jji91mC41DUNpjleo4L4eAQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "extend": "^3.0.2",
+ "https-proxy-agent": "^7.0.1",
+ "node-fetch": "^3.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/gcp-metadata": {
+ "version": "8.1.2",
+ "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz",
+ "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "gaxios": "^7.0.0",
+ "google-logging-utils": "^1.0.0",
+ "json-bigint": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/generic-pool": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz",
@@ -3148,9 +3351,9 @@
"license": "MIT"
},
"node_modules/glob/node_modules/brace-expansion": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
- "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -3187,6 +3390,32 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/google-auth-library": {
+ "version": "10.9.1",
+ "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.9.1.tgz",
+ "integrity": "sha512-i1ydyHrqcIxXkWh/uBmVkzCvIuq5yiK2ATndIe5XxKholrG/MTYP9xGYka4sQhrbIAgGjL2B6NOE7rFaiF3fXw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "base64-js": "^1.3.0",
+ "ecdsa-sig-formatter": "^1.0.11",
+ "gaxios": "^7.1.4",
+ "gcp-metadata": "8.1.2",
+ "google-logging-utils": "1.1.3",
+ "jws": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/google-logging-utils": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz",
+ "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/gopd": {
"version": "1.2.0",
"license": "MIT",
@@ -3264,6 +3493,42 @@
"url": "https://opencollective.com/express"
}
},
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent/node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/https-proxy-agent/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
"node_modules/human-signals": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
@@ -3464,7 +3729,7 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
"integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -4200,9 +4465,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz",
- "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==",
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz",
+ "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==",
"dev": true,
"funding": [
{
@@ -4235,6 +4500,15 @@
"node": ">=6"
}
},
+ "node_modules/json-bigint": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+ "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "bignumber.js": "^9.0.0"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -4276,6 +4550,27 @@
"node": ">=6"
}
},
+ "node_modules/jwa": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
+ "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
+ "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^2.0.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kareem": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz",
@@ -4359,6 +4654,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/long": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+ "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+ "license": "Apache-2.0"
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -4510,7 +4811,39 @@
"url": "https://github.com/sponsors/isaacs"
}
},
- "node_modules/mongodb": {
+ "node_modules/mongodb-connection-string-url": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
+ "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/whatwg-url": "^11.0.2",
+ "whatwg-url": "^14.1.0 || ^13.0.0"
+ }
+ },
+ "node_modules/mongoose": {
+ "version": "8.24.4",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.4.tgz",
+ "integrity": "sha512-DoV8hYy5ssoh/j99rhYzeS2BLLcLJZ+EGQYfe/Ln+yMorBy0wgKdlwJBj59zJ1N0MnEKQypbeqUYFB0FVThmYg==",
+ "license": "MIT",
+ "dependencies": {
+ "bson": "^6.10.4",
+ "kareem": "2.6.3",
+ "mongodb": "~6.20.0",
+ "mpath": "0.9.0",
+ "mquery": "5.0.0",
+ "ms": "2.1.3",
+ "sift": "17.1.3"
+ },
+ "engines": {
+ "node": ">=16.20.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mongoose"
+ }
+ },
+ "node_modules/mongoose/node_modules/mongodb": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.20.0.tgz",
"integrity": "sha512-Tl6MEIU3K4Rq3TSHd+sZQqRBoGlFsOgNrH5ltAcFBV62Re3Fd+FcaVf8uSEQFOJ51SDowDVttBTONMfoYWrWlQ==",
@@ -4556,38 +4889,6 @@
}
}
},
- "node_modules/mongodb-connection-string-url": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz",
- "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@types/whatwg-url": "^11.0.2",
- "whatwg-url": "^14.1.0 || ^13.0.0"
- }
- },
- "node_modules/mongoose": {
- "version": "8.24.4",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.24.4.tgz",
- "integrity": "sha512-DoV8hYy5ssoh/j99rhYzeS2BLLcLJZ+EGQYfe/Ln+yMorBy0wgKdlwJBj59zJ1N0MnEKQypbeqUYFB0FVThmYg==",
- "license": "MIT",
- "dependencies": {
- "bson": "^6.10.4",
- "kareem": "2.6.3",
- "mongodb": "~6.20.0",
- "mpath": "0.9.0",
- "mquery": "5.0.0",
- "ms": "2.1.3",
- "sift": "17.1.3"
- },
- "engines": {
- "node": ">=16.20.1"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mongoose"
- }
- },
"node_modules/mongoose/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -4596,8 +4897,6 @@
},
"node_modules/morgan": {
"version": "1.11.0",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.11.0.tgz",
- "integrity": "sha512-zSkVu3t18r39pw4ixfBKvfZi3y2UOqr7d4WYwcj3m8nXpEQK4rPO6GLzs/CExoRgmX3y9EjmmcXqv6jq0SK46g==",
"license": "MIT",
"dependencies": {
"basic-auth": "~2.0.1",
@@ -4694,14 +4993,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/natural/node_modules/sylvester": {
- "version": "0.0.12",
- "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz",
- "integrity": "sha512-SzRP5LQ6Ts2G5NyAa/jg16s8e3R7rfdFjizy1zeoecYWw+nGL+YA1xZvW/+iJmidBGSdLkuvdwTYEyJEb+EiUw==",
- "engines": {
- "node": ">=0.2.6"
- }
- },
"node_modules/negotiator": {
"version": "0.6.3",
"license": "MIT",
@@ -4709,6 +5000,44 @@
"node": ">= 0.6"
}
},
+ "node_modules/node-domexception": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz",
+ "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==",
+ "deprecated": "Use your platform's native DOMException instead",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/jimmywarting"
+ },
+ {
+ "type": "github",
+ "url": "https://paypal.me/jimmywarting"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.5.0"
+ }
+ },
+ "node_modules/node-fetch": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz",
+ "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==",
+ "license": "MIT",
+ "dependencies": {
+ "data-uri-to-buffer": "^4.0.0",
+ "fetch-blob": "^3.1.4",
+ "formdata-polyfill": "^4.0.10"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/node-fetch"
+ }
+ },
"node_modules/node-int64": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
@@ -4717,9 +5046,9 @@
"license": "MIT"
},
"node_modules/node-releases": {
- "version": "2.0.53",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.53.tgz",
- "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==",
+ "version": "2.0.55",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.55.tgz",
+ "integrity": "sha512-mIrE/Cw9y+9Au6dS5vDKDhQza9YvG6w+ZrS6X+ZzA7yFW/soAeaups4Qzn1bL6g5FVy8WtP79+0j82oPIbqRjQ==",
"dev": true,
"license": "MIT",
"engines": {
@@ -4728,8 +5057,6 @@
},
"node_modules/nodemon": {
"version": "3.1.14",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz",
- "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4907,6 +5234,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -5274,6 +5614,29 @@
"node": ">= 6"
}
},
+ "node_modules/protobufjs": {
+ "version": "7.6.5",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz",
+ "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==",
+ "hasInstallScript": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.5",
+ "@protobufjs/eventemitter": "^1.1.1",
+ "@protobufjs/fetch": "^1.1.1",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.1",
+ "@types/node": ">=13.7.0",
+ "long": "^5.3.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"license": "MIT",
@@ -5480,6 +5843,15 @@
"node": ">=10"
}
},
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -5930,9 +6302,9 @@
}
},
"node_modules/sylvester": {
- "version": "0.0.21",
- "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.21.tgz",
- "integrity": "sha512-yUT0ukFkFEt4nb+NY+n2ag51aS/u9UHXoZw+A4jgD77/jzZsBoSDHuqysrVCBC4CYR4TYvUJq54ONpXgDBH8tA==",
+ "version": "0.0.12",
+ "resolved": "https://registry.npmjs.org/sylvester/-/sylvester-0.0.12.tgz",
+ "integrity": "sha512-SzRP5LQ6Ts2G5NyAa/jg16s8e3R7rfdFjizy1zeoecYWw+nGL+YA1xZvW/+iJmidBGSdLkuvdwTYEyJEb+EiUw==",
"engines": {
"node": ">=0.2.6"
}
@@ -5960,9 +6332,9 @@
"license": "MIT"
},
"node_modules/test-exclude/node_modules/brace-expansion": {
- "version": "1.1.18",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz",
- "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==",
+ "version": "1.1.21",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz",
+ "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -6097,7 +6469,6 @@
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
"integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/unpipe": {
@@ -6108,9 +6479,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz",
- "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz",
+ "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==",
"dev": true,
"funding": [
{
@@ -6201,6 +6572,15 @@
"makeerror": "1.0.12"
}
},
+ "node_modules/web-streams-polyfill": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz",
+ "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/webidl-conversions": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
@@ -6297,6 +6677,27 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
}
},
+ "node_modules/ws": {
+ "version": "8.21.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz",
+ "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/summership-26-prs/Vanisha Tiwari/server/package.json b/summership-26-prs/Vanisha Tiwari/server/package.json
index 7b074dea..158d68a1 100644
--- a/summership-26-prs/Vanisha Tiwari/server/package.json
+++ b/summership-26-prs/Vanisha Tiwari/server/package.json
@@ -11,6 +11,7 @@
"clean": "node src/scripts/clean.js"
},
"dependencies": {
+ "@google/genai": "^2.18.0",
"cors": "^2.8.5",
"dotenv": "^16.0.3",
"express": "^4.18.2",
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/index.js b/summership-26-prs/Vanisha Tiwari/server/src/index.js
index e569a038..5143dc70 100644
--- a/summership-26-prs/Vanisha Tiwari/server/src/index.js
+++ b/summership-26-prs/Vanisha Tiwari/server/src/index.js
@@ -1,86 +1,30 @@
+require('dotenv').config();
+
const express = require('express');
const cors = require('cors');
const morgan = require('morgan');
-
-// Import routes
const scenarioRoutes = require('./routes/scenarios');
const sessionRoutes = require('./routes/sessions');
const analyticsRoutes = require('./routes/analytics');
const roadmapRoutes = require('./routes/roadmap');
-const nlpRoutes = require('./routes/nlp');
-const learningPathRoutes = require('./routes/learningPath'); // β
Make sure this exists
-const pythonRoutes = require('./routes/python');
-require('dotenv').config();
const app = express();
const port = process.env.PORT || 5000;
-// Middleware
-app.use(cors({
- origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173',
- methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
- allowedHeaders: ['Content-Type', 'Authorization']
-}));
+app.use(cors({ origin: process.env.CLIENT_ORIGIN || 'http://localhost:5173' }));
app.use(express.json());
-app.use(express.urlencoded({ extended: true }));
app.use(morgan('dev'));
-// Health check
-app.get('/api/health', (_req, res) => res.json({
- ok: true,
- product: 'PyBe',
- version: '1.0.0'
-}));
-
-// API Routes - ALL MUST BE REGISTERED
+app.get('/api/health', (_req, res) => res.json({ ok: true, product: 'PyBe' }));
app.use('/api/scenarios', scenarioRoutes);
app.use('/api/sessions', sessionRoutes);
app.use('/api/analytics', analyticsRoutes);
app.use('/api/roadmap', roadmapRoutes);
-app.use('/api/nlp', nlpRoutes);
-app.use('/api/learning-path', learningPathRoutes); // β
This must be here!
-app.use('/api/python', pythonRoutes);
-
-// 404 handler
-app.use((_req, res) => {
- res.status(404).json({
- message: 'Route not found',
- availableRoutes: [
- '/api/health',
- '/api/scenarios',
- '/api/sessions',
- '/api/analytics',
- '/api/roadmap',
- '/api/nlp',
- '/api/learning-path', // β
Should show here
- '/api/python'
- ]
- });
-});
-// Error handling middleware
app.use((error, _req, res, _next) => {
- console.error('Error:', error);
- res.status(error.status || 500).json({
- message: error.message || 'Internal server error',
- ...(process.env.NODE_ENV === 'development' && { stack: error.stack })
- });
+ console.error(error);
+ res.status(error.status || 500).json({ message: error.message || 'Server error' });
});
-app.listen(port, () => {
- console.log(`π PyBe API running on http://localhost:${port}`);
- console.log(`π Available endpoints:`);
- console.log(` - GET /api/health`);
- console.log(` - GET /api/scenarios`);
- console.log(` - POST /api/sessions`);
- console.log(` - GET /api/analytics`);
- console.log(` - GET /api/roadmap`);
- console.log(` - POST /api/nlp/convert-reasoning`);
- console.log(` - POST /api/nlp/validate-code`);
- console.log(` - POST /api/nlp/extract-concepts`);
- console.log(` - POST /api/nlp/generate-code`);
- console.log(` - GET /api/learning-path`); // β
Should show here
- console.log(` - POST /api/python/execute`);
- console.log(` - POST /api/python/validate`);
-});
\ No newline at end of file
+app.listen(port, () => console.log(`PyBe API running on http://localhost:${port}`));
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/routes/sessions.js b/summership-26-prs/Vanisha Tiwari/server/src/routes/sessions.js
index 05227391..b4daf940 100644
--- a/summership-26-prs/Vanisha Tiwari/server/src/routes/sessions.js
+++ b/summership-26-prs/Vanisha Tiwari/server/src/routes/sessions.js
@@ -1,12 +1,20 @@
const express = require('express');
+
const store = require('../data/store');
const engine = require('../services/learningEngine');
+const mentorContext = require('../services/mentorContext');
+const learnerContext = require('../services/learnerContext');
+const learnerProfile = require('../services/learnerProfile');
+const adaptiveStrategy = require('../services/adaptiveStrategy');
+const adaptiveDifficulty = require('../services/adaptiveDifficulty');
+const aiMentor = require('../services/aiMentor');
const router = express.Router();
router.get('/', async (_req, res, next) => {
try {
const sessions = await store.listSessions();
+
res.json(sessions.slice(0, 30));
} catch (error) {
next(error);
@@ -15,28 +23,206 @@ router.get('/', async (_req, res, next) => {
router.post('/', async (req, res, next) => {
try {
- const scenario = await store.getScenario(req.body.scenarioId);
- if (!scenario) return res.status(404).json({ message: 'Scenario not found' });
-
- const abstractionMap = engine.mapReasoning(req.body.reasoning);
- const generatedCode = engine.generateCode(scenario, abstractionMap);
- const prompt = engine.evaluatePrompt(req.body.promptText);
- const session = await store.addSession({
- learnerName: req.body.learnerName || 'Guest learner',
- scenario: scenario._id,
- reasoning: req.body.reasoning,
- promptText: req.body.promptText || '',
- abstractionMap,
- generatedCode,
- codeExplanation: engine.explainCode(abstractionMap),
- promptScore: prompt.score,
- promptFeedback: prompt.feedback,
- reflection: req.body.reflection || '',
- misconceptions: engine.detectMisconceptions(req.body.reasoning),
- masterySignals: engine.masterySignals(abstractionMap, prompt.score)
- });
- res.status(201).json(session);
+ // --------------------------------
+ // 1. Get scenario
+ // --------------------------------
+
+ const scenario =
+ await store.getScenario(
+ req.body.scenarioId
+ );
+
+ if (!scenario) {
+ return res
+ .status(404)
+ .json({
+ message: 'Scenario not found'
+ });
+ }
+
+ // --------------------------------
+ // 2. Analyze current learner input
+ // --------------------------------
+
+ const abstractionMap =
+ engine.mapReasoning(
+ req.body.reasoning,
+ scenario
+ );
+
+ const generatedCode =
+ engine.generateCode(
+ scenario,
+ abstractionMap
+ );
+
+ const prompt =
+ engine.evaluatePrompt(
+ req.body.promptText
+ );
+
+ const misconceptions =
+ engine.detectMisconceptions(
+ req.body.reasoning
+ );
+
+ const masterySignals =
+ engine.masterySignals(
+ abstractionMap,
+ prompt.score
+ );
+
+ // --------------------------------
+ // 3. Retrieve previous sessions
+ // --------------------------------
+
+ const previousSessions =
+ await store.listSessions();
+
+ const currentConcepts =
+ abstractionMap.map(
+ (item) =>
+ item.pythonConcept
+ );
+
+ const relevantHistory =
+ learnerContext.buildLearnerHistory(
+ previousSessions,
+ scenario._id,
+ currentConcepts,
+ misconceptions
+ );
+
+ // --------------------------------
+ // 4. Build learner profile
+ // --------------------------------
+
+ const profile =
+ learnerProfile.buildLearnerProfile(
+ previousSessions
+ );
+
+ // --------------------------------
+ // 5. Choose mentoring strategy
+ // --------------------------------
+
+ const strategy =
+ adaptiveStrategy.chooseStrategy(
+ profile,
+ misconceptions
+ );
+
+ // --------------------------------
+ // 6. Choose adaptive difficulty
+ // --------------------------------
+
+ const difficulty =
+ adaptiveDifficulty.chooseDifficulty({
+ profile,
+ promptScore: prompt.score,
+ misconceptions,
+ strategy
+ });
+
+ // --------------------------------
+ // 7. Build mentor context
+ // --------------------------------
+
+ const context =
+ mentorContext.buildMentorContext({
+ scenario,
+ reasoning:
+ req.body.reasoning,
+ abstractionMap,
+ generatedCode,
+ promptScore:
+ prompt.score,
+ promptFeedback:
+ prompt.feedback,
+ reflection:
+ req.body.reflection,
+ misconceptions,
+ masterySignals
+ });
+
+ context.learnerHistory =
+ relevantHistory;
+
+ context.learnerProfile =
+ profile;
+
+ context.adaptiveStrategy =
+ strategy;
+
+ context.adaptiveDifficulty =
+ difficulty;
+
+ // --------------------------------
+ // 8. Generate AI mentor response
+ // --------------------------------
+
+ const mentorResponse =
+ await aiMentor.generateMentorResponse(
+ context
+ );
+
+ // --------------------------------
+ // 9. Save session
+ // --------------------------------
+
+ const session =
+ await store.addSession({
+ learnerName:
+ req.body.learnerName ||
+ 'Guest learner',
+
+ scenario:
+ scenario._id,
+
+ reasoning:
+ req.body.reasoning,
+
+ promptText:
+ req.body.promptText || '',
+
+ abstractionMap,
+
+ generatedCode,
+
+ codeExplanation:
+ engine.explainCode(
+ abstractionMap
+ ),
+
+ promptScore:
+ prompt.score,
+
+ promptFeedback:
+ prompt.feedback,
+
+ reflection:
+ req.body.reflection || '',
+
+ misconceptions,
+
+ masterySignals,
+
+ mentorContext:
+ context,
+
+ mentorResponse
+ });
+
+ res
+ .status(201)
+ .json(session);
+
} catch (error) {
+ console.error(
+ 'Session processing error:',
+ error
+ );
+
next(error);
}
});
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveDifficulty.js b/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveDifficulty.js
new file mode 100644
index 00000000..d679e15e
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveDifficulty.js
@@ -0,0 +1,57 @@
+function chooseDifficulty({
+ profile = {},
+ promptScore = 0,
+ misconceptions = [],
+ strategy = {}
+}) {
+ // Repeated or current misconceptions
+ // should keep the learner at foundation level.
+ if (
+ strategy.mode === 'REMEDIATE' ||
+ misconceptions.length > 0
+ ) {
+ return {
+ level: 'FOUNDATION',
+ reason:
+ 'The learner needs stronger understanding of the current concept before increasing difficulty.'
+ };
+ }
+
+ // Strong and consistent performance
+ // allows the mentor to increase difficulty.
+ if (
+ strategy.mode === 'CHALLENGE' &&
+ promptScore >= 80 &&
+ (profile.averagePromptScore || 0) >= 70
+ ) {
+ return {
+ level: 'ADVANCED',
+ reason:
+ 'The learner is consistently demonstrating strong performance.'
+ };
+ }
+
+ // Moderate performance
+ // gets an application-level question.
+ if (
+ promptScore >= 60 ||
+ (profile.averagePromptScore || 0) >= 60
+ ) {
+ return {
+ level: 'INTERMEDIATE',
+ reason:
+ 'The learner has enough understanding to apply the concept in a new situation.'
+ };
+ }
+
+ // Default
+ return {
+ level: 'FOUNDATION',
+ reason:
+ 'Build the core concept before increasing complexity.'
+ };
+}
+
+module.exports = {
+ chooseDifficulty
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveStrategy.js b/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveStrategy.js
new file mode 100644
index 00000000..0d78ba08
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/adaptiveStrategy.js
@@ -0,0 +1,48 @@
+function chooseStrategy(profile, misconceptions = []) {
+ const repeated =
+ profile.repeatedMisconceptions || [];
+
+ // Learner repeatedly makes the same mistake
+ if (
+ repeated.length > 0 ||
+ misconceptions.length > 0
+ ) {
+ return {
+ mode: 'REMEDIATE',
+ reason:
+ 'Focus on correcting a misconception before introducing more complexity.'
+ };
+ }
+
+ // Learner consistently writes strong prompts
+ if (
+ profile.averagePromptScore >= 80
+ ) {
+ return {
+ mode: 'CHALLENGE',
+ reason:
+ 'The learner is demonstrating strong prompt quality and can handle a deeper question.'
+ };
+ }
+
+ // No meaningful history yet
+ if (
+ profile.sessionsAnalyzed === 0
+ ) {
+ return {
+ mode: 'EXPLAIN',
+ reason:
+ 'Introduce the concept clearly before increasing difficulty.'
+ };
+ }
+
+ return {
+ mode: 'ENCOURAGE',
+ reason:
+ 'Reinforce the learnerβs progress and ask a slightly deeper question.'
+ };
+}
+
+module.exports = {
+ chooseStrategy
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/aiMentor.js b/summership-26-prs/Vanisha Tiwari/server/src/services/aiMentor.js
new file mode 100644
index 00000000..a541eec6
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/aiMentor.js
@@ -0,0 +1,316 @@
+const {
+ GoogleGenAI
+} = require('@google/genai');
+
+const {
+ validateMentorResponse
+} = require('./mentorValidator');
+
+const ai = new GoogleGenAI({
+ apiKey: process.env.GEMINI_API_KEY
+});
+
+async function generateMentorResponse(
+ mentorContext
+) {
+ const history =
+ mentorContext.learnerHistory || [];
+
+ const profile =
+ mentorContext.learnerProfile || {};
+
+ const strategy =
+ mentorContext.adaptiveStrategy || {};
+
+ const difficulty =
+ mentorContext.adaptiveDifficulty || {};
+
+ const misconceptions =
+ mentorContext.misconceptions || [];
+
+ const prompt = `
+You are PyBe, an adaptive Python learning mentor.
+
+Analyze the learner's current reasoning using:
+
+1. Current session
+2. Relevant previous sessions
+3. Learner profile
+4. Adaptive mentoring strategy
+5. Adaptive difficulty level
+
+CURRENT SCENARIO:
+${JSON.stringify(
+ mentorContext.scenario,
+ null,
+ 2
+)}
+
+CURRENT LEARNER REASONING:
+${mentorContext.learnerReasoning || '(none)'}
+
+CURRENT PYTHON CONCEPTS:
+${JSON.stringify(
+ mentorContext.abstractionMap || [],
+ null,
+ 2
+)}
+
+CURRENT MISCONCEPTIONS:
+${JSON.stringify(
+ misconceptions,
+ null,
+ 2
+)}
+
+CURRENT PROMPT SCORE:
+${mentorContext.promptEvaluation?.score || 0}
+
+RELEVANT PREVIOUS LEARNER SESSIONS:
+${JSON.stringify(
+ history,
+ null,
+ 2
+)}
+
+LEARNER PROFILE:
+${JSON.stringify(
+ profile,
+ null,
+ 2
+)}
+
+ADAPTIVE MENTORING STRATEGY:
+${JSON.stringify(
+ strategy,
+ null,
+ 2
+)}
+
+ADAPTIVE DIFFICULTY:
+${JSON.stringify(
+ difficulty,
+ null,
+ 2
+)}
+
+ADAPTIVE STRATEGY RULES:
+
+REMEDIATE:
+Focus on correcting a recurring misconception
+and use a corrective question.
+
+EXPLAIN:
+Clarify the missing idea with a simple
+explanation before asking the next question.
+
+CHALLENGE:
+The learner is performing strongly.
+Ask a deeper application question instead
+of repeating a basic explanation.
+
+ENCOURAGE:
+Reinforce supported progress and ask a
+slightly more advanced question.
+
+ADAPTIVE DIFFICULTY RULES:
+
+FOUNDATION:
+Focus on core understanding and use a
+simple question.
+
+INTERMEDIATE:
+Ask the learner to apply the concept in a
+slightly different situation.
+
+ADVANCED:
+Ask a deeper application or reasoning question.
+
+The selected strategy and difficulty MUST
+directly control the NEXT QUESTION.
+
+QUESTION RULES:
+
+REMEDIATE + FOUNDATION:
+Ask a corrective question about the exact
+misconception. Do not introduce a new concept.
+
+EXPLAIN + FOUNDATION:
+Ask a simple concept-check question using
+the current scenario.
+
+EXPLAIN + INTERMEDIATE:
+Ask the learner to apply the current concept
+to a slightly different input.
+
+ENCOURAGE + FOUNDATION:
+Ask a simple question that confirms the learner
+understands the current concept.
+
+ENCOURAGE + INTERMEDIATE:
+Ask a slightly deeper application question.
+
+CHALLENGE + INTERMEDIATE:
+Ask a new application or comparison question.
+
+CHALLENGE + ADVANCED:
+Ask a deeper reasoning question involving
+edge cases, alternative approaches, or tradeoffs.
+
+Never ask a question unrelated to the current
+scenario.
+
+Never introduce a new Python concept unless
+the current scenario requires it.
+
+The NEXT QUESTION must be answerable using
+the concepts in the current scenario.
+
+GENERAL INSTRUCTIONS:
+
+- Identify what the learner currently understands.
+- Identify ONE specific gap or misconception.
+- Connect it to the relevant Python concept.
+- Use previous sessions only when supported by the data.
+- Use the learner profile to personalize the response.
+- If a misconception appears repeatedly, target it.
+- If there is evidence of improvement, mention it.
+- Never invent learner history.
+- Never claim improvement without evidence.
+- Do not confuse concepts between scenarios.
+- Follow the selected adaptive strategy.
+- Follow the selected adaptive difficulty.
+- Ask exactly ONE question that targets the learner's current gap.
+- Do not give away the answer.
+- Keep the response under 180 words.
+- Do not add extra headings.
+- Do not change the required format.
+
+Use exactly this format:
+
+UNDERSTOOD:
+...
+
+GAP:
+...
+
+PYTHON CONNECTION:
+...
+
+HISTORY CONNECTION:
+...
+
+LEARNER PROFILE INSIGHT:
+...
+
+NEXT QUESTION:
+...
+`;
+
+ const models = [
+ 'gemini-3.7-flash',
+ 'gemini-3.6-flash'
+ ];
+
+ let lastError;
+
+ for (const model of models) {
+ try {
+ console.log(
+ `Trying Gemini model: ${model}`
+ );
+
+ const response =
+ await ai.models.generateContent({
+ model,
+ contents: prompt
+ });
+
+ console.log(
+ `Gemini response received from: ${model}`
+ );
+
+ const mentorResponse =
+ response.text;
+
+ if (
+ validateMentorResponse(
+ mentorResponse
+ )
+ ) {
+ return mentorResponse;
+ }
+
+ console.error(
+ `Gemini returned an invalid mentor response from ${model}`
+ );
+
+ } catch (error) {
+ lastError = error;
+
+ console.error(
+ `Gemini model ${model} failed:`,
+ error.status,
+ error.message
+ );
+ }
+ }
+
+ console.error(
+ 'All Gemini mentor models failed:',
+ lastError
+ );
+
+ const fallbackQuestion =
+ strategy.mode === 'REMEDIATE'
+ ? 'What rule would you use to decide when the current longest pencil should be replaced?'
+ : strategy.mode === 'CHALLENGE'
+ ? 'What would happen if two pencils had the same maximum length?'
+ : strategy.mode === 'EXPLAIN'
+ ? 'Why do you need to compare each pencil with the current longest pencil?'
+ : 'What should the program do when it finds a pencil longer than the current longest one?';
+
+ const fallbackGap =
+ misconceptions.length > 0
+ ? misconceptions[0]
+ : 'The reasoning needs a clearer decision rule.';
+
+ const conceptNames =
+ (mentorContext.abstractionMap || [])
+ .map(
+ (item) =>
+ item.pythonConcept
+ )
+ .join(', ') ||
+ 'the current Python concept';
+
+ return `
+UNDERSTOOD:
+You identified the main Python idea needed to solve the current scenario.
+
+GAP:
+${fallbackGap}
+
+PYTHON CONNECTION:
+Your reasoning maps to ${conceptNames}.
+
+HISTORY CONNECTION:
+${
+ history.length > 0
+ ? 'Previous relevant learner sessions were considered.'
+ : 'No strongly relevant previous session was available for this scenario.'
+}
+
+LEARNER PROFILE INSIGHT:
+The learner has an average prompt score of ${
+ profile.averagePromptScore || 0
+ } based on available sessions.
+
+NEXT QUESTION:
+${fallbackQuestion}
+`;
+}
+
+module.exports = {
+ generateMentorResponse
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/learnerContext.js b/summership-26-prs/Vanisha Tiwari/server/src/services/learnerContext.js
new file mode 100644
index 00000000..d5a6b936
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/learnerContext.js
@@ -0,0 +1,87 @@
+function buildLearnerHistory(
+ sessions = [],
+ currentScenarioId,
+ currentConcepts = [],
+ currentMisconceptions = []
+) {
+ const history = sessions
+ .filter((session) => {
+ const scenarioId =
+ typeof session.scenario === 'object'
+ ? session.scenario._id
+ : session.scenario;
+
+ return scenarioId !== currentScenarioId;
+ })
+ .map((session) => {
+ const concepts =
+ session.abstractionMap?.map(
+ (item) => item.pythonConcept
+ ) || [];
+
+ const misconceptions =
+ session.misconceptions || [];
+
+ const conceptMatch = concepts.some(
+ (concept) =>
+ currentConcepts.includes(concept)
+ );
+
+ const misconceptionMatch =
+ misconceptions.some((misconception) =>
+ currentMisconceptions.includes(
+ misconception
+ )
+ );
+
+ let relevanceScore = 0;
+
+ if (conceptMatch) {
+ relevanceScore += 2;
+ }
+
+ if (misconceptionMatch) {
+ relevanceScore += 3;
+ }
+
+ return {
+ relevanceScore,
+
+ scenario:
+ typeof session.scenario === 'object'
+ ? session.scenario.title
+ : 'Previous scenario',
+
+ concepts,
+
+ promptScore:
+ session.promptScore || 0,
+
+ misconceptions,
+
+ reasoning:
+ session.reasoning || ''
+ };
+ });
+
+ return history
+ .filter(
+ (session) => session.relevanceScore > 0
+ )
+ .sort(
+ (a, b) =>
+ b.relevanceScore -
+ a.relevanceScore
+ )
+ .slice(0, 5)
+ .map(
+ ({
+ relevanceScore,
+ ...session
+ }) => session
+ );
+}
+
+module.exports = {
+ buildLearnerHistory
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/learnerProfile.js b/summership-26-prs/Vanisha Tiwari/server/src/services/learnerProfile.js
new file mode 100644
index 00000000..16afba41
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/learnerProfile.js
@@ -0,0 +1,74 @@
+function buildLearnerProfile(sessions = []) {
+ const concepts = {};
+ const misconceptions = {};
+ let totalPromptScore = 0;
+ let promptScoreCount = 0;
+
+ for (const session of sessions) {
+ // Track concepts
+ const sessionConcepts =
+ session.abstractionMap || [];
+
+ for (const item of sessionConcepts) {
+ const concept =
+ item.pythonConcept;
+
+ if (!concept) continue;
+
+ concepts[concept] =
+ (concepts[concept] || 0) + 1;
+ }
+
+ // Track misconceptions
+ for (const mistake of session.misconceptions || []) {
+ misconceptions[mistake] =
+ (misconceptions[mistake] || 0) + 1;
+ }
+
+ // Track prompt quality
+ if (
+ typeof session.promptScore === 'number'
+ ) {
+ totalPromptScore +=
+ session.promptScore;
+
+ promptScoreCount++;
+ }
+ }
+
+ const averagePromptScore =
+ promptScoreCount
+ ? Math.round(
+ totalPromptScore /
+ promptScoreCount
+ )
+ : 0;
+
+ const repeatedMisconceptions =
+ Object.entries(misconceptions)
+ .filter(
+ ([, count]) => count >= 2
+ )
+ .map(
+ ([mistake, count]) => ({
+ mistake,
+ occurrences: count
+ })
+ );
+
+ return {
+ sessionsAnalyzed: sessions.length,
+
+ concepts,
+
+ misconceptions,
+
+ repeatedMisconceptions,
+
+ averagePromptScore
+ };
+}
+
+module.exports = {
+ buildLearnerProfile
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/learningEngine.js b/summership-26-prs/Vanisha Tiwari/server/src/services/learningEngine.js
index 9e6b82be..6d273b45 100644
--- a/summership-26-prs/Vanisha Tiwari/server/src/services/learningEngine.js
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/learningEngine.js
@@ -3,107 +3,672 @@ const conceptRules = [
keywords: ['repeat', 'again', 'each', 'every', 'loop'],
pattern: 'Repetition',
pythonConcept: 'for / while loops',
- explanation: 'Your reasoning repeats an action, which maps naturally to loop constructs.'
+ explanation:
+ 'Your reasoning repeats an action, which maps naturally to loop constructs.'
},
{
keywords: ['if', 'when', 'unless', 'decide', 'choose', 'condition'],
pattern: 'Decision making',
pythonConcept: 'if / elif / else',
- explanation: 'You are branching based on a condition, which is exactly what conditional statements express.'
+ explanation:
+ 'You are branching based on a condition, which is exactly what conditional statements express.'
},
{
keywords: ['list', 'items', 'collection', 'group', 'many'],
pattern: 'Collection handling',
- pythonConcept: 'lists and dictionaries',
- explanation: 'You grouped multiple values, so Python collections help store and process them.'
+ pythonConcept: 'lists',
+ explanation:
+ 'You grouped multiple values, so a Python list can store and process them.'
+ },
+ {
+ keywords: ['dictionary', 'key', 'value', 'lookup', 'name with count'],
+ pattern: 'Key-value storage',
+ pythonConcept: 'dictionaries',
+ explanation:
+ 'You are connecting names to values, which maps naturally to Python dictionaries.'
},
{
keywords: ['calculate', 'total', 'average', 'sum', 'score', 'cost'],
pattern: 'Computation',
pythonConcept: 'variables and arithmetic expressions',
- explanation: 'You are transforming values into a result, so variables and expressions become useful.'
+ explanation:
+ 'You are transforming values into a result using variables and arithmetic.'
},
{
keywords: ['step', 'process', 'recipe', 'function', 'reuse'],
pattern: 'Reusable procedure',
pythonConcept: 'functions',
- explanation: 'You described a repeatable process, which maps to a Python function.'
+ explanation:
+ 'You described a repeatable process, which maps to a Python function.'
},
{
keywords: ['compare', 'match', 'filter', 'find', 'search'],
pattern: 'Selection and filtering',
- pythonConcept: 'comparisons and list comprehensions',
- explanation: 'You are narrowing options using rules, which Python can express with comparisons and filters.'
+ pythonConcept: 'comparisons and filtering',
+ explanation:
+ 'You are selecting values according to a rule.'
+ },
+ {
+ keywords: ['remainder', 'divisible', 'even', 'odd'],
+ pattern: 'Remainder checking',
+ pythonConcept: 'modulo',
+ explanation:
+ "You are checking a remainder, which maps to Python's modulo operator."
+ },
+ {
+ keywords: ['position', 'first', 'second', 'index'],
+ pattern: 'Position access',
+ pythonConcept: 'indexing',
+ explanation:
+ 'You are accessing a value by its position in a collection.'
+ },
+ {
+ keywords: ['subtract', 'remaining', 'left', 'spent'],
+ pattern: 'Subtraction',
+ pythonConcept: 'subtraction',
+ explanation:
+ 'You are finding a remaining value by subtracting one amount from another.'
}
];
-function mapReasoning(reasoning = '') {
+function mapReasoning(reasoning = '', scenario = {}) {
const lower = reasoning.toLowerCase();
- const matches = conceptRules.filter((rule) => rule.keywords.some((keyword) => lower.includes(keyword)));
- return matches.length ? matches : [{
- pattern: 'Sequential thinking',
- pythonConcept: 'statements and variables',
- explanation: 'You described a step-by-step solution. Python starts by representing those steps as statements.'
- }];
-}
+ const scenarioConcepts =
+ (scenario.concepts || []).map((concept) =>
+ concept.toLowerCase()
+ );
+
+ const conceptRules = [
+ {
+ scenarioKeywords: ['loop', 'loops', 'repetition'],
+ reasoningKeywords: [
+ 'repeat',
+ 'again',
+ 'each',
+ 'every',
+ 'loop',
+ 'compare each'
+ ],
+ pattern: 'Repetition',
+ pythonConcept: 'for / while loops',
+ explanation:
+ 'The scenario requires processing multiple values, which maps naturally to loops.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'condition',
+ 'conditional',
+ 'comparison',
+ 'comparisons'
+ ],
+ reasoningKeywords: [
+ 'if',
+ 'when',
+ 'unless',
+ 'decide',
+ 'choose',
+ 'condition',
+ 'compare',
+ 'larger',
+ 'smaller',
+ 'greater'
+ ],
+ pattern: 'Decision making',
+ pythonConcept: 'if / elif / else',
+ explanation:
+ 'The scenario requires making a decision based on a condition.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'list',
+ 'lists',
+ 'collection'
+ ],
+ reasoningKeywords: [
+ 'list',
+ 'items',
+ 'collection',
+ 'group',
+ 'many'
+ ],
+ pattern: 'Collection handling',
+ pythonConcept: 'lists',
+ explanation:
+ 'The scenario works with multiple values, which Python can represent using lists.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'dictionary',
+ 'dictionaries',
+ 'key-value'
+ ],
+ reasoningKeywords: [
+ 'name',
+ 'key',
+ 'value',
+ 'dictionary',
+ 'lookup'
+ ],
+ pattern: 'Key-value storage',
+ pythonConcept: 'dictionaries',
+ explanation:
+ 'The scenario connects keys with values, which maps naturally to dictionaries.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'arithmetic',
+ 'calculation',
+ 'average',
+ 'variables',
+ 'subtraction'
+ ],
+ reasoningKeywords: [
+ 'calculate',
+ 'total',
+ 'average',
+ 'sum',
+ 'score',
+ 'cost',
+ 'add',
+ 'divide',
+ 'subtract'
+ ],
+ pattern: 'Computation',
+ pythonConcept: 'variables and arithmetic expressions',
+ explanation:
+ 'The scenario requires transforming values into a result using variables and arithmetic.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'filter',
+ 'filtering',
+ 'comparison',
+ 'comparisons',
+ 'search'
+ ],
+ reasoningKeywords: [
+ 'compare',
+ 'match',
+ 'filter',
+ 'find',
+ 'search',
+ 'larger',
+ 'smaller',
+ 'greater'
+ ],
+ pattern: 'Selection and filtering',
+ pythonConcept: 'comparisons and filtering',
+ explanation:
+ 'The scenario requires selecting values according to a rule.'
+ },
+
+ {
+ scenarioKeywords: [
+ 'function',
+ 'functions',
+ 'reusable'
+ ],
+ reasoningKeywords: [
+ 'function',
+ 'reuse',
+ 'reusable',
+ 'procedure'
+ ],
+ pattern: 'Reusable procedure',
+ pythonConcept: 'functions',
+ explanation:
+ 'The scenario describes a reusable operation, which maps naturally to a Python function.'
+ }
+ ];
+
+ const matches = conceptRules.filter((rule) => {
+ const scenarioMatches =
+ rule.scenarioKeywords.some((keyword) =>
+ scenarioConcepts.some((concept) =>
+ concept.includes(keyword)
+ )
+ );
+
+ const reasoningMatches =
+ rule.reasoningKeywords.some((keyword) =>
+ lower.includes(keyword)
+ );
+
+ return scenarioMatches && reasoningMatches;
+ });
+
+ if (matches.length > 0) {
+ return matches;
+ }
+
+ // If the learner's wording does not contain
+ // the expected keywords, trust the scenario.
+ return conceptRules.filter((rule) =>
+ rule.scenarioKeywords.some((keyword) =>
+ scenarioConcepts.some((concept) =>
+ concept.includes(keyword)
+ )
+ )
+ );
+}
+/*
+ * The scenario determines what the generated code should solve.
+ *
+ * This is deliberately explicit rather than asking the engine
+ * to guess a solution from generic concept keywords.
+ */
function generateCode(scenario, maps) {
- const concepts = maps.map((item) => item.pythonConcept).join(', ');
- const hasLoop = concepts.includes('loop');
- const hasCondition = concepts.includes('if');
- const hasFunction = concepts.includes('function');
+ const title = scenario.title.toLowerCase();
+
+ // Beginner scenarios
+ if (title.includes('bag weight')) {
+ return `bag_weight = 4.5
+
+print(bag_weight)`;
+ }
+
+ if (title.includes('rainy day')) {
+ return `raining = True
+
+if raining:
+ print("Carry an umbrella")
+else:
+ print("Leave the umbrella at home")`;
+ }
+
+ if (title.includes('snack prices')) {
+ return `samosa_price = 20
+juice_price = 30
+
+total_cost = samosa_price + juice_price
+
+print(total_cost)`;
+ }
+
+ if (title.includes('greeting by name')) {
+ return `name = "Ishi"
+
+greeting = f"Hello, {name}!"
+
+print(greeting)`;
+ }
+
+ if (title.includes('pass mark')) {
+ return `score = 72
+pass_mark = 40
+
+if score >= pass_mark:
+ print("Pass")
+else:
+ print("Fail")`;
+ }
+
+ if (title.includes('pocket money')) {
+ return `pocket_money = 500
+spent = 120
+
+remaining = pocket_money - spent
+
+print(remaining)`;
+ }
+
+ if (title.includes('favorite color')) {
+ return `colors = ["blue", "green", "black"]
+
+print(colors)`;
+ }
+
+ if (title.includes('first item')) {
+ return `items = ["pencil", "eraser", "ruler"]
+
+first_item = items[0]
+
+print(first_item)`;
+ }
+
+ if (title.includes('attendance count')) {
+ return `present_students = [
+ "Asha",
+ "Rahul",
+ "Ishi",
+ "Neha"
+]
+
+count = len(present_students)
+
+print(count)`;
+ }
+
+ if (title.includes('temperature message')) {
+ return `temperature = 32
+
+if temperature > 30:
+ print("Hot")
+else:
+ print("Comfortable")`;
+ }
+
+ // Explorer scenarios
+ if (title.includes('water bottle')) {
+ return `breaks = [
+ "10:30",
+ "12:00",
+ "15:00"
+]
+
+for break_time in breaks:
+ print("Drink water at", break_time)`;
+ }
+
+ if (title.includes('longest pencil')) {
+ return `lengths = [12, 15, 10, 18]
+
+longest = lengths[0]
+
+for length in lengths[1:]:
+ if length > longest:
+ longest = length
+
+print(longest)`;
+ }
+
+ if (title.includes('chore checklist')) {
+ return `chores = [
+ "sweep floor",
+ "wash dishes",
+ "organize desk"
+]
+
+for chore in chores:
+ print(chore, "- done")`;
+ }
+
+ if (title.includes('movie age')) {
+ return `movies = [
+ {"title": "Movie A", "minimum_age": 10},
+ {"title": "Movie B", "minimum_age": 15},
+ {"title": "Movie C", "minimum_age": 12}
+]
+
+learner_age = 12
+
+for movie in movies:
+ if learner_age >= movie["minimum_age"]:
+ print(movie["title"])`;
+ }
- if (hasLoop && hasCondition) {
- return 'items = [12, 7, 19, 4]\nthreshold = 10\n\nfor item in items:\n if item >= threshold:\n print(f"{item} needs attention")\n else:\n print(f"{item} is okay")';
+ if (title.includes('classroom supply')) {
+ return `supplies = {
+ "chalk": 20,
+ "markers": 8,
+ "notebooks": 15
+}
+
+print(supplies["markers"])`;
}
- if (hasFunction) {
- return 'def solve_scenario(inputs):\n result = []\n for value in inputs:\n result.append(value * 2)\n return result\n\nprint(solve_scenario([1, 2, 3]))';
+ if (title.includes('bus stop')) {
+ return `stops = [
+ "Main Gate",
+ "Library Stop",
+ "Market Stop",
+ "Station"
+]
+
+target = "Library Stop"
+
+for stop in stops:
+ if stop == target:
+ print("Found:", stop)
+ break`;
}
- if (hasLoop) {
- return 'steps = ["notice the situation", "look for a pattern", "apply the rule"]\n\nfor step in steps:\n print(step)';
+ if (title.includes('average practice')) {
+ return `scores = [80, 75, 90, 85, 70]
+
+total = 0
+
+for score in scores:
+ total += score
+
+average = total / len(scores)
+
+print(average)`;
}
- if (hasCondition) {
- return 'temperature = 32\n\nif temperature > 30:\n print("Take action now")\nelse:\n print("Keep observing")';
+ if (title.includes('even roll')) {
+ return `roll_numbers = [1, 2, 3, 4, 5, 6]
+
+even_numbers = []
+
+for roll_number in roll_numbers:
+ if roll_number % 2 == 0:
+ even_numbers.append(roll_number)
+
+print(even_numbers)`;
}
- return `scenario = "${scenario.title.replace(/"/g, '\\"')}"\nreasoning = "Break the situation into clear steps"\nprint(scenario)\nprint(reasoning)`;
+ if (title.includes('capitalize name')) {
+ return `names = ["ishi", "rahul", "neha"]
+
+for name in names:
+ print(name.title())`;
+ }
+
+ if (title.includes('missing homework')) {
+ return `all_students = {"Asha", "Rahul", "Ishi", "Neha"}
+submitted = {"Asha", "Ishi"}
+
+missing = all_students - submitted
+
+print(missing)`;
+ }
+
+ // Builder scenarios
+ if (title.includes('reusable discount')) {
+ return `def apply_discount(bill):
+ if bill >= 1000:
+ return bill * 0.9
+
+ return bill
+
+print(apply_discount(1200))`;
+ }
+
+ if (title.includes('mini quiz')) {
+ return `def check_answer(answer, correct_answer):
+ return answer == correct_answer
+
+print(check_answer("Python", "Python"))`;
+ }
+
+ if (title.includes('step counter')) {
+ return `def total_steps(step_counts):
+ return sum(step_counts)
+
+print(total_steps([3000, 4500, 5000]))`;
+ }
+
+ if (title.includes('safe username')) {
+ return `def make_username(name):
+ return name.lower().replace(" ", "")
+
+print(make_username("Ishi Jain"))`;
+ }
+
+ if (title.includes('retry until valid')) {
+ return `number = -2
+
+while number <= 0:
+ print("Invalid number")
+ number += 1
+
+print("Valid number:", number)`;
+ }
+
+ if (title.includes('score report')) {
+ return `def create_report(name, score):
+ return {
+ "name": name,
+ "score": score
+ }
+
+print(create_report("Ishi", 85))`;
+ }
+
+ if (title.includes('task status')) {
+ return `tasks = {
+ "assignment": "pending",
+ "project": "pending"
+}
+
+tasks["assignment"] = "done"
+
+print(tasks)`;
+ }
+
+ if (title.includes('receipt')) {
+ return `def create_receipt(item, price):
+ return f"{item}: βΉ{price}"
+
+print(create_receipt("Samosa", 20))`;
+ }
+
+ if (title.includes('choose next scenario')) {
+ return `def choose_level(score):
+ if score >= 80:
+ return "Advanced"
+ elif score >= 50:
+ return "Intermediate"
+ else:
+ return "Foundation"
+
+print(choose_level(75))`;
+ }
+
+ if (title.includes('reflection keyword')) {
+ return `reflection = "I was confused about loops"
+
+keywords = ["confused", "stuck", "unclear"]
+
+needs_support = any(
+ word in reflection.lower()
+ for word in keywords
+)
+
+print(needs_support)`;
+ }
+
+ /*
+ * Safe fallback.
+ *
+ * We still avoid pretending that generic code solves an
+ * unknown scenario.
+ */
+ return `scenario = "${scenario.title.replace(/"/g, '\\"')}"
+
+print("Scenario:", scenario)`;
}
function explainCode(maps) {
- return `The code starts from your natural reasoning and turns it into Python structure: ${maps.map((map) => `${map.pattern} becomes ${map.pythonConcept}`).join('; ')}.`;
+ return `The code starts from your natural reasoning and turns it into Python structure: ${maps
+ .map(
+ (map) =>
+ `${map.pattern} becomes ${map.pythonConcept}`
+ )
+ .join('; ')}.`;
}
function evaluatePrompt(promptText = '') {
const feedback = [];
+
let score = 35;
- if (promptText.length > 40) score += 15;
- else feedback.push('Add more context about the situation and expected output.');
- if (/step|explain|why|reason/i.test(promptText)) score += 20;
- else feedback.push('Ask the AI to explain its reasoning, not just produce code.');
- if (/example|input|output|data/i.test(promptText)) score += 15;
- else feedback.push('Include an example input or output to make the prompt testable.');
- if (/python|loop|if|list|function/i.test(promptText)) score += 15;
- else feedback.push('Name the Python concept you think may apply.');
+
+ if (promptText.length > 40) {
+ score += 15;
+ } else {
+ feedback.push(
+ 'Add more context about the situation and expected output.'
+ );
+ }
+
+ if (/step|explain|why|reason/i.test(promptText)) {
+ score += 20;
+ } else {
+ feedback.push(
+ 'Ask the AI to explain its reasoning, not just produce code.'
+ );
+ }
+
+ if (/example|input|output|data/i.test(promptText)) {
+ score += 15;
+ } else {
+ feedback.push(
+ 'Include an example input or output to make the prompt testable.'
+ );
+ }
+
+ if (/python|loop|if|list|function/i.test(promptText)) {
+ score += 15;
+ } else {
+ feedback.push(
+ 'Name the Python concept you think may apply.'
+ );
+ }
+
return {
score: Math.min(score, 100),
- feedback: feedback.length ? feedback : ['Strong prompt: it includes context, reasoning, examples, and a Python direction.']
+ feedback:
+ feedback.length
+ ? feedback
+ : [
+ 'Strong prompt: it includes context, reasoning, examples, and a Python direction.'
+ ]
};
}
function detectMisconceptions(reasoning = '') {
const misses = [];
- if (/always|never/i.test(reasoning)) misses.push('Watch for absolute rules. Programming logic often needs explicit edge cases.');
- if (reasoning.length < 60) misses.push('Reasoning is brief. Try naming the inputs, decision rule, and expected result.');
+
+ if (/always|never/i.test(reasoning)) {
+ misses.push(
+ 'Watch for absolute rules. Programming logic often needs explicit edge cases.'
+ );
+ }
+
+ if (reasoning.length < 40) {
+ misses.push(
+ 'Reasoning is very brief. Try naming the inputs, decision rule, and expected result.'
+ );
+}
+
return misses;
}
function masterySignals(maps, promptScore) {
- const signals = maps.map((map) => `Recognized ${map.pattern.toLowerCase()}`);
- if (promptScore >= 70) signals.push('Prompt maturity is developing');
+ const signals = maps.map(
+ (map) =>
+ `Recognized ${map.pattern.toLowerCase()}`
+ );
+
+ if (promptScore >= 70) {
+ signals.push('Prompt maturity is developing');
+ }
+
return signals;
}
-module.exports = { mapReasoning, generateCode, explainCode, evaluatePrompt, detectMisconceptions, masterySignals };
+module.exports = {
+ mapReasoning,
+ generateCode,
+ explainCode,
+ evaluatePrompt,
+ detectMisconceptions,
+ masterySignals
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/mentorContext.js b/summership-26-prs/Vanisha Tiwari/server/src/services/mentorContext.js
new file mode 100644
index 00000000..ba6d5193
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/mentorContext.js
@@ -0,0 +1,43 @@
+function buildMentorContext({
+ scenario,
+ reasoning,
+ abstractionMap,
+ generatedCode,
+ promptScore,
+ promptFeedback,
+ reflection,
+ misconceptions,
+ masterySignals
+}) {
+ return {
+ scenario: {
+ id: scenario._id,
+ title: scenario.title,
+ difficulty: scenario.difficulty,
+ concepts: scenario.concepts,
+ context: scenario.context,
+ objectives: scenario.objectives
+ },
+
+ learnerReasoning: reasoning,
+
+ abstractionMap,
+
+ generatedCode,
+
+ promptEvaluation: {
+ score: promptScore,
+ feedback: promptFeedback
+ },
+
+ reflection: reflection || '',
+
+ misconceptions: misconceptions || [],
+
+ masterySignals: masterySignals || []
+ };
+}
+
+module.exports = {
+ buildMentorContext
+};
\ No newline at end of file
diff --git a/summership-26-prs/Vanisha Tiwari/server/src/services/mentorValidator.js b/summership-26-prs/Vanisha Tiwari/server/src/services/mentorValidator.js
new file mode 100644
index 00000000..43deffd3
--- /dev/null
+++ b/summership-26-prs/Vanisha Tiwari/server/src/services/mentorValidator.js
@@ -0,0 +1,26 @@
+const REQUIRED_SECTIONS = [
+ 'UNDERSTOOD:',
+ 'GAP:',
+ 'PYTHON CONNECTION:',
+ 'HISTORY CONNECTION:',
+ 'LEARNER PROFILE INSIGHT:',
+ 'NEXT QUESTION:'
+];
+
+function validateMentorResponse(response) {
+ if (
+ typeof response !== 'string' ||
+ !response.trim()
+ ) {
+ return false;
+ }
+
+ return REQUIRED_SECTIONS.every(
+ (section) =>
+ response.includes(section)
+ );
+}
+
+module.exports = {
+ validateMentorResponse
+};
\ No newline at end of file