Skip to content

levelbuilder: generate lesson with AI#72537

Open
breville wants to merge 13 commits intostagingfrom
levelbuilder-ai-generate-lesson
Open

levelbuilder: generate lesson with AI#72537
breville wants to merge 13 commits intostagingfrom
levelbuilder-ai-generate-lesson

Conversation

@breville
Copy link
Copy Markdown
Member

@breville breville commented May 6, 2026

This proposes a new levelbuilder feature to generate a lesson's levels using AI.

The goal is to make it faster and easier to create a lesson's levels. It's influenced by @jamjamgobambam's Levels and Slides Drafter demo.

This feature is accessed by visiting /s/[script]/lessons/[lesson]/generate.

As working right now, a levelbuilder can generate an entire lesson of panels & weblab2 levels from a single description. It's also easy to adjust the set of levels & their individual descriptions before generating them.

From a single lesson prompt

When starting, it's optional to generate a set of levels from a single lesson prompt:

localhost-studio code org_9000_s_intro-to-web-lab_lessons_2_generate (3)

If provided, this prompt is also fed to all subsequent generation operations to maintain some common context across all levels.

A set of level descriptions

Here's an example set of level descriptions generated from this prompt:

localhost-studio code org_9000_s_intro-to-web-lab_lessons_2_generate (4)

If the single lesson prompt was not used, this can all be set up manually. And even if the prompt was used, this can all be adjusted before the levels generation takes place.

Levels generation

When ready, levels generation can be initiated. A popup shows progress, with even more detail emitted to the console. The feature creates levels and their content: images & text for panels levels; instructions, HTML/CSS/JS for weblab2 levels.

Screenshot 2026-05-05 at 4 37 39 PM

The generated content of all preceding levels is provided for each new level's generation, helping it to maintain consistency.

A lesson

Once generation is complete, we have a lesson with levels. They'll include panels:

Screenshot 2026-05-05 at 8 35 00 PM

And weblab2:

Screenshot 2026-05-05 at 4 44 46 PM

Revisions

It's important to note that this is designed to be run repeatedly for the same lesson when desired. On the generate page, each level's Generate checkbox is checked if the description is updated, and can also be checked manually, so that the level will be updated on the next initiation of levels generation.

Other labs

This feature enumerates but leaves unsupported levels intact, so that a wider variety of level types can be included in a lesson. Of course, we can add generation of additional level types in the future.

@breville breville added the AI generated This PR has been substantially generated using AI. label May 6, 2026
@breville breville requested review from a team and jamjamgobambam May 8, 2026 20:40
Copy link
Copy Markdown
Contributor

@sanchitmalhotra126 sanchitmalhotra126 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So cool! The future is coming 😎

This will be a huge speedup levelbuilders and I'm excited to see us already taking advantage of the AI Gateway for internal use. Left comments as I saw them but didn't pore too deep into some of the longer files. Broadly I'd say my feedback for the next round of iteration is 1) encourage reuse of existing types and frameworks where possible (Lab2 types, HttpClient, etc) and 2) break up larger files into smaller modules.


import {uploadLevelAsset} from './levelApi';

// We don't import getModel from aichat/api/client/helpers/modelHelpers because
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh a bit confused by this - doesn't seem like getModel should have Lab2Registry in the import path, unless I'm missing something? And even then seems like simply importing Lab2Registry shouldn't be an issue if we're not actually calling it?

If it doesn't cause issue, I would prefer to use the existing helpers in aichat/api/client since this gives us a good use case for vetting the AI Gateway client code against internal tool usage (cc @edcodedotorg you might be interested in this use case too).

),
layout: z
.enum([
'text-top-left',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we consolidate/share with the PanelLayout type?

const prompt = [
'You are helping a curriculum author build a "Panels" level: a short,',
'comic-strip-style sequence of full-width panels with overlay text.',
'The level description follows. Plan a sequence of 3 to 6 panels that,',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the "3 to 6" be configurable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think 3 is 6 is a reasonable default but the prompt writer should be able to specify their own count (or at least range) which overrides this.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we break up this file 🙂 would be great to at least split the panels and weblab2 specific generation pieces into their own files, with any shared utils in a common location

});

export interface Weblab2Generation {
startSources: object;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this can't be MultiFileSource?

// handle_json_params.
export async function updatePanelsLevel(
levelId: number,
panels: object[]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be Panel[]?

// so this is the same payload shape used by the codebridge save button.
export async function updateStartSources(
levelId: number,
startSources: object
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be MultiFileSource (presumably we do something similar when we save start sources in labs)? @molly-moen @fisher-alice might know better

Comment on lines +123 to +128
export async function updateLongInstructions(
levelId: number,
longInstructions: string
): Promise<void> {
const form = new FormData();
form.append('level[long_instructions]', longInstructions);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if there's a case for a generic updateLevelProperty API

const blob = new Blob([data.buffer as ArrayBuffer], {type: mediaType});
const form = new FormData();
form.append('file', blob, filename);
const response = await fetch('/level_assets/upload', {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential future refactor - share this upload code with UploadImageDialog

export async function loadLessonLevelProperties(
lessonId: number
): Promise<Record<string, Record<string, unknown>>> {
const response = await fetch(`/lessons/${lessonId}/level_properties`, {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potentially shareable with useLoadLevelProperties though it is just an API call. If not shared, I'd at least say let's use HttpClient and LevelPropertiesValidator.

Return type can also be LevelPropertiesMap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI generated This PR has been substantially generated using AI.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants