Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@ npm run dev

## Notes

The AI behavior in this prototype is deterministic and local. The abstraction mapper, prompt evaluator, and Python construct generator use rule-based logic so you can run everything without external AI keys. Later phases can replace those services with OpenAI, RAG, or TinyLLM components.
The AI behavior in this prototype is deterministic and local. The abstraction mapper, prompt evaluator, and Python construct generator use a robust V2 rule-based logic engine with weighted keyword matching, scenario-aware evaluation, and dynamic feedback generation, allowing you to run everything locally without external AI keys. Later phases can replace those services with OpenAI, RAG, or TinyLLM components.

Learning data is stored in `server/src/data/db.json`. This keeps the prototype simple and fully local, without MongoDB, Docker, Atlas, or any external database.
17 changes: 17 additions & 0 deletions changeLog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Changelog

## [1.1.0] - Smarter Learning Engine & Keyword Collision Fixes

This release focuses on significantly upgrading the core evaluation logic in `learningEngine.js` to provide a more dynamic, accurate, and scenario-aware learning experience.

### Enhanced
- **Two-Tier Weighted Keyword System**: Replaced flat substring matching with a primary/secondary weighted system. The engine now distinguishes between strong technical indicators (e.g., `variable`, `loop`) and conversational phrasing (e.g., `store`, `value`), requiring higher thresholds for the latter to prevent false positives.
- **Regex Pre-compilation**: Replaced `.includes()` with pre-compiled regular expressions using word boundaries (`\b`), significantly improving performance and matching accuracy.
- **Scenario-Aware Mapping**: The evaluation engine now cross-references detected keywords against the specific `scenario.concepts`.
- **Dynamic Over-engineering Feedback**: If a learner suggests an overly complex concept (e.g., a loop) for a scenario that only requires a simpler one (e.g., a variable), the engine dynamically guides them back to the expected concept.
- **Composable Code Generation**: Replaced static code templates with a dynamic AST-like string builder. It now composes variable assignments, loops, conditionals, and functions dynamically based on the exact combination of detected concepts, while injecting scenario context directly into the code.
- **Granular Prompt Evaluation**: `evaluatePrompt` calculates continuous scores based on length and intelligently verifies whether the learner mentioned the specific Python concepts required for the active scenario.
- **Context-Specific Misconceptions**: Introduced a `misconceptionsDictionary` that flags concept-specific anti-patterns (e.g., warning against manual repetition only when a loop is expected).

### Fixed
- **Keyword Collision Bug**: Removed broad conversational words (`store`, `value`) from lists and dictionaries patterns, reassigning them to a new dedicated `Data storage` rule for variables. This prevents the engine from incorrectly triggering lists and dictionaries on normal conversational sentences.
1 change: 1 addition & 0 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

