Skip to content

Conversation

bgoldowsky
Copy link
Contributor

@bgoldowsky bgoldowsky commented Aug 18, 2025

CLUE-240

Defines a new "AI" tile type. This is authored with a prompt, and generates content that is displayed in the document. The content is cached in Firestore.

  • Since this tile can be used in general ways, some renaming is included to remove 'exemplars' from naming related objects.
  • The prompt in the tile is augmented with a system prompt that can be set in the unit configuration (in settings.ai.systemPrompt).
  • The prompt is also augmented with the class document summaries that are generated nightly.
  • Add a 'show class summaries' button for viewing the class summaries (requires showAiSummary=true command line arg).
  • Add a way in that dialog to update the class summaries at will, to avoid having to wait for at-midnight to run.
  • Add some fixes to authoring to make sure the authorTools section in unit configuration is respected. This would be where the AI tile toolbar button would normally be included.

This adds NPM dependency markdown-to-jsx, so run npm install.

Note the Firebase functions in this PR have already been deployed to production for testing.

Additional tests, including Cypress tests for the new tile type, would be nice to add to this, but we ran out of time.

@bgoldowsky bgoldowsky changed the base branch from master to CLUE-215-context-in-exemplars August 18, 2025 12:47
Copy link

codecov bot commented Aug 18, 2025

Codecov Report

❌ Patch coverage is 60.00000% with 70 lines in your changes missing coverage. Please review.
✅ Project coverage is 86.73%. Comparing base (c68e614) to head (ae39805).
⚠️ Report is 32 commits behind head on master.

Files with missing lines Patch % Lines
src/components/document/summary-button.tsx 15.51% 48 Missing and 1 partial ⚠️
src/plugins/ai/ai-tile.tsx 82.53% 11 Missing ⚠️
src/plugins/ai/ai-utils.tsx 63.63% 4 Missing ⚠️
src/plugins/ai/ai-content.ts 83.33% 3 Missing ⚠️
src/lib/firestore.ts 0.00% 2 Missing ⚠️
src/register-tile-types.ts 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #2626      +/-   ##
==========================================
- Coverage   86.83%   86.73%   -0.10%     
==========================================
  Files         783      789       +6     
  Lines       42342    42514     +172     
  Branches    10809    10836      +27     
==========================================
+ Hits        36768    36876     +108     
- Misses       5257     5320      +63     
- Partials      317      318       +1     
Flag Coverage Δ
cypress-regression 78.07% <45.86%> (-0.13%) ⬇️
cypress-smoke 44.07% <13.53%> (-0.10%) ⬇️
jest 48.79% <48.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

cypress bot commented Aug 18, 2025

collaborative-learning    Run #16466

Run Properties:  status check passed Passed #16466  •  git commit ae39805f7e: Don't call teacher work "Student work"
Project collaborative-learning
Branch Review CLUE-240-ai-content
Run status status check passed Passed #16466
Run duration 19m 15s
Commit git commit ae39805f7e: Don't call teacher work "Student work"
Committer Boris Goldowsky
View all properties for this run ↗︎

Test results
Tests that failed  Failures 0
Tests that were flaky  Flaky 2
Tests that did not run due to a developer annotating a test with .skip  Pending 4
Tests that did not run due to a failure in a mocha hook  Skipped 0
Tests that passed  Passing 149
View all changes introduced in this branch ↗︎

Base automatically changed from CLUE-215-context-in-exemplars to master August 19, 2025 13:40
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR introduces a new AI tile type for generating dynamic content in exemplars. The AI tile accepts a prompt and generates content that is displayed in the document, with responses cached in Firestore to improve performance.

  • Adds AI tile registration and component infrastructure
  • Implements Firebase functions for content generation and class data summarization
  • Integrates OpenAI API for content generation with caching and locking mechanisms

Reviewed Changes

Copilot reviewed 33 out of 35 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/utilities/auth-utils.ts Extracts production URL check into reusable function
src/register-tile-types.ts Registers new AI tile type for dynamic loading
src/plugins/ai/*.ts Core AI tile implementation with content model, component, and utilities
src/components/document/summary-button.tsx Adds UI component for viewing AI-generated class summaries
shared/update-class-data-docs.ts Refactors class data document processing for AI summarization
functions-v2/src/*.ts Implements Firebase functions for AI content generation and class data processing
src/clue/app-config.json Adds default AI system prompt configuration

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +65 to +66
setLastUpdated(new Date(timestamp._seconds*1000));
}
Copy link
Preview

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

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

The direct access to _seconds suggests this is accessing a private Firebase Timestamp property. Consider using the public Timestamp API or converting the timestamp on the server side to avoid potential breaking changes if the internal structure changes.

Suggested change
setLastUpdated(new Date(timestamp._seconds*1000));
}
// Use public API if available, fallback to _seconds if necessary
if (timestamp && typeof timestamp.toDate === "function") {
setLastUpdated(timestamp.toDate());
} else if (timestamp && typeof timestamp._seconds === "number") {
setLastUpdated(new Date(timestamp._seconds * 1000));
}

Copilot uses AI. Check for mistakes.

Comment on lines +12 to +13
const portals: string[] = ["learn.concord.org"]; // Consider all classes on production.
const demos = ["AITEST"]; // Consider only classes in this demo area.
Copy link
Preview

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

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

The hardcoded portal and demo arrays limit flexibility. Consider making these configurable parameters or environment variables to support different deployment scenarios.

Suggested change
const portals: string[] = ["learn.concord.org"]; // Consider all classes on production.
const demos = ["AITEST"]; // Consider only classes in this demo area.
// Portals and demos can be configured via environment variables (comma-separated), or default to the current values.
const portals: string[] = process.env.PORTALS ? process.env.PORTALS.split(",").map(s => s.trim()) : ["learn.concord.org"]; // Consider all classes on production.
const demos: string[] = process.env.DEMOS ? process.env.DEMOS.split(",").map(s => s.trim()) : ["AITEST"]; // Consider only classes in this demo area.

Copilot uses AI. Check for mistakes.

Comment on lines +8 to +15

/**
* Convert the AI tile content model to a Text tile content model.
* The content of the Text tile will be the AI-generated content, converted to HTML for Slate.
* @param content - The AI tile content model
* @returns The Text tile content model
*/
export function switchToTextContent(content: any) {
Copy link
Preview

Copilot AI Aug 28, 2025

Choose a reason for hiding this comment

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

The parameter type any reduces type safety. Consider defining a proper interface for the AI content or using the existing AIContentModelType.

Suggested change
/**
* Convert the AI tile content model to a Text tile content model.
* The content of the Text tile will be the AI-generated content, converted to HTML for Slate.
* @param content - The AI tile content model
* @returns The Text tile content model
*/
export function switchToTextContent(content: any) {
import type { AIContentModelType } from "../../models/tiles/ai/ai-content";
/**
* Convert the AI tile content model to a Text tile content model.
* The content of the Text tile will be the AI-generated content, converted to HTML for Slate.
* @param content - The AI tile content model
* @returns The Text tile content model
*/
export function switchToTextContent(content: AIContentModelType) {

Copilot uses AI. Check for mistakes.

@bgoldowsky bgoldowsky marked this pull request as ready for review August 28, 2025 20:46
@@ -173,7 +173,7 @@ There are a number of URL parameters that can aid in testing:
|`studentDocumentHistoryId`|string |Open the history slider and move to the specified revision in the `studentDocument`.|
|`portalDomain` |string |Used for dev/qa of standalone auth, this overrides which portal is used for auth|
|`firebaseEnv` |`production`,`staging` |Target Firebase project for data and functions|
|`showAiSummary` |`true` |When set to true the "ai summary" button in the document editor|
|`showAiSummary` |`true` |When set to true the "ai summary" button in the document editor and Student Work view|
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
|`showAiSummary` |`true` |When set to true the "ai summary" button in the document editor and Student Work view|
|`showAiSummary` |`true` |When set to true show the "ai summary" button in the document editor and Student Work view|

let errorMessage = "";
const lastUpdated = Timestamp.now();
try {
const response = await openai.invoke(messages);
Copy link
Member

Choose a reason for hiding this comment

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

Is there a limit to how long a firebase function can run? Will this return before that time runs out?
If this is an http request having it wait for multiple seconds while waiting for open ai will likely cause problems.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have not noticed problems but I don't know what the limits are.

Component: AIComponent,
tileEltClass: "ai-tool-tile",
Icon,
HeaderIcon: Icon // TODO: if this ever becomes a "real" tile (used in student/teacher work) we'll need a HeaderIcon.
Copy link
Member

Choose a reason for hiding this comment

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

Is this tile used by teachers to configure a prompt for their student summary? So it is kind of used in teacher "work" if I'm understanding this correctly. Could you update this comment to clarify? Or maybe better would be to add some doc to the plugins/ai which describes how this tile is used.

Copy link
Contributor Author

@bgoldowsky bgoldowsky Aug 29, 2025

Choose a reason for hiding this comment

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

The canonical use is that these tiles are authored in curriculum (specifically, in Exemplars) and not available to students or teachers. When copied into a student/teacher document, the AI tile is transformed into a Text tile.

Copy link
Member

@scytacki scytacki left a comment

Choose a reason for hiding this comment

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

This looks really good. I just a left a few comments. I'd guess the timeout issue will be a problem sometime in the future, but I don't think there is time to address it now.

If you could add some doc describing the point of the new ai tile that would be the most useful.

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

Successfully merging this pull request may close these issues.

3 participants