131 changes: 130 additions & 1 deletion server/src/data/db.json
Original file line number Diff line number Diff line change
Expand Up @@ -596,5 +596,134 @@
"updatedAt": "2026-06-19T11:54:44.733Z"
}
],
"sessions": []
"sessions": [
{
"_id": "b3e4ae1c-13c0-441b-bb9f-555948716f6f",
"learnerName": "Guest learner",
"scenario": "8cf644f0-1402-4763-9542-02bd2ca0d1dc",
"reasoning": "a float variable .",
"promptText": "float x = 5.18",
"abstractionMap": [
{
"pattern": "Sequential thinking",
"pythonConcept": "statements and variables",
"explanation": "You described a step-by-step solution. Python starts by representing those steps as statements."
}
],
"generatedCode": "scenario = \"Bag Weight Label\"\nreasoning = \"Break the situation into clear steps\"\nprint(scenario)\nprint(reasoning)",
"codeExplanation": "The code starts from your natural reasoning and turns it into Python structure: Sequential thinking becomes statements and variables.",
"promptScore": 35,
"promptFeedback": [
"Add more context about the situation and expected output.",
"Ask the AI to explain its reasoning, not just produce code.",
"Include an example input or output to make the prompt testable.",
"Name the Python concept you think may apply."
],
"reflection": "i'm good in c and python",
"misconceptions": [
"Reasoning is brief. Try naming the inputs, decision rule, and expected result."
],
"masterySignals": [
"Recognized sequential thinking"
],
"createdAt": "2026-07-31T14:15:29.260Z",
"updatedAt": "2026-07-31T14:15:29.260Z"
},
{
"_id": "c624af90-3a46-4baa-ba8b-0b99fc53fd26",
"learnerName": "Guest learner",
"scenario": "8cf644f0-1402-4763-9542-02bd2ca0d1dc",
"reasoning": "i will need to store a decimal or float type of value for weights in a variable .",
"promptText": "i will make a variable named x and assign it a value of school bag weight. eg: float x = 10.05 kg",
"abstractionMap": [
{
"pattern": "Collection handling",
"pythonConcept": "lists",
"explanation": "You grouped multiple values, so Python lists help store and process them. (Note: This might be over-engineering for this specific scenario)",
"confidence": "60% confidence"
},
{
"pattern": "Data mapping",
"pythonConcept": "dictionaries",
"explanation": "You are associating pieces of data together, which maps perfectly to a Python dictionary. (Note: This might be over-engineering for this specific scenario)",
"confidence": "60% confidence"
}
],
"generatedCode": "# Scenario: Bag Weight Label\n\nitems = [target_value, \"other_data\", \"more_data\"]\ndata_map = {\"key1\": target_value, \"key2\": \"other_data\"}\n",
"codeExplanation": "The code starts from your natural reasoning and turns it into Python structure: Collection handling becomes lists; Data mapping becomes dictionaries.",
"promptScore": 79,
"promptFeedback": [
"Ask the AI to explain its reasoning, not just produce code."
],
"reflection": "i think i understand the flow of the problem ",
"misconceptions": [
"Try to store values in names (variables) instead of hardcoding numbers directly."
],
"masterySignals": [
"Recognized collection handling",
"Recognized data mapping",
"Prompt maturity is developing"
],
"createdAt": "2026-08-02T10:01:55.517Z",
"updatedAt": "2026-08-02T10:01:55.517Z"
},
{
"_id": "df3eab14-f99a-4d81-9515-98c15cc553c8",
"learnerName": "Guest learner",
"scenario": "8cf644f0-1402-4763-9542-02bd2ca0d1dc",
"reasoning": "i will store a decimal(float) point number for the weight and store the value in a variable.",
"promptText": "i will first declare a variable and then assign it the float value of the weight. eg float x = 10.5 kg.",
"abstractionMap": [
{
"pattern": "Data storage",
"pythonConcept": "variables",
"explanation": "You identified data that needs to be kept in memory, which maps to declaring variables.",
"confidence": "90% confidence"
}
],
"generatedCode": "# Scenario: Bag Weight Label\n\ntarget_value = \"sample_data\"\nprint(f\"Processed {target_value}\")\n",
"codeExplanation": "The code starts from your natural reasoning and turns it into Python structure: Data storage becomes variables.",
"promptScore": 80,
"promptFeedback": [
"Ask the AI to explain its reasoning, not just produce code."
],
"reflection": "i just know things",
"misconceptions": [],
"masterySignals": [
"Recognized data storage",
"Prompt maturity is developing"
],
"createdAt": "2026-08-02T10:11:29.243Z",
"updatedAt": "2026-08-02T10:11:29.243Z"
},
{
"_id": "d39ca5cd-e2ea-4d40-8c05-1eb805e6600c",
"learnerName": "Guest learner",
"scenario": "2a3496ef-bb71-4dde-83f5-37844063678d",
"reasoning": "for every break i will use a loop that will iterate through all the breaks once and reminds.",
"promptText": "i will first see how many breaks are there and then use a loop from 1st break to end break and in each break print the reminder .",
"abstractionMap": [
{
"pattern": "Repetition",
"pythonConcept": "loops",
"explanation": "Your reasoning repeats an action, which maps naturally to loop constructs.",
"confidence": "90% confidence"
}
],
"generatedCode": "# Scenario: Water Bottle Reminder\n\nfor item in items:\n # Process item\n print(item)\n",
"codeExplanation": "The code starts from your natural reasoning and turns it into Python structure: Repetition becomes loops.",
"promptScore": 100,
"promptFeedback": [
"Strong prompt: it includes context, reasoning, examples, and the right Python direction."
],
"reflection": "i just know it",
"misconceptions": [],
"masterySignals": [
"Recognized repetition",
"Prompt maturity is developing"
],
"createdAt": "2026-08-02T10:13:47.165Z",
"updatedAt": "2026-08-02T10:13:47.165Z"
}
]
}
6 changes: 3 additions & 3 deletions server/src/routes/sessions.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ router.post('/', async (req, res, next) => {
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 abstractionMap = engine.mapReasoning(scenario, req.body.reasoning);
const generatedCode = engine.generateCode(scenario, abstractionMap);
const prompt = engine.evaluatePrompt(req.body.promptText);
const prompt = engine.evaluatePrompt(scenario, req.body.promptText);
const session = await store.addSession({
learnerName: req.body.learnerName || 'Guest learner',
scenario: scenario._id,
Expand All @@ -32,7 +32,7 @@ router.post('/', async (req, res, next) => {
promptScore: prompt.score,
promptFeedback: prompt.feedback,
reflection: req.body.reflection || '',
misconceptions: engine.detectMisconceptions(req.body.reasoning),
misconceptions: engine.detectMisconceptions(scenario, req.body.reasoning),
masterySignals: engine.masterySignals(abstractionMap, prompt.score)
});
res.status(201).json(session);
Expand Down
Loading