From 019eab4de2af12b1e0cd6a8ffffa3dc2a3d3b6fb Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:16:18 -0600 Subject: [PATCH 01/27] categorizer --- packages/perseus/src/prompt-types.ts | 2 +- ...r.test.ts => categorizer-ai-utils.test.ts} | 30 ++++++++++++++++++- ...rompt-utils.ts => categorizer-ai-utils.ts} | 0 .../categorizer/prompt-utils.test.ts | 30 ------------------- .../graded-group/prompt-utils.test.ts | 2 +- .../src/widgets/categorizer/categorizer.tsx | 4 +-- 6 files changed, 33 insertions(+), 35 deletions(-) rename packages/perseus/src/widget-ai-utils/categorizer/{categorizer.test.ts => categorizer-ai-utils.test.ts} (77%) rename packages/perseus/src/widget-ai-utils/categorizer/{prompt-utils.ts => categorizer-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 964110e6d1..f7ad28e6ab 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -1,4 +1,4 @@ -import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/prompt-utils"; +import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/categorizer-ai-utils"; import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/prompt-utils"; import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/prompt-utils"; import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/categorizer/categorizer.test.ts b/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts similarity index 77% rename from packages/perseus/src/widget-ai-utils/categorizer/categorizer.test.ts rename to packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts index 198aef2412..a945928bf2 100644 --- a/packages/perseus/src/widget-ai-utils/categorizer/categorizer.test.ts +++ b/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts @@ -5,7 +5,10 @@ import {testDependencies} from "../../../../../testing/test-dependencies"; import * as Dependencies from "../../dependencies"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./categorizer-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; +import type {PerseusCategorizerUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; export const randomizedQuestion: PerseusRenderer = { @@ -35,7 +38,7 @@ export const randomizedQuestion: PerseusRenderer = { }, }; -describe("categorizer widget", () => { +describe("Categorizer AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -47,6 +50,31 @@ describe("categorizer widget", () => { ); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + items: ["Luke Skywalker", "Darth Vader", "Yoda", "Han Solo"], + categories: ["Galactic Empire", "Rebel Alliance"], + values: [1, 0, 1, 1], + }; + + const userInput: PerseusCategorizerUserInput = { + values: [1, 0, 0, 1], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "categorizer", + options: { + items: ["Luke Skywalker", "Darth Vader", "Yoda", "Han Solo"], + categories: ["Galactic Empire", "Rebel Alliance"], + }, + userInput: { + itemToCategoryMapping: [1, 0, 0, 1], + }, + }); + }); + it("should get prompt json which matches the state of the UI for a randomized question", async () => { // arrange const {renderer} = renderQuestion(randomizedQuestion); diff --git a/packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.test.ts deleted file mode 100644 index ead765fa65..0000000000 --- a/packages/perseus/src/widget-ai-utils/categorizer/prompt-utils.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {PerseusCategorizerUserInput} from "../../validation.types"; - -describe("Categorizer getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - items: ["Luke Skywalker", "Darth Vader", "Yoda", "Han Solo"], - categories: ["Galactic Empire", "Rebel Alliance"], - values: [1, 0, 1, 1], - }; - - const userInput: PerseusCategorizerUserInput = { - values: [1, 0, 0, 1], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "categorizer", - options: { - items: ["Luke Skywalker", "Darth Vader", "Yoda", "Han Solo"], - categories: ["Galactic Empire", "Rebel Alliance"], - }, - userInput: { - itemToCategoryMapping: [1, 0, 0, 1], - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts index d9f7c060c1..b5a39ec6bb 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts @@ -1,6 +1,6 @@ import {getPromptJSON} from "./prompt-utils"; -import type {CategorizerPromptJSON} from "../categorizer/prompt-utils"; +import type {CategorizerPromptJSON} from "../categorizer/categorizer-ai-utils"; import type {ImagePromptJSON} from "../image/prompt-utils"; describe("GradedGroup getPromptJSON", () => { diff --git a/packages/perseus/src/widgets/categorizer/categorizer.tsx b/packages/perseus/src/widgets/categorizer/categorizer.tsx index c0ce91575f..93cc0daa8b 100644 --- a/packages/perseus/src/widgets/categorizer/categorizer.tsx +++ b/packages/perseus/src/widgets/categorizer/categorizer.tsx @@ -14,7 +14,7 @@ import Renderer from "../../renderer"; import mediaQueries from "../../styles/media-queries"; import sharedStyles from "../../styles/shared"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/categorizer/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/categorizer/categorizer-ai-utils"; import scoreCategorizer from "./score-categorizer"; @@ -24,7 +24,7 @@ import type { PerseusCategorizerRubric, PerseusCategorizerUserInput, } from "../../validation.types"; -import type {CategorizerPromptJSON} from "../../widget-ai-utils/categorizer/prompt-utils"; +import type {CategorizerPromptJSON} from "../../widget-ai-utils/categorizer/categorizer-ai-utils"; type Props = WidgetProps & { values: ReadonlyArray; From 66549dde6e206920661259b0efa330d92faf345e Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:18:31 -0600 Subject: [PATCH 02/27] cs-program --- ...program.test.ts => cs-program-ai-utils.test.ts} | 14 +++++++++++++- .../{prompt-utils.ts => cs-program-ai-utils.ts} | 0 .../cs-program/prompt-utils.test.ts | 13 ------------- .../perseus/src/widgets/cs-program/cs-program.tsx | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) rename packages/perseus/src/widget-ai-utils/cs-program/{cs-program.test.ts => cs-program-ai-utils.test.ts} (79%) rename packages/perseus/src/widget-ai-utils/cs-program/{prompt-utils.ts => cs-program-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.test.ts diff --git a/packages/perseus/src/widget-ai-utils/cs-program/cs-program.test.ts b/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts similarity index 79% rename from packages/perseus/src/widget-ai-utils/cs-program/cs-program.test.ts rename to packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts index 8016484371..519fc60a1a 100644 --- a/packages/perseus/src/widget-ai-utils/cs-program/cs-program.test.ts +++ b/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./cs-program-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; export const question1: PerseusRenderer = { @@ -28,7 +30,17 @@ export const question1: PerseusRenderer = { }, }; -describe("cs-program widget", () => { +describe("CS Program AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const resultJSON = getPromptJSON(); + + expect(resultJSON).toEqual({ + type: "cs-program", + isSupported: false, + message: "", + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1, {isMobile: false}); diff --git a/packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.test.ts deleted file mode 100644 index 243c6c8ebe..0000000000 --- a/packages/perseus/src/widget-ai-utils/cs-program/prompt-utils.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("CS Program getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const resultJSON = getPromptJSON(); - - expect(resultJSON).toEqual({ - type: "cs-program", - isSupported: false, - message: "", - }); - }); -}); diff --git a/packages/perseus/src/widgets/cs-program/cs-program.tsx b/packages/perseus/src/widgets/cs-program/cs-program.tsx index 68c10df5ea..fb1813a23d 100644 --- a/packages/perseus/src/widgets/cs-program/cs-program.tsx +++ b/packages/perseus/src/widgets/cs-program/cs-program.tsx @@ -13,7 +13,7 @@ import {articleMaxWidthInPx} from "../../styles/constants"; import Util from "../../util"; import {isFileProtocol} from "../../util/mobile-native-utils"; import {toAbsoluteUrl} from "../../util/url-utils"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/cs-program/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/cs-program/cs-program-ai-utils"; import scoreCSProgram from "./score-cs-program"; From 69f8376ed6a001b30774aa15221b6d5081f4eada Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:24:33 -0600 Subject: [PATCH 03/27] dropdown and definition --- packages/perseus/src/prompt-types.ts | 4 +-- ...on.test.ts => definition-ai-utils.test.ts} | 19 ++++++++++++- ...prompt-utils.ts => definition-ai-utils.ts} | 0 .../definition/prompt-utils.test.ts | 18 ------------ ...down.test.ts => dropdown-ai-utils.test.ts} | 28 ++++++++++++++++++- .../{prompt-utils.ts => dropdown-ai-utils.ts} | 0 .../dropdown/prompt-utils.test.ts | 28 ------------------- .../src/widgets/definition/definition.tsx | 4 +-- .../perseus/src/widgets/dropdown/dropdown.tsx | 4 +-- 9 files changed, 51 insertions(+), 54 deletions(-) rename packages/perseus/src/widget-ai-utils/definition/{definition.test.ts => definition-ai-utils.test.ts} (75%) rename packages/perseus/src/widget-ai-utils/definition/{prompt-utils.ts => definition-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/definition/prompt-utils.test.ts rename packages/perseus/src/widget-ai-utils/dropdown/{dropdown.test.ts => dropdown-ai-utils.test.ts} (74%) rename packages/perseus/src/widget-ai-utils/dropdown/{prompt-utils.ts => dropdown-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index f7ad28e6ab..c8bfd94bc7 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -1,6 +1,6 @@ import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/categorizer-ai-utils"; -import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/prompt-utils"; -import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/prompt-utils"; +import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/definition-ai-utils"; +import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-utils"; import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/prompt-utils"; import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/prompt-utils"; import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/definition/definition.test.ts b/packages/perseus/src/widget-ai-utils/definition/definition-ai-utils.test.ts similarity index 75% rename from packages/perseus/src/widget-ai-utils/definition/definition.test.ts rename to packages/perseus/src/widget-ai-utils/definition/definition-ai-utils.test.ts index 5548b7ed56..751f5d2070 100644 --- a/packages/perseus/src/widget-ai-utils/definition/definition.test.ts +++ b/packages/perseus/src/widget-ai-utils/definition/definition-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./definition-ai-utils"; + const question = { content: "Read the excerpt and answer the question below. \n\nThe Governor and Council of the Massachusetts had much conference many days; and at last . . . . concluded a peace and friendship with [[\u2603 definition 1]], upon these conditions.", @@ -23,7 +25,22 @@ const question = { }, } as const; -describe("definition widget", () => { +describe("Definition AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + definition: "to confuse or fluster", + togglePrompt: "bumfuzzle", + }; + + const resultJSON = getPromptJSON(renderProps); + + expect(resultJSON).toEqual({ + type: "definition", + definition: "to confuse or fluster", + togglePrompt: "bumfuzzle", + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/definition/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/definition/definition-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/definition/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/definition/definition-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/definition/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/definition/prompt-utils.test.ts deleted file mode 100644 index d7a5ef98e2..0000000000 --- a/packages/perseus/src/widget-ai-utils/definition/prompt-utils.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Definition getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - definition: "to confuse or fluster", - togglePrompt: "bumfuzzle", - }; - - const resultJSON = getPromptJSON(renderProps); - - expect(resultJSON).toEqual({ - type: "definition", - definition: "to confuse or fluster", - togglePrompt: "bumfuzzle", - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/dropdown/dropdown.test.ts b/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts similarity index 74% rename from packages/perseus/src/widget-ai-utils/dropdown/dropdown.test.ts rename to packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts index 18aeaa9df9..8953793007 100644 --- a/packages/perseus/src/widget-ai-utils/dropdown/dropdown.test.ts +++ b/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts @@ -3,7 +3,10 @@ import {userEvent as userEventLib} from "@testing-library/user-event"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./dropdown-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; +import type {PerseusDropdownUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; export const question1: PerseusRenderer = { @@ -38,7 +41,7 @@ export const question1: PerseusRenderer = { }, }; -describe("dropdown widget", () => { +describe("Dropdown AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -46,6 +49,29 @@ describe("dropdown widget", () => { }); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + choices: ["Pickles", "Tomato", "Onion", "Lettuce"], + }; + + const userInput: PerseusDropdownUserInput = { + value: 3, + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "dropdown", + options: { + items: ["Pickles", "Tomato", "Onion", "Lettuce"], + }, + userInput: { + // Offset to account for placeholder + selectedIndex: 2, + }, + }); + }); + it("should get prompt json which matches the state of the UI for a randomized question", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.test.ts deleted file mode 100644 index 55e5cafa73..0000000000 --- a/packages/perseus/src/widget-ai-utils/dropdown/prompt-utils.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {PerseusDropdownUserInput} from "../../validation.types"; - -describe("Dropdown getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - choices: ["Pickles", "Tomato", "Onion", "Lettuce"], - }; - - const userInput: PerseusDropdownUserInput = { - value: 3, - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "dropdown", - options: { - items: ["Pickles", "Tomato", "Onion", "Lettuce"], - }, - userInput: { - // Offset to account for placeholder - selectedIndex: 2, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/definition/definition.tsx b/packages/perseus/src/widgets/definition/definition.tsx index 46c94ff947..b003d1a32a 100644 --- a/packages/perseus/src/widgets/definition/definition.tsx +++ b/packages/perseus/src/widgets/definition/definition.tsx @@ -6,7 +6,7 @@ import * as React from "react"; import {PerseusI18nContext} from "../../components/i18n-context"; import {DefinitionConsumer} from "../../definition-context"; import Renderer from "../../renderer"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/definition/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/definition/definition-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import type { @@ -14,7 +14,7 @@ import type { PerseusDefinitionWidgetOptions, } from "../../perseus-types"; import type {Widget, WidgetExports, WidgetProps} from "../../types"; -import type {DefinitionPromptJSON} from "../../widget-ai-utils/definition/prompt-utils"; +import type {DefinitionPromptJSON} from "../../widget-ai-utils/definition/definition-ai-utils"; type RenderProps = PerseusDefinitionWidgetOptions; diff --git a/packages/perseus/src/widgets/dropdown/dropdown.tsx b/packages/perseus/src/widgets/dropdown/dropdown.tsx index 473c96b6e6..4025041791 100644 --- a/packages/perseus/src/widgets/dropdown/dropdown.tsx +++ b/packages/perseus/src/widgets/dropdown/dropdown.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import ReactDOM from "react-dom"; import {ApiOptions} from "../../perseus-api"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/dropdown/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/dropdown/dropdown-ai-utils"; import scoreDropdown from "./score-dropdown"; @@ -13,7 +13,7 @@ import type { PerseusDropdownRubric, PerseusDropdownUserInput, } from "../../validation.types"; -import type {DropdownPromptJSON} from "../../widget-ai-utils/dropdown/prompt-utils"; +import type {DropdownPromptJSON} from "../../widget-ai-utils/dropdown/dropdown-ai-utils"; type Props = WidgetProps & { selected: number; From f6ca5cf4736760aab4a9d99c11243fdb232fc490 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:26:37 -0600 Subject: [PATCH 04/27] explanation --- packages/perseus/src/prompt-types.ts | 2 +- .../categorizer/categorizer-ai-utils.test.ts | 2 +- .../cs-program/cs-program-ai-utils.test.ts | 2 +- .../dropdown/dropdown-ai-utils.test.ts | 2 +- ...n.test.ts => explanation-ai-utils.test.ts} | 21 +++++++++++++++++-- ...rompt-utils.ts => explanation-ai-utils.ts} | 0 .../explanation/prompt-utils.test.ts | 18 ---------------- .../src/widgets/explanation/explanation.tsx | 4 ++-- 8 files changed, 25 insertions(+), 26 deletions(-) rename packages/perseus/src/widget-ai-utils/explanation/{explanation.test.ts => explanation-ai-utils.test.ts} (73%) rename packages/perseus/src/widget-ai-utils/explanation/{prompt-utils.ts => explanation-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/explanation/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index c8bfd94bc7..f7c4ea90ae 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -1,7 +1,7 @@ import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/categorizer-ai-utils"; import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/definition-ai-utils"; import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-utils"; -import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/prompt-utils"; +import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanation-ai-utils"; import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/prompt-utils"; import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/prompt-utils"; import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts index a945928bf2..7748b75808 100644 --- a/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/categorizer/categorizer-ai-utils.test.ts @@ -11,7 +11,7 @@ import type {PerseusRenderer} from "../../perseus-types"; import type {PerseusCategorizerUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; -export const randomizedQuestion: PerseusRenderer = { +const randomizedQuestion: PerseusRenderer = { content: "**Classify each graph according to the kind of relationship it suggests.**\n\n$\\qquad\\qquad\\quad\\text{Graph 1}\\qquad\\qquad\\quad\\qquad\\qquad\\quad\\text{Graph 2}$\n\n\n\n[[\u2603 categorizer 1]]\n\n**Graph 1.**\n\n![](https://ka-perseus-graphie.s3.amazonaws.com/049c091ed0978112aba3a36b0591d992baf7b1ac.png)\n\n**Graph 2.**\n\n![](https://ka-perseus-graphie.s3.amazonaws.com/40df186f39fb6d65de6bee0d8b681502d10cb37a.png) \n", images: {}, diff --git a/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts index 519fc60a1a..5ff58542cf 100644 --- a/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/cs-program/cs-program-ai-utils.test.ts @@ -4,7 +4,7 @@ import {getPromptJSON} from "./cs-program-ai-utils"; import type {PerseusRenderer} from "../../perseus-types"; -export const question1: PerseusRenderer = { +const question1: PerseusRenderer = { content: "[[\u2603 cs-program 1]]\n\n", images: {}, widgets: { diff --git a/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts index 8953793007..279de3fe63 100644 --- a/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/dropdown/dropdown-ai-utils.test.ts @@ -9,7 +9,7 @@ import type {PerseusRenderer} from "../../perseus-types"; import type {PerseusDropdownUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; -export const question1: PerseusRenderer = { +const question1: PerseusRenderer = { content: "The total number of boxes the forklift can carry is [[☃ dropdown 1]] $60$.", images: {}, diff --git a/packages/perseus/src/widget-ai-utils/explanation/explanation.test.ts b/packages/perseus/src/widget-ai-utils/explanation/explanation-ai-utils.test.ts similarity index 73% rename from packages/perseus/src/widget-ai-utils/explanation/explanation.test.ts rename to packages/perseus/src/widget-ai-utils/explanation/explanation-ai-utils.test.ts index 009305a911..86b2c88755 100644 --- a/packages/perseus/src/widget-ai-utils/explanation/explanation.test.ts +++ b/packages/perseus/src/widget-ai-utils/explanation/explanation-ai-utils.test.ts @@ -1,8 +1,10 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./explanation-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; -export const question1: PerseusRenderer = { +const question1: PerseusRenderer = { content: "Here's the explanation\n[[\u2603 explanation 1]]\nDid you get that?", images: {}, @@ -30,7 +32,22 @@ export const question1: PerseusRenderer = { }, }; -describe("explanation widget", () => { +describe("Explanation getPromptJSON", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + showPrompt: "Show explanation", + explanation: "This is the explanation", + }; + + const resultJSON = getPromptJSON(renderProps); + + expect(resultJSON).toEqual({ + type: "explanation", + showPrompt: "Show explanation", + explanation: "This is the explanation", + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/explanation/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/explanation/explanation-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/explanation/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/explanation/explanation-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/explanation/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/explanation/prompt-utils.test.ts deleted file mode 100644 index 6030406e10..0000000000 --- a/packages/perseus/src/widget-ai-utils/explanation/prompt-utils.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Explanation getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - showPrompt: "Show explanation", - explanation: "This is the explanation", - }; - - const resultJSON = getPromptJSON(renderProps); - - expect(resultJSON).toEqual({ - type: "explanation", - showPrompt: "Show explanation", - explanation: "This is the explanation", - }); - }); -}); diff --git a/packages/perseus/src/widgets/explanation/explanation.tsx b/packages/perseus/src/widgets/explanation/explanation.tsx index 114cf25237..94f2e80b6e 100644 --- a/packages/perseus/src/widgets/explanation/explanation.tsx +++ b/packages/perseus/src/widgets/explanation/explanation.tsx @@ -10,12 +10,12 @@ import _ from "underscore"; import {PerseusI18nContext} from "../../components/i18n-context"; import * as Changeable from "../../mixins/changeable"; import Renderer from "../../renderer"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/explanation/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/explanation/explanation-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import type {PerseusExplanationWidgetOptions} from "../../perseus-types"; import type {Widget, WidgetExports, WidgetProps} from "../../types"; -import type {ExplanationPromptJSON} from "../../widget-ai-utils/explanation/prompt-utils"; +import type {ExplanationPromptJSON} from "../../widget-ai-utils/explanation/explanation-ai-utils"; type RenderProps = PerseusExplanationWidgetOptions; // transform = _.identity From b3218ef88ec5f2bd83e3e57f7cc95ac9ebc9d788 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:28:09 -0600 Subject: [PATCH 05/27] expression --- packages/perseus/src/prompt-types.ts | 2 +- ...on.test.ts => expression-ai-utils.test.ts} | 20 ++++++++++++++++++- ...prompt-utils.ts => expression-ai-utils.ts} | 0 .../expression/prompt-utils.test.ts | 19 ------------------ .../src/widgets/expression/expression.tsx | 4 ++-- 5 files changed, 22 insertions(+), 23 deletions(-) rename packages/perseus/src/widget-ai-utils/expression/{expression.test.ts => expression-ai-utils.test.ts} (78%) rename packages/perseus/src/widget-ai-utils/expression/{prompt-utils.ts => expression-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/expression/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index f7c4ea90ae..35537b8f5c 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -2,7 +2,7 @@ import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/categori import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/definition-ai-utils"; import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-utils"; import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanation-ai-utils"; -import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/prompt-utils"; +import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression-ai-utils"; import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/prompt-utils"; import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/prompt-utils"; import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/expression/expression.test.ts b/packages/perseus/src/widget-ai-utils/expression/expression-ai-utils.test.ts similarity index 78% rename from packages/perseus/src/widget-ai-utils/expression/expression.test.ts rename to packages/perseus/src/widget-ai-utils/expression/expression-ai-utils.test.ts index 1d541aa252..9dcfa83d99 100644 --- a/packages/perseus/src/widget-ai-utils/expression/expression.test.ts +++ b/packages/perseus/src/widget-ai-utils/expression/expression-ai-utils.test.ts @@ -3,6 +3,8 @@ import {act} from "@testing-library/react"; import {ItemExtras} from "../../perseus-types"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./expression-ai-utils"; + import type {PerseusAnswerArea, PerseusRenderer} from "../../perseus-types"; const expression = { @@ -37,7 +39,23 @@ const expression = { hints: [], }; -describe("expression widget", () => { +describe("Expression AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + visibleLabel: "Enter an expression", + }; + + const resultJSON = getPromptJSON(renderProps, "2 + 2"); + + expect(resultJSON).toEqual({ + type: "expression", + label: "Enter an expression", + userInput: { + value: "2 + 2", + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion( diff --git a/packages/perseus/src/widget-ai-utils/expression/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/expression/expression-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/expression/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/expression/expression-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/expression/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/expression/prompt-utils.test.ts deleted file mode 100644 index bf38043ddd..0000000000 --- a/packages/perseus/src/widget-ai-utils/expression/prompt-utils.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Expression getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - visibleLabel: "Enter an expression", - }; - - const resultJSON = getPromptJSON(renderProps, "2 + 2"); - - expect(resultJSON).toEqual({ - type: "expression", - label: "Enter an expression", - userInput: { - value: "2 + 2", - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/expression/expression.tsx b/packages/perseus/src/widgets/expression/expression.tsx index f0fb5ee8a7..c50b967721 100644 --- a/packages/perseus/src/widgets/expression/expression.tsx +++ b/packages/perseus/src/widgets/expression/expression.tsx @@ -16,7 +16,7 @@ import {useDependencies} from "../../dependencies"; import * as Changeable from "../../mixins/changeable"; import {ApiOptions, ClassNames as ApiClassNames} from "../../perseus-api"; import a11y from "../../util/a11y"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/expression/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/expression/expression-ai-utils"; import getDecimalSeparator from "./get-decimal-separator"; import scoreExpression from "./score-expression"; @@ -28,7 +28,7 @@ import type { PerseusExpressionRubric, PerseusExpressionUserInput, } from "../../validation.types"; -import type {ExpressionPromptJSON} from "../../widget-ai-utils/expression/prompt-utils"; +import type {ExpressionPromptJSON} from "../../widget-ai-utils/expression/expression-ai-utils"; import type {Keys as Key, KeypadConfiguration} from "@khanacademy/math-input"; import type {PropsFor} from "@khanacademy/wonder-blocks-core"; From 09e9fd3c490050323e11bd79ae592255d84aaf41 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:30:07 -0600 Subject: [PATCH 06/27] graded group --- packages/perseus/src/prompt-types.ts | 2 +- .../graded-group-set/prompt-utils.ts | 2 +- ....test.ts => graded-group-ai-utils.test.ts} | 108 ++++++++++++++++- ...ompt-utils.ts => graded-group-ai-utils.ts} | 0 .../graded-group/prompt-utils.test.ts | 109 ------------------ .../src/widgets/graded-group/graded-group.tsx | 4 +- 6 files changed, 111 insertions(+), 114 deletions(-) rename packages/perseus/src/widget-ai-utils/graded-group/{graded-group.test.ts => graded-group-ai-utils.test.ts} (72%) rename packages/perseus/src/widget-ai-utils/graded-group/{prompt-utils.ts => graded-group-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 35537b8f5c..0fd5ba7d81 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -3,7 +3,7 @@ import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/definition import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-utils"; import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanation-ai-utils"; import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression-ai-utils"; -import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/prompt-utils"; +import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded-group-ai-utils"; import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/prompt-utils"; import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/prompt-utils"; import type {GroupPromptJSON} from "./widget-ai-utils/group/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts index b90ca809e9..8221a5b618 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts @@ -1,5 +1,5 @@ import type gradedGroupSet from "../../widgets/graded-group-set"; -import type {GradedGroupPromptJSON} from "../graded-group/prompt-utils"; +import type {GradedGroupPromptJSON} from "../graded-group/graded-group-ai-utils"; import type React from "react"; export type GradedGroupSetPromptJSON = { diff --git a/packages/perseus/src/widget-ai-utils/graded-group/graded-group.test.ts b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts similarity index 72% rename from packages/perseus/src/widget-ai-utils/graded-group/graded-group.test.ts rename to packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts index 49bd17b3fa..f64c9c4d15 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group/graded-group.test.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts @@ -5,7 +5,11 @@ import {testDependencies} from "../../../../../testing/test-dependencies"; import * as Dependencies from "../../dependencies"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./graded-group-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; +import type {CategorizerPromptJSON} from "../categorizer/categorizer-ai-utils"; +import type {ImagePromptJSON} from "../image/prompt-utils"; import type {UserEvent} from "@testing-library/user-event"; const question: PerseusRenderer = { @@ -75,7 +79,7 @@ const question: PerseusRenderer = { }, }; -describe("graded-group widget", () => { +describe("GradedGroup AI utils", () => { let userEvent: UserEvent; beforeEach(() => { @@ -88,6 +92,108 @@ describe("graded-group widget", () => { ); }); + it("it returns JSON with the expected format and fields when rendererJSON is undefined", () => { + const title = "title"; + const rendererJSON = undefined; + const hintRendererJSON = { + content: "hint content", + widgets: {}, + }; + const result = getPromptJSON(title, rendererJSON, hintRendererJSON); + + expect(result).toEqual({ + type: "graded-group", + title, + content: "", + widgets: {}, + hint: { + content: "hint content", + widgets: {}, + }, + }); + }); + + it("it returns JSON with the expected format and fields", () => { + const title = "title"; + const rendererJSON = { + title: "Metabolic strategies of bacteria", + content: + "1. **Which of the following statements about metabolic strategies of bacteria are true?**\n\n [[☃ categorizer 1]]", + images: {}, + widgets: { + "categorizer 1": { + type: "categorizer", + options: { + items: [ + "Some bacteria conduct photosynthesis and produce oxygen, much like plants.", + "Bacteria are always autotrophic but they may get energy from either light or chemical sources.", + "Some chemosynthetic bacteria introduce energy and fixed carbon into communities where photosynthesis is not possible (e.g., deep-sea vents).", + "Some bacteria live symbiotically inside of host organisms and provide the host with nutrients.", + ], + categories: ["True", "False"], + }, + userInput: { + itemToCategoryMapping: [0, 0, 0, 0], + }, + } satisfies CategorizerPromptJSON, + }, + }; + + const hintRendererJSON = { + content: "hint content", + widgets: { + "image 1": { + type: "image", + options: { + altText: "alt text", + title: "title", + caption: "caption", + imageUrl: "url", + }, + } satisfies ImagePromptJSON, + }, + }; + const result = getPromptJSON(title, rendererJSON, hintRendererJSON); + + expect(result).toEqual({ + type: "graded-group", + title, + content: + "1. **Which of the following statements about metabolic strategies of bacteria are true?**\n\n [[☃ categorizer 1]]", + images: {}, + widgets: { + "categorizer 1": { + type: "categorizer", + options: { + items: [ + "Some bacteria conduct photosynthesis and produce oxygen, much like plants.", + "Bacteria are always autotrophic but they may get energy from either light or chemical sources.", + "Some chemosynthetic bacteria introduce energy and fixed carbon into communities where photosynthesis is not possible (e.g., deep-sea vents).", + "Some bacteria live symbiotically inside of host organisms and provide the host with nutrients.", + ], + categories: ["True", "False"], + }, + userInput: { + itemToCategoryMapping: [0, 0, 0, 0], + }, + }, + }, + hint: { + content: "hint content", + widgets: { + "image 1": { + type: "image", + options: { + altText: "alt text", + title: "title", + caption: "caption", + imageUrl: "url", + }, + }, + }, + }, + }); + }); it("should get prompt json which matches the state of the UI when the hint is collapsed", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts deleted file mode 100644 index b5a39ec6bb..0000000000 --- a/packages/perseus/src/widget-ai-utils/graded-group/prompt-utils.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {CategorizerPromptJSON} from "../categorizer/categorizer-ai-utils"; -import type {ImagePromptJSON} from "../image/prompt-utils"; - -describe("GradedGroup getPromptJSON", () => { - it("it returns JSON with the expected format and fields when rendererJSON is undefined", () => { - const title = "title"; - const rendererJSON = undefined; - const hintRendererJSON = { - content: "hint content", - widgets: {}, - }; - const result = getPromptJSON(title, rendererJSON, hintRendererJSON); - - expect(result).toEqual({ - type: "graded-group", - title, - content: "", - widgets: {}, - hint: { - content: "hint content", - widgets: {}, - }, - }); - }); - - it("it returns JSON with the expected format and fields", () => { - const title = "title"; - const rendererJSON = { - title: "Metabolic strategies of bacteria", - content: - "1. **Which of the following statements about metabolic strategies of bacteria are true?**\n\n [[☃ categorizer 1]]", - images: {}, - widgets: { - "categorizer 1": { - type: "categorizer", - options: { - items: [ - "Some bacteria conduct photosynthesis and produce oxygen, much like plants.", - "Bacteria are always autotrophic but they may get energy from either light or chemical sources.", - "Some chemosynthetic bacteria introduce energy and fixed carbon into communities where photosynthesis is not possible (e.g., deep-sea vents).", - "Some bacteria live symbiotically inside of host organisms and provide the host with nutrients.", - ], - categories: ["True", "False"], - }, - userInput: { - itemToCategoryMapping: [0, 0, 0, 0], - }, - } satisfies CategorizerPromptJSON, - }, - }; - - const hintRendererJSON = { - content: "hint content", - widgets: { - "image 1": { - type: "image", - options: { - altText: "alt text", - title: "title", - caption: "caption", - imageUrl: "url", - }, - } satisfies ImagePromptJSON, - }, - }; - const result = getPromptJSON(title, rendererJSON, hintRendererJSON); - - expect(result).toEqual({ - type: "graded-group", - title, - content: - "1. **Which of the following statements about metabolic strategies of bacteria are true?**\n\n [[☃ categorizer 1]]", - images: {}, - widgets: { - "categorizer 1": { - type: "categorizer", - options: { - items: [ - "Some bacteria conduct photosynthesis and produce oxygen, much like plants.", - "Bacteria are always autotrophic but they may get energy from either light or chemical sources.", - "Some chemosynthetic bacteria introduce energy and fixed carbon into communities where photosynthesis is not possible (e.g., deep-sea vents).", - "Some bacteria live symbiotically inside of host organisms and provide the host with nutrients.", - ], - categories: ["True", "False"], - }, - userInput: { - itemToCategoryMapping: [0, 0, 0, 0], - }, - }, - }, - hint: { - content: "hint content", - widgets: { - "image 1": { - type: "image", - options: { - altText: "alt text", - title: "title", - caption: "caption", - imageUrl: "url", - }, - }, - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/graded-group/graded-group.tsx b/packages/perseus/src/widgets/graded-group/graded-group.tsx index 15777d7016..23eaeef1bc 100644 --- a/packages/perseus/src/widgets/graded-group/graded-group.tsx +++ b/packages/perseus/src/widgets/graded-group/graded-group.tsx @@ -21,7 +21,7 @@ import { tableBackgroundAccent, } from "../../styles/constants"; import a11y from "../../util/a11y"; -import {getPromptJSON} from "../../widget-ai-utils/graded-group/prompt-utils"; +import {getPromptJSON} from "../../widget-ai-utils/graded-group/graded-group-ai-utils"; import GradedGroupAnswerBar from "./graded-group-answer-bar"; @@ -36,7 +36,7 @@ import type { WidgetProps, } from "../../types"; import type {PerseusGradedGroupRubric} from "../../validation.types"; -import type {GradedGroupPromptJSON} from "../../widget-ai-utils/graded-group/prompt-utils"; +import type {GradedGroupPromptJSON} from "../../widget-ai-utils/graded-group/graded-group-ai-utils"; const GRADING_STATUSES = { ungraded: "ungraded" as const, From a2046340996782af2effba876ba955cc6ddb62f5 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:32:12 -0600 Subject: [PATCH 07/27] graded group set --- packages/perseus/src/prompt-types.ts | 2 +- ...t.ts => graded-group-set-ai-utils.test.ts} | 94 ++++++++++++++++++- ...-utils.ts => graded-group-set-ai-utils.ts} | 0 .../graded-group-set/prompt-utils.test.ts | 93 ------------------ .../graded-group-set/graded-group-set.tsx | 4 +- 5 files changed, 96 insertions(+), 97 deletions(-) rename packages/perseus/src/widget-ai-utils/graded-group-set/{graded-group-set.test.ts => graded-group-set-ai-utils.test.ts} (61%) rename packages/perseus/src/widget-ai-utils/graded-group-set/{prompt-utils.ts => graded-group-set-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 0fd5ba7d81..3c9405f2e6 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -4,7 +4,7 @@ import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-ut import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanation-ai-utils"; import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression-ai-utils"; import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded-group-ai-utils"; -import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/prompt-utils"; +import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/prompt-utils"; import type {GroupPromptJSON} from "./widget-ai-utils/group/prompt-utils"; import type {ImagePromptJSON} from "./widget-ai-utils/image/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set.test.ts b/packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set-ai-utils.test.ts similarity index 61% rename from packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set.test.ts rename to packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set-ai-utils.test.ts index 3d118638f1..56bddb3c41 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set.test.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set-ai-utils.test.ts @@ -6,9 +6,11 @@ import * as Dependencies from "../../dependencies"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; import {article1} from "../../widgets/graded-group-set/graded-group-set.testdata"; +import {getPromptJSON} from "./graded-group-set-ai-utils"; + import type {UserEvent} from "@testing-library/user-event"; -describe("graded-group-set widget", () => { +describe("GradedGroupSet AI utils", () => { let userEvent: UserEvent; beforeEach(() => { @@ -21,6 +23,96 @@ describe("graded-group-set widget", () => { ); }); + it("it returns JSON with the expected format and fields", () => { + const activeGroupJSON: any = { + title: "Problem 1a", + content: "$0.5 + 0.4 =$ [[☃ numeric-input 1]]", + widgets: { + "numeric-input 1": { + type: "numeric-input", + alignment: "default", + options: { + labelText: "Numeric input label", + size: "Normal", + coefficient: false, + static: false, + }, + userInput: { + currentValue: "42", + }, + }, + }, + hint: { + content: + "There are many ways to solve this problem. Let's see two student solutions.\n\n###Student A's solution:\n\nI thought in terms of tenths.\n\n$\\phantom{=}0.5 + 0.4$\n\n$=5$ tenths $+ ~4$ tenths\n\n$=9$ tenths\n\n$=0.9$\n\n###Student B's solution:\n\nI used tenths grids.\n\n[[☃ image 1]]\n\n[[☃ image 2]]\n\n[[☃ image 3]]\n\n$\\blueD{0.5} + \\greenD{0.4} = 0.9$\n\n###The answer:\n\n$0.5 + 0.4 = 0.9$", + widgets: { + "image 1": { + options: { + title: "", + backgroundImage: { + url: "web+graphie://ka-perseus-graphie.s3.amazonaws.com/2a56a60275b7227ed9c5b89e489587c8cb13eb7b", + }, + labels: [], + alt: "A square divided into 10 rows to show tenths. 5 of the rows are shaded to represent 5 tenths.", + caption: "", + }, + type: "image", + }, + }, + }, + }; + + const renderProps: any = { + gradedGroups: [{title: "Problem 1a"}, {title: "Problem 1b"}], + }; + + const result = getPromptJSON(renderProps, activeGroupJSON); + + expect(result).toEqual({ + type: "graded-group-set", + options: { + groupCount: 2, + currentGroup: { + title: "Problem 1a", + content: "$0.5 + 0.4 =$ [[☃ numeric-input 1]]", + widgets: { + "numeric-input 1": { + type: "numeric-input", + alignment: "default", + options: { + labelText: "Numeric input label", + size: "Normal", + coefficient: false, + static: false, + }, + userInput: { + currentValue: "42", + }, + }, + }, + hint: { + content: + "There are many ways to solve this problem. Let's see two student solutions.\n\n###Student A's solution:\n\nI thought in terms of tenths.\n\n$\\phantom{=}0.5 + 0.4$\n\n$=5$ tenths $+ ~4$ tenths\n\n$=9$ tenths\n\n$=0.9$\n\n###Student B's solution:\n\nI used tenths grids.\n\n[[☃ image 1]]\n\n[[☃ image 2]]\n\n[[☃ image 3]]\n\n$\\blueD{0.5} + \\greenD{0.4} = 0.9$\n\n###The answer:\n\n$0.5 + 0.4 = 0.9$", + widgets: { + "image 1": { + options: { + title: "", + backgroundImage: { + url: "web+graphie://ka-perseus-graphie.s3.amazonaws.com/2a56a60275b7227ed9c5b89e489587c8cb13eb7b", + }, + labels: [], + alt: "A square divided into 10 rows to show tenths. 5 of the rows are shaded to represent 5 tenths.", + caption: "", + }, + type: "image", + }, + }, + }, + }, + }, + }); + }); + it("should get prompt json which matches the state of the UI when the hint is collapsed", async () => { // Arrange const {renderer} = renderQuestion(article1); diff --git a/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/graded-group-set/graded-group-set-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.test.ts deleted file mode 100644 index 54a8335e4d..0000000000 --- a/packages/perseus/src/widget-ai-utils/graded-group-set/prompt-utils.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("GradedGroupSet getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const activeGroupJSON: any = { - title: "Problem 1a", - content: "$0.5 + 0.4 =$ [[☃ numeric-input 1]]", - widgets: { - "numeric-input 1": { - type: "numeric-input", - alignment: "default", - options: { - labelText: "Numeric input label", - size: "Normal", - coefficient: false, - static: false, - }, - userInput: { - currentValue: "42", - }, - }, - }, - hint: { - content: - "There are many ways to solve this problem. Let's see two student solutions.\n\n###Student A's solution:\n\nI thought in terms of tenths.\n\n$\\phantom{=}0.5 + 0.4$\n\n$=5$ tenths $+ ~4$ tenths\n\n$=9$ tenths\n\n$=0.9$\n\n###Student B's solution:\n\nI used tenths grids.\n\n[[☃ image 1]]\n\n[[☃ image 2]]\n\n[[☃ image 3]]\n\n$\\blueD{0.5} + \\greenD{0.4} = 0.9$\n\n###The answer:\n\n$0.5 + 0.4 = 0.9$", - widgets: { - "image 1": { - options: { - title: "", - backgroundImage: { - url: "web+graphie://ka-perseus-graphie.s3.amazonaws.com/2a56a60275b7227ed9c5b89e489587c8cb13eb7b", - }, - labels: [], - alt: "A square divided into 10 rows to show tenths. 5 of the rows are shaded to represent 5 tenths.", - caption: "", - }, - type: "image", - }, - }, - }, - }; - - const renderProps: any = { - gradedGroups: [{title: "Problem 1a"}, {title: "Problem 1b"}], - }; - - const result = getPromptJSON(renderProps, activeGroupJSON); - - expect(result).toEqual({ - type: "graded-group-set", - options: { - groupCount: 2, - currentGroup: { - title: "Problem 1a", - content: "$0.5 + 0.4 =$ [[☃ numeric-input 1]]", - widgets: { - "numeric-input 1": { - type: "numeric-input", - alignment: "default", - options: { - labelText: "Numeric input label", - size: "Normal", - coefficient: false, - static: false, - }, - userInput: { - currentValue: "42", - }, - }, - }, - hint: { - content: - "There are many ways to solve this problem. Let's see two student solutions.\n\n###Student A's solution:\n\nI thought in terms of tenths.\n\n$\\phantom{=}0.5 + 0.4$\n\n$=5$ tenths $+ ~4$ tenths\n\n$=9$ tenths\n\n$=0.9$\n\n###Student B's solution:\n\nI used tenths grids.\n\n[[☃ image 1]]\n\n[[☃ image 2]]\n\n[[☃ image 3]]\n\n$\\blueD{0.5} + \\greenD{0.4} = 0.9$\n\n###The answer:\n\n$0.5 + 0.4 = 0.9$", - widgets: { - "image 1": { - options: { - title: "", - backgroundImage: { - url: "web+graphie://ka-perseus-graphie.s3.amazonaws.com/2a56a60275b7227ed9c5b89e489587c8cb13eb7b", - }, - labels: [], - alt: "A square divided into 10 rows to show tenths. 5 of the rows are shaded to represent 5 tenths.", - caption: "", - }, - type: "image", - }, - }, - }, - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx b/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx index 6aebbc29ad..25b5ef2c43 100644 --- a/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx +++ b/packages/perseus/src/widgets/graded-group-set/graded-group-set.tsx @@ -17,7 +17,7 @@ import { negativePhoneMargin, } from "../../styles/constants"; import a11y from "../../util/a11y"; -import {getPromptJSON} from "../../widget-ai-utils/graded-group-set/prompt-utils"; +import {getPromptJSON} from "../../widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; import {GradedGroup} from "../graded-group/graded-group"; import type { @@ -26,7 +26,7 @@ import type { } from "../../perseus-types"; import type {FocusPath, Widget, WidgetExports, WidgetProps} from "../../types"; import type {PerseusGradedGroupSetRubric} from "../../validation.types"; -import type {GradedGroupSetPromptJSON} from "../../widget-ai-utils/graded-group-set/prompt-utils"; +import type {GradedGroupSetPromptJSON} from "../../widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; type IndicatorsProps = { currentGroup: number; From c0551a70c5cc8843d11b2fc77d129ecb00d25001 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:35:32 -0600 Subject: [PATCH 08/27] grapher --- packages/perseus/src/prompt-types.ts | 2 +- ...apher.test.ts => grapher-ai-utils.test.ts} | 94 ++++++++++++++++++- .../{prompt-utils.ts => grapher-ai-utils.ts} | 0 .../grapher/prompt-utils.test.ts | 93 ------------------ .../perseus/src/widgets/grapher/grapher.tsx | 4 +- 5 files changed, 96 insertions(+), 97 deletions(-) rename packages/perseus/src/widget-ai-utils/grapher/{grapher.test.ts => grapher-ai-utils.test.ts} (50%) rename packages/perseus/src/widget-ai-utils/grapher/{prompt-utils.ts => grapher-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/grapher/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 3c9405f2e6..846b821daa 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -5,7 +5,7 @@ import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanat import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression-ai-utils"; import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded-group-ai-utils"; import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; -import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/prompt-utils"; +import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/grapher-ai-utils"; import type {GroupPromptJSON} from "./widget-ai-utils/group/prompt-utils"; import type {ImagePromptJSON} from "./widget-ai-utils/image/prompt-utils"; import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/grapher/grapher.test.ts b/packages/perseus/src/widget-ai-utils/grapher/grapher-ai-utils.test.ts similarity index 50% rename from packages/perseus/src/widget-ai-utils/grapher/grapher.test.ts rename to packages/perseus/src/widget-ai-utils/grapher/grapher-ai-utils.test.ts index bb2b6e7889..d16b21600d 100644 --- a/packages/perseus/src/widget-ai-utils/grapher/grapher.test.ts +++ b/packages/perseus/src/widget-ai-utils/grapher/grapher-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./grapher-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; const question: PerseusRenderer = { @@ -50,7 +52,97 @@ const question: PerseusRenderer = { }, }; -describe("grapher widget", () => { +describe("Grapher AI utils", () => { + it("it returns JSON with the expected format and fields for a linear graph", () => { + const renderProps: any = { + availableTypes: ["linear"], + graph: { + range: [0, 10, 0, 10], + labels: ["x", "y"], + step: 1, + gridStep: 1, + snapStep: 1, + backgroundImage: {url: "http://khanaacademy.org/image.jpg"}, + }, + }; + + const userInput: any = { + type: "linear", + coords: [ + [0, 0], + [1, 1], + ], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "grapher", + options: { + availableTypes: ["linear"], + range: [0, 10, 0, 10], + labels: ["x", "y"], + tickStep: 1, + gridStep: 1, + snapStep: 1, + backgroundImageUrl: "http://khanaacademy.org/image.jpg", + }, + userInput: { + type: "linear", + coords: [ + [0, 0], + [1, 1], + ], + }, + }); + }); + + it('it returns JSON with the expected format and fields for a "logarithm" graph', () => { + const renderProps: any = { + availableTypes: ["lograithm"], + graph: { + range: [0, 10, 0, 10], + labels: ["x", "y"], + step: 1, + gridStep: 1, + snapStep: 1, + backgroundImage: {url: ""}, + }, + }; + + const userInput: any = { + type: "logarithm", + coords: [ + [0, 0], + [1, 1], + ], + asymptote: [-1, 4], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "grapher", + options: { + availableTypes: ["lograithm"], + range: [0, 10, 0, 10], + labels: ["x", "y"], + tickStep: 1, + gridStep: 1, + snapStep: 1, + backgroundImageUrl: "", + }, + userInput: { + type: "logarithm", + coords: [ + [0, 0], + [1, 1], + ], + asymptote: [-1, 4], + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/grapher/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/grapher/grapher-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/grapher/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/grapher/grapher-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/grapher/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/grapher/prompt-utils.test.ts deleted file mode 100644 index fdbb58e7b2..0000000000 --- a/packages/perseus/src/widget-ai-utils/grapher/prompt-utils.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Grapher getPromptJSON", () => { - it("it returns JSON with the expected format and fields for a linear graph", () => { - const renderProps: any = { - availableTypes: ["linear"], - graph: { - range: [0, 10, 0, 10], - labels: ["x", "y"], - step: 1, - gridStep: 1, - snapStep: 1, - backgroundImage: {url: "http://khanaacademy.org/image.jpg"}, - }, - }; - - const userInput: any = { - type: "linear", - coords: [ - [0, 0], - [1, 1], - ], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "grapher", - options: { - availableTypes: ["linear"], - range: [0, 10, 0, 10], - labels: ["x", "y"], - tickStep: 1, - gridStep: 1, - snapStep: 1, - backgroundImageUrl: "http://khanaacademy.org/image.jpg", - }, - userInput: { - type: "linear", - coords: [ - [0, 0], - [1, 1], - ], - }, - }); - }); - - it('it returns JSON with the expected format and fields for a "logarithm" graph', () => { - const renderProps: any = { - availableTypes: ["lograithm"], - graph: { - range: [0, 10, 0, 10], - labels: ["x", "y"], - step: 1, - gridStep: 1, - snapStep: 1, - backgroundImage: {url: ""}, - }, - }; - - const userInput: any = { - type: "logarithm", - coords: [ - [0, 0], - [1, 1], - ], - asymptote: [-1, 4], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "grapher", - options: { - availableTypes: ["lograithm"], - range: [0, 10, 0, 10], - labels: ["x", "y"], - tickStep: 1, - gridStep: 1, - snapStep: 1, - backgroundImageUrl: "", - }, - userInput: { - type: "logarithm", - coords: [ - [0, 0], - [1, 1], - ], - asymptote: [-1, 4], - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/grapher/grapher.tsx b/packages/perseus/src/widgets/grapher/grapher.tsx index c71bb5aef2..b84f87c687 100644 --- a/packages/perseus/src/widgets/grapher/grapher.tsx +++ b/packages/perseus/src/widgets/grapher/grapher.tsx @@ -18,7 +18,7 @@ import KhanColors from "../../util/colors"; import {getInteractiveBoxFromSizeClass} from "../../util/sizing-utils"; /* Graphie and relevant components. */ /* Mixins. */ -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/grapher/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/grapher/grapher-ai-utils"; import scoreGrapher from "./score-grapher"; import { @@ -40,7 +40,7 @@ import type { PerseusGrapherRubric, PerseusGrapherUserInput, } from "../../validation.types"; -import type {GrapherPromptJSON} from "../../widget-ai-utils/grapher/prompt-utils"; +import type {GrapherPromptJSON} from "../../widget-ai-utils/grapher/grapher-ai-utils"; import type {PropsFor} from "@khanacademy/wonder-blocks-core"; // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'. From a90a589f8efd6ba897e4ff901ef0b51f1c38bf30 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:37:20 -0600 Subject: [PATCH 09/27] group --- packages/perseus/src/prompt-types.ts | 2 +- .../{group.test.ts => group-ai-utils.test.ts} | 95 ++++++++++++++++++- ...testdata.ts => group-ai-utils.testdata.ts} | 0 .../{prompt-utils.ts => group-ai-utils.ts} | 0 .../group/prompt-utils.test.ts | 93 ------------------ packages/perseus/src/widgets/group/group.tsx | 4 +- 6 files changed, 96 insertions(+), 98 deletions(-) rename packages/perseus/src/widget-ai-utils/group/{group.test.ts => group-ai-utils.test.ts} (51%) rename packages/perseus/src/widget-ai-utils/group/{group.testdata.ts => group-ai-utils.testdata.ts} (100%) rename packages/perseus/src/widget-ai-utils/group/{prompt-utils.ts => group-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/group/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 846b821daa..2a079ddd69 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -6,7 +6,7 @@ import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded-group-ai-utils"; import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/grapher-ai-utils"; -import type {GroupPromptJSON} from "./widget-ai-utils/group/prompt-utils"; +import type {GroupPromptJSON} from "./widget-ai-utils/group/group-ai-utils"; import type {ImagePromptJSON} from "./widget-ai-utils/image/prompt-utils"; import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/prompt-utils"; import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/group/group.test.ts b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts similarity index 51% rename from packages/perseus/src/widget-ai-utils/group/group.test.ts rename to packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts index 96c338eb5c..e65317a325 100644 --- a/packages/perseus/src/widget-ai-utils/group/group.test.ts +++ b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts @@ -1,8 +1,99 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; -import {question1} from "./group.testdata"; +import {getPromptJSON} from "./group-ai-utils"; +import {question1} from "./group-ai-utils.testdata"; + +import type {RendererPromptJSON} from "../../prompt-types"; + +describe("Group AI utils", () => { + it("should return a GroupPromptJSON with default values when rendererJSON is undefined", () => { + const result = getPromptJSON(undefined); + + expect(result).toEqual({ + type: "group", + content: "", + widgets: {}, + }); + }); + + it("should return a GroupPromptJSON when rendererJSON has multiple widgets", () => { + // Arrange + const rendererJSON: RendererPromptJSON = { + content: + "**In one week, how many more hours are in the periods with a $35$ percent discount than in the periods with the regular price?**\n\n[[☃ radio 1]]", + widgets: { + "radio 1": { + type: "radio", + hasNoneOfTheAbove: false, + options: [ + {value: "$45$"}, + {value: "$42$"}, + {value: "$30$"}, + {value: "$18$"}, + {value: "$15$"}, + ], + userInput: { + selectedOptions: [false, false, false, false, false], + }, + }, + "matrix 1": { + type: "matrix", + options: { + height: 3, + width: 3, + }, + userInput: { + answerRows: [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ], + }, + }, + }, + }; + + // Act + const result = getPromptJSON(rendererJSON); + + // Assert + expect(result).toEqual({ + type: "group", + content: + "**In one week, how many more hours are in the periods with a $35$ percent discount than in the periods with the regular price?**\n\n[[☃ radio 1]]", + widgets: { + "radio 1": { + type: "radio", + hasNoneOfTheAbove: false, + options: [ + {value: "$45$"}, + {value: "$42$"}, + {value: "$30$"}, + {value: "$18$"}, + {value: "$15$"}, + ], + userInput: { + selectedOptions: [false, false, false, false, false], + }, + }, + "matrix 1": { + type: "matrix", + options: { + height: 3, + width: 3, + }, + userInput: { + answerRows: [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ], + }, + }, + }, + }); + }); -describe("group widget", () => { it("Should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/group/group.testdata.ts b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.testdata.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/group/group.testdata.ts rename to packages/perseus/src/widget-ai-utils/group/group-ai-utils.testdata.ts diff --git a/packages/perseus/src/widget-ai-utils/group/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/group/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/group/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/group/prompt-utils.test.ts deleted file mode 100644 index d5a24631d7..0000000000 --- a/packages/perseus/src/widget-ai-utils/group/prompt-utils.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {RendererPromptJSON} from "../../prompt-types"; - -describe("Group getPromptJSON", () => { - it("should return a GroupPromptJSON with default values when rendererJSON is undefined", () => { - const result = getPromptJSON(undefined); - - expect(result).toEqual({ - type: "group", - content: "", - widgets: {}, - }); - }); - - it("should return a GroupPromptJSON when rendererJSON has multiple widgets", () => { - // Arrange - const rendererJSON: RendererPromptJSON = { - content: - "**In one week, how many more hours are in the periods with a $35$ percent discount than in the periods with the regular price?**\n\n[[☃ radio 1]]", - widgets: { - "radio 1": { - type: "radio", - hasNoneOfTheAbove: false, - options: [ - {value: "$45$"}, - {value: "$42$"}, - {value: "$30$"}, - {value: "$18$"}, - {value: "$15$"}, - ], - userInput: { - selectedOptions: [false, false, false, false, false], - }, - }, - "matrix 1": { - type: "matrix", - options: { - height: 3, - width: 3, - }, - userInput: { - answerRows: [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ], - }, - }, - }, - }; - - // Act - const result = getPromptJSON(rendererJSON); - - // Assert - expect(result).toEqual({ - type: "group", - content: - "**In one week, how many more hours are in the periods with a $35$ percent discount than in the periods with the regular price?**\n\n[[☃ radio 1]]", - widgets: { - "radio 1": { - type: "radio", - hasNoneOfTheAbove: false, - options: [ - {value: "$45$"}, - {value: "$42$"}, - {value: "$30$"}, - {value: "$18$"}, - {value: "$15$"}, - ], - userInput: { - selectedOptions: [false, false, false, false, false], - }, - }, - "matrix 1": { - type: "matrix", - options: { - height: 3, - width: 3, - }, - userInput: { - answerRows: [ - [1, 2, 3], - [4, 5, 6], - [7, 8, 9], - ], - }, - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/group/group.tsx b/packages/perseus/src/widgets/group/group.tsx index f8f512a42d..6c5a3a8fdf 100644 --- a/packages/perseus/src/widgets/group/group.tsx +++ b/packages/perseus/src/widgets/group/group.tsx @@ -6,7 +6,7 @@ import {PerseusI18nContext} from "../../components/i18n-context"; import * as Changeable from "../../mixins/changeable"; import {ApiOptions} from "../../perseus-api"; import Renderer from "../../renderer"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/group/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/group/group-ai-utils"; import type {PerseusGroupWidgetOptions} from "../../perseus-types"; import type { @@ -18,7 +18,7 @@ import type { WidgetProps, } from "../../types"; import type {PerseusGroupRubric, UserInputArray} from "../../validation.types"; -import type {GroupPromptJSON} from "../../widget-ai-utils/group/prompt-utils"; +import type {GroupPromptJSON} from "../../widget-ai-utils/group/group-ai-utils"; type RenderProps = PerseusGroupWidgetOptions; // exports has no 'transform' type Props = WidgetProps; From 07236469285473b45c43a439b4d0b20474dfcfc0 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:38:56 -0600 Subject: [PATCH 10/27] iframe --- .../{iframe.test.ts => iframe-ai-utils.test.ts} | 14 +++++++++++++- .../iframe/{prompt-utils.ts => iframe-ai-utils.ts} | 0 .../widget-ai-utils/iframe/prompt-utils.test.ts | 13 ------------- packages/perseus/src/widgets/iframe/iframe.tsx | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) rename packages/perseus/src/widget-ai-utils/iframe/{iframe.test.ts => iframe-ai-utils.test.ts} (83%) rename packages/perseus/src/widget-ai-utils/iframe/{prompt-utils.ts => iframe-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/iframe/prompt-utils.test.ts diff --git a/packages/perseus/src/widget-ai-utils/iframe/iframe.test.ts b/packages/perseus/src/widget-ai-utils/iframe/iframe-ai-utils.test.ts similarity index 83% rename from packages/perseus/src/widget-ai-utils/iframe/iframe.test.ts rename to packages/perseus/src/widget-ai-utils/iframe/iframe-ai-utils.test.ts index b21be78cd7..5bc2541d52 100644 --- a/packages/perseus/src/widget-ai-utils/iframe/iframe.test.ts +++ b/packages/perseus/src/widget-ai-utils/iframe/iframe-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./iframe-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; const question1: PerseusRenderer = { @@ -36,7 +38,17 @@ const question1: PerseusRenderer = { }, }; -describe("iframe widget", () => { +describe("Iframe AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const resultJSON = getPromptJSON(); + + expect(resultJSON).toEqual({ + type: "iframe", + isSupported: false, + message: "", + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1, {isMobile: false}); diff --git a/packages/perseus/src/widget-ai-utils/iframe/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/iframe/iframe-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/iframe/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/iframe/iframe-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/iframe/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/iframe/prompt-utils.test.ts deleted file mode 100644 index b742018e18..0000000000 --- a/packages/perseus/src/widget-ai-utils/iframe/prompt-utils.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Iframe getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const resultJSON = getPromptJSON(); - - expect(resultJSON).toEqual({ - type: "iframe", - isSupported: false, - message: "", - }); - }); -}); diff --git a/packages/perseus/src/widgets/iframe/iframe.tsx b/packages/perseus/src/widgets/iframe/iframe.tsx index 3bd87caf7e..664d0c572e 100644 --- a/packages/perseus/src/widgets/iframe/iframe.tsx +++ b/packages/perseus/src/widgets/iframe/iframe.tsx @@ -14,7 +14,7 @@ import _ from "underscore"; import {getDependencies} from "../../dependencies"; import * as Changeable from "../../mixins/changeable"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/iframe/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/iframe/iframe-ai-utils"; import {scoreIframe} from "./score-iframe"; From 9cfd28e5f06848a9f7fdbf1bf408b2077d97a5e1 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:41:42 -0600 Subject: [PATCH 11/27] image and input-number --- packages/perseus/src/prompt-types.ts | 4 +-- .../graded-group-ai-utils.test.ts | 2 +- .../{image.test.ts => image-ai-utils.test.ts} | 27 ++++++++++++++++- .../{prompt-utils.ts => image-ai-utils.ts} | 0 .../image/prompt-utils.test.ts | 26 ----------------- ....test.ts => input-number-ai-utils.test.ts} | 29 ++++++++++++++++++- ...ompt-utils.ts => input-number-ai-utils.ts} | 0 .../input-number/prompt-utils.test.ts | 29 ------------------- packages/perseus/src/widgets/image/image.tsx | 4 +-- .../src/widgets/input-number/input-number.tsx | 4 +-- 10 files changed, 61 insertions(+), 64 deletions(-) rename packages/perseus/src/widget-ai-utils/image/{image.test.ts => image-ai-utils.test.ts} (78%) rename packages/perseus/src/widget-ai-utils/image/{prompt-utils.ts => image-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/image/prompt-utils.test.ts rename packages/perseus/src/widget-ai-utils/input-number/{input-number.test.ts => input-number-ai-utils.test.ts} (75%) rename packages/perseus/src/widget-ai-utils/input-number/{prompt-utils.ts => input-number-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/input-number/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 2a079ddd69..d26cc6aff5 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -7,8 +7,8 @@ import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded- import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/grapher-ai-utils"; import type {GroupPromptJSON} from "./widget-ai-utils/group/group-ai-utils"; -import type {ImagePromptJSON} from "./widget-ai-utils/image/prompt-utils"; -import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/prompt-utils"; +import type {ImagePromptJSON} from "./widget-ai-utils/image/image-ai-utils"; +import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-number-ai-utils"; import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/prompt-utils"; import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/prompt-utils"; import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts index f64c9c4d15..e785a29c35 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.test.ts @@ -9,7 +9,7 @@ import {getPromptJSON} from "./graded-group-ai-utils"; import type {PerseusRenderer} from "../../perseus-types"; import type {CategorizerPromptJSON} from "../categorizer/categorizer-ai-utils"; -import type {ImagePromptJSON} from "../image/prompt-utils"; +import type {ImagePromptJSON} from "../image/image-ai-utils"; import type {UserEvent} from "@testing-library/user-event"; const question: PerseusRenderer = { diff --git a/packages/perseus/src/widget-ai-utils/image/image.test.ts b/packages/perseus/src/widget-ai-utils/image/image-ai-utils.test.ts similarity index 78% rename from packages/perseus/src/widget-ai-utils/image/image.test.ts rename to packages/perseus/src/widget-ai-utils/image/image-ai-utils.test.ts index 227da3a1d9..15a2c9bbb8 100644 --- a/packages/perseus/src/widget-ai-utils/image/image.test.ts +++ b/packages/perseus/src/widget-ai-utils/image/image-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./image-ai-utils"; + import type {ImageWidget} from "../../perseus-types"; const question = { @@ -37,7 +39,30 @@ const question = { }, } as const; -describe("image widget", () => { +describe("Image AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + alt: "An image of a textbook", + title: "Textbook", + caption: "A textbook", + backgroundImage: { + url: "https://www.khanacademy.org/some-image.png", + }, + }; + + const resultJSON = getPromptJSON(renderProps); + + expect(resultJSON).toEqual({ + type: "image", + options: { + altText: "An image of a textbook", + title: "Textbook", + caption: "A textbook", + imageUrl: "https://www.khanacademy.org/some-image.png", + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question, {isMobile: false}); diff --git a/packages/perseus/src/widget-ai-utils/image/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/image/image-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/image/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/image/image-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/image/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/image/prompt-utils.test.ts deleted file mode 100644 index aa4b13656d..0000000000 --- a/packages/perseus/src/widget-ai-utils/image/prompt-utils.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Image getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - alt: "An image of a textbook", - title: "Textbook", - caption: "A textbook", - backgroundImage: { - url: "https://www.khanacademy.org/some-image.png", - }, - }; - - const resultJSON = getPromptJSON(renderProps); - - expect(resultJSON).toEqual({ - type: "image", - options: { - altText: "An image of a textbook", - title: "Textbook", - caption: "A textbook", - imageUrl: "https://www.khanacademy.org/some-image.png", - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/input-number/input-number.test.ts b/packages/perseus/src/widget-ai-utils/input-number/input-number-ai-utils.test.ts similarity index 75% rename from packages/perseus/src/widget-ai-utils/input-number/input-number.test.ts rename to packages/perseus/src/widget-ai-utils/input-number/input-number-ai-utils.test.ts index 7e51c2ab5c..73316c1aa3 100644 --- a/packages/perseus/src/widget-ai-utils/input-number/input-number.test.ts +++ b/packages/perseus/src/widget-ai-utils/input-number/input-number-ai-utils.test.ts @@ -3,7 +3,10 @@ import {userEvent as userEventLib} from "@testing-library/user-event"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./input-number-ai-utils"; + import type {InputNumberWidget, PerseusRenderer} from "../../perseus-types"; +import type {PerseusInputNumberUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; const question: PerseusRenderer = { @@ -32,7 +35,7 @@ const question: PerseusRenderer = { }, }; -describe("input-number widget", () => { +describe("InputNumber AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -40,6 +43,30 @@ describe("input-number widget", () => { }); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + simplify: "optional", + answerType: "integer", + }; + + const userInput: PerseusInputNumberUserInput = { + currentValue: "123", + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "input-number", + options: { + simplify: "optional", + answerType: "integer", + }, + userInput: { + value: "123", + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/input-number/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/input-number/input-number-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/input-number/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/input-number/input-number-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/input-number/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/input-number/prompt-utils.test.ts deleted file mode 100644 index f6c9ff60e3..0000000000 --- a/packages/perseus/src/widget-ai-utils/input-number/prompt-utils.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {PerseusInputNumberUserInput} from "../../validation.types"; - -describe("InputNumber getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - simplify: "optional", - answerType: "integer", - }; - - const userInput: PerseusInputNumberUserInput = { - currentValue: "123", - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "input-number", - options: { - simplify: "optional", - answerType: "integer", - }, - userInput: { - value: "123", - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/image/image.tsx b/packages/perseus/src/widgets/image/image.tsx index 9f439bf628..29208c1065 100644 --- a/packages/perseus/src/widgets/image/image.tsx +++ b/packages/perseus/src/widgets/image/image.tsx @@ -8,12 +8,12 @@ import {PerseusI18nContext} from "../../components/i18n-context"; import SvgImage from "../../components/svg-image"; import * as Changeable from "../../mixins/changeable"; import Renderer from "../../renderer"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/image/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/image/image-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import type {Range, PerseusImageWidgetOptions} from "../../perseus-types"; import type {ChangeFn, WidgetExports, WidgetProps, Widget} from "../../types"; -import type {ImagePromptJSON} from "../../widget-ai-utils/image/prompt-utils"; +import type {ImagePromptJSON} from "../../widget-ai-utils/image/image-ai-utils"; const defaultBoxSize = 400; const defaultRange: Range = [0, 10]; diff --git a/packages/perseus/src/widgets/input-number/input-number.tsx b/packages/perseus/src/widgets/input-number/input-number.tsx index f2c7bd6216..0799bf2d75 100644 --- a/packages/perseus/src/widgets/input-number/input-number.tsx +++ b/packages/perseus/src/widgets/input-number/input-number.tsx @@ -8,7 +8,7 @@ import {PerseusI18nContext} from "../../components/i18n-context"; import InputWithExamples from "../../components/input-with-examples"; import SimpleKeypadInput from "../../components/simple-keypad-input"; import {ApiOptions} from "../../perseus-api"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/input-number/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/input-number/input-number-ai-utils"; import scoreInputNumber, {answerTypes} from "./score-input-number"; @@ -19,7 +19,7 @@ import type { PerseusInputNumberRubric, PerseusInputNumberUserInput, } from "../../validation.types"; -import type {InputNumberPromptJSON} from "../../widget-ai-utils/input-number/prompt-utils"; +import type {InputNumberPromptJSON} from "../../widget-ai-utils/input-number/input-number-ai-utils"; const formExamples = { integer: function (options, strings: PerseusStrings) { From 9c7a967a78c28f2b423c13acbd9e672847328dec Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:43:45 -0600 Subject: [PATCH 12/27] interaction --- ...action.test.ts => interaction-ai-utils.test.ts} | 14 +++++++++++++- .../{prompt-utils.ts => interaction-ai-utils.ts} | 0 .../interaction/prompt-utils.test.ts | 13 ------------- .../src/widgets/interaction/interaction.tsx | 2 +- 4 files changed, 14 insertions(+), 15 deletions(-) rename packages/perseus/src/widget-ai-utils/interaction/{interaction.test.ts => interaction-ai-utils.test.ts} (96%) rename packages/perseus/src/widget-ai-utils/interaction/{prompt-utils.ts => interaction-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/interaction/prompt-utils.test.ts diff --git a/packages/perseus/src/widget-ai-utils/interaction/interaction.test.ts b/packages/perseus/src/widget-ai-utils/interaction/interaction-ai-utils.test.ts similarity index 96% rename from packages/perseus/src/widget-ai-utils/interaction/interaction.test.ts rename to packages/perseus/src/widget-ai-utils/interaction/interaction-ai-utils.test.ts index 13d342d98a..b434dd94db 100644 --- a/packages/perseus/src/widget-ai-utils/interaction/interaction.test.ts +++ b/packages/perseus/src/widget-ai-utils/interaction/interaction-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./interaction-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; const question1: PerseusRenderer = { @@ -262,7 +264,17 @@ const question1: PerseusRenderer = { }, }; -describe("interaction widget", () => { +describe("Interaction AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const resultJSON = getPromptJSON(); + + expect(resultJSON).toEqual({ + type: "interaction", + isSupported: false, + message: "", + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/interaction/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/interaction/interaction-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/interaction/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/interaction/interaction-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/interaction/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/interaction/prompt-utils.test.ts deleted file mode 100644 index 788eeae1a6..0000000000 --- a/packages/perseus/src/widget-ai-utils/interaction/prompt-utils.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Interaction getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const resultJSON = getPromptJSON(); - - expect(resultJSON).toEqual({ - type: "interaction", - isSupported: false, - message: "", - }); - }); -}); diff --git a/packages/perseus/src/widgets/interaction/interaction.tsx b/packages/perseus/src/widgets/interaction/interaction.tsx index ed16636efe..d27beee785 100644 --- a/packages/perseus/src/widgets/interaction/interaction.tsx +++ b/packages/perseus/src/widgets/interaction/interaction.tsx @@ -8,7 +8,7 @@ import _ from "underscore"; import Graphie from "../../components/graphie"; import * as Changeable from "../../mixins/changeable"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/interaction/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/interaction/interaction-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import type {Coord} from "../../interactive2/types"; From a1a302df36522b41bd941fe2a3625cf121b8d538 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:46:05 -0600 Subject: [PATCH 13/27] interactive graph --- ....ts => interactive-graph-ai-utils.test.ts} | 445 +++++++++++++++++- ...utils.ts => interactive-graph-ai-utils.ts} | 0 .../interactive-graph.test.ts | 442 ----------------- .../perseus/src/widgets/interactive-graph.tsx | 4 +- 4 files changed, 445 insertions(+), 446 deletions(-) rename packages/perseus/src/widget-ai-utils/interactive-graph/{prompt-utils.test.ts => interactive-graph-ai-utils.test.ts} (50%) rename packages/perseus/src/widget-ai-utils/interactive-graph/{prompt-utils.ts => interactive-graph-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph.test.ts diff --git a/packages/perseus/src/widget-ai-utils/interactive-graph/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.test.ts similarity index 50% rename from packages/perseus/src/widget-ai-utils/interactive-graph/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.test.ts index a9e8301b84..b4632503a1 100644 --- a/packages/perseus/src/widget-ai-utils/interactive-graph/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.test.ts @@ -1,6 +1,36 @@ -import {getPromptJSON} from "./prompt-utils"; +import {userEvent as userEventLib} from "@testing-library/user-event"; + +import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import { + angleQuestion, + circleQuestion, + linearQuestion, + linearSystemQuestion, + pointQuestion, + polygonQuestion, + quadraticQuestion, + rayQuestion, + segmentQuestion, + sinusoidQuestion, +} from "../../widgets/interactive-graphs/interactive-graph.testdata"; +import {trueForAllMafsSupportedGraphTypes} from "../../widgets/interactive-graphs/mafs-supported-graph-types"; + +import {getPromptJSON} from "./interactive-graph-ai-utils"; + +import type {UserEvent} from "@testing-library/user-event"; + +const apiOptions = { + flags: {mafs: trueForAllMafsSupportedGraphTypes}, +}; + +describe("InteractiveGraph AI utils", () => { + let userEvent: UserEvent; + beforeEach(() => { + userEvent = userEventLib.setup({ + advanceTimers: jest.advanceTimersByTime, + }); + }); -describe("getPromptJSON", () => { it("should return JSON for an angle graph", () => { const renderProps: any = { graph: { @@ -590,4 +620,415 @@ describe("getPromptJSON", () => { getPromptJSON(renderProps, userInput); }).toThrow("Unhandled case for 'fake-graph-type'"); }); + + it("should get prompt JSON for an angle graph", async () => { + // Arrange + const {renderer} = renderQuestion(angleQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "**Drag the vertex of the angle to place the vertex at point $\\text{A}$.** \n\n**Drag another point on the angle to make one of the rays go through point $\\text{B}$.**\n\n**Make the other ray go through one of the unlabeled black points to create an acute angle.** \n*The arc symbol near the vertex indicates the angle being measured.*\n\n[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "angle", + angleOffsetDegrees: 1, + startCoords: undefined, + }, + backgroundImageUrl: + "https://ka-perseus-graphie.s3.amazonaws.com/807ea77cf7031c1b9a45694083f05b5e09b01946.png", + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [6.998933866094739, 0.12216684506098452], + [0, 0], + [6.535062985480412, 2.5085756468171017], + ], + angleOffsetDegrees: 1, + }, + }, + }, + }); + }); + + it("should get prompt JSON for a circle graph", async () => { + // Arrange + const {renderer} = renderQuestion(circleQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "circle", + startParams: { + center: undefined, + radius: undefined, + }, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + center: [0, 0], + radius: 2, + }, + }, + }, + }); + }); + + it("should get prompt JSON for a linear graph", async () => { + // Arrange + const {renderer} = renderQuestion(linearQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "linear", + startCoords: undefined, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [-5, 5], + [5, 5], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a linear system graph", async () => { + // Arrange + const {renderer} = renderQuestion(linearSystemQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "linear-system", + startCoords: undefined, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [ + [-5, 5], + [5, 5], + ], + [ + [-5, -5], + [5, -5], + ], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a point graph", async () => { + // Arrange + const {renderer} = renderQuestion(pointQuestion, apiOptions); + + // Act + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "We want to find the zeros of this polynomial:\n\n$p(x)=x(2x+5)(x+1)$\n\n**Plot all the zeros ($x$-intercepts) of the polynomial in the interactive graph.**\n\n[[\u2603 interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "point", + numPoints: "unlimited", + startCoords: undefined, + }, + backgroundImageUrl: + "web+graphie://ka-perseus-graphie.s3.amazonaws.com/9e825947f778170369f22da5f87239cbf4c1ebe3", + range: [ + [-4, 4], + [-4, 4], + ], + labels: ["x", "y"], + }, + userInput: { + coords: undefined, + }, + }, + }, + }); + }); + + it("should get prompt JSON for a polygon graph", async () => { + // Arrange + const {renderer} = renderQuestion(polygonQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "**Sides shown** Drag the vertices of the triangle below to draw a right triangle with side lengths $3$, $4$, and $5$. \n[[\u2603 interactive-graph 1]] \n", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "polygon", + match: undefined, + numSides: 3, + startCoords: undefined, + }, + backgroundImageUrl: null, + range: [ + [-1, 6], + [-1, 6], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [3.5, 2], + [2.5, 4], + [1.5, 2], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a quadratic graph", async () => { + // Arrange + const {renderer} = renderQuestion(quadraticQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "quadratic", + startCoords: undefined, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [-5, 5], + [0, -5], + [5, 5], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a ray graph", async () => { + // Arrange + const {renderer} = renderQuestion(rayQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "**Move the ray so it has an endpoint at point $\\text{B}$ and goes through point $\\text{A}$. Then complete the statement below.**\n\n[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "ray", + startCoords: undefined, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [-5, 5], + [5, 5], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a segment graph", async () => { + // Arrange + const {renderer} = renderQuestion(segmentQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "Line segment $\\overline{OG}$ is rotated $180^\\circ$ about the point $(-2,4)$. \n\n**Draw the image of this rotation using the interactive graph.**\n\n*The direction of a rotation by a positive angle is counter-clockwise.* \n\n[[☃ interactive-graph 1]]\n\n", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "segment", + startCoords: undefined, + numSegments: 1, + }, + backgroundImageUrl: null, + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [ + [-5, 5], + [5, 5], + ], + ], + }, + }, + }, + }); + }); + + it("should get prompt JSON for a sinusoid graph", async () => { + // Arrange + const {renderer} = renderQuestion(sinusoidQuestion, apiOptions); + + // Act + await userEvent.tab(); + await userEvent.tab(); + await userEvent.keyboard("{arrowup}{arrowdown}"); + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "**Graph $h(x)=3\\sin(2x-\\pi)+2$ in the interactive widget.** \n*Note that one moveable point always defines an extremum point in the graph and the other point always defines a neighbouring intersection with the midline.*\n\n[[☃ interactive-graph 1]]", + widgets: { + "interactive-graph 1": { + type: "interactive-graph", + options: { + graph: { + type: "sinusoid", + startCoords: undefined, + }, + backgroundImageUrl: + "https://ka-perseus-graphie.s3.amazonaws.com/ba6cf7327a7aaed2386ca00d48b6d554a357ac57.png", + range: [ + [-10, 10], + [-10, 10], + ], + labels: ["x", "y"], + }, + userInput: { + coords: [ + [0, 0], + [3, 2], + ], + }, + }, + }, + }); + }); }); diff --git a/packages/perseus/src/widget-ai-utils/interactive-graph/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/interactive-graph/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph.test.ts b/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph.test.ts deleted file mode 100644 index bd75755237..0000000000 --- a/packages/perseus/src/widget-ai-utils/interactive-graph/interactive-graph.test.ts +++ /dev/null @@ -1,442 +0,0 @@ -import {userEvent as userEventLib} from "@testing-library/user-event"; - -import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; -import { - angleQuestion, - circleQuestion, - linearQuestion, - linearSystemQuestion, - pointQuestion, - polygonQuestion, - quadraticQuestion, - rayQuestion, - segmentQuestion, - sinusoidQuestion, -} from "../../widgets/interactive-graphs/interactive-graph.testdata"; -import {trueForAllMafsSupportedGraphTypes} from "../../widgets/interactive-graphs/mafs-supported-graph-types"; - -import type {UserEvent} from "@testing-library/user-event"; - -const apiOptions = { - flags: {mafs: trueForAllMafsSupportedGraphTypes}, -}; - -describe("interactive-graph widget", () => { - let userEvent: UserEvent; - beforeEach(() => { - userEvent = userEventLib.setup({ - advanceTimers: jest.advanceTimersByTime, - }); - }); - - it("should get prompt JSON for an angle graph", async () => { - // Arrange - const {renderer} = renderQuestion(angleQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "**Drag the vertex of the angle to place the vertex at point $\\text{A}$.** \n\n**Drag another point on the angle to make one of the rays go through point $\\text{B}$.**\n\n**Make the other ray go through one of the unlabeled black points to create an acute angle.** \n*The arc symbol near the vertex indicates the angle being measured.*\n\n[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "angle", - angleOffsetDegrees: 1, - startCoords: undefined, - }, - backgroundImageUrl: - "https://ka-perseus-graphie.s3.amazonaws.com/807ea77cf7031c1b9a45694083f05b5e09b01946.png", - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [6.998933866094739, 0.12216684506098452], - [0, 0], - [6.535062985480412, 2.5085756468171017], - ], - angleOffsetDegrees: 1, - }, - }, - }, - }); - }); - - it("should get prompt JSON for a circle graph", async () => { - // Arrange - const {renderer} = renderQuestion(circleQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "circle", - startParams: { - center: undefined, - radius: undefined, - }, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - center: [0, 0], - radius: 2, - }, - }, - }, - }); - }); - - it("should get prompt JSON for a linear graph", async () => { - // Arrange - const {renderer} = renderQuestion(linearQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "linear", - startCoords: undefined, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [-5, 5], - [5, 5], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a linear system graph", async () => { - // Arrange - const {renderer} = renderQuestion(linearSystemQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "linear-system", - startCoords: undefined, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [ - [-5, 5], - [5, 5], - ], - [ - [-5, -5], - [5, -5], - ], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a point graph", async () => { - // Arrange - const {renderer} = renderQuestion(pointQuestion, apiOptions); - - // Act - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "We want to find the zeros of this polynomial:\n\n$p(x)=x(2x+5)(x+1)$\n\n**Plot all the zeros ($x$-intercepts) of the polynomial in the interactive graph.**\n\n[[\u2603 interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "point", - numPoints: "unlimited", - startCoords: undefined, - }, - backgroundImageUrl: - "web+graphie://ka-perseus-graphie.s3.amazonaws.com/9e825947f778170369f22da5f87239cbf4c1ebe3", - range: [ - [-4, 4], - [-4, 4], - ], - labels: ["x", "y"], - }, - userInput: { - coords: undefined, - }, - }, - }, - }); - }); - - it("should get prompt JSON for a polygon graph", async () => { - // Arrange - const {renderer} = renderQuestion(polygonQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "**Sides shown** Drag the vertices of the triangle below to draw a right triangle with side lengths $3$, $4$, and $5$. \n[[\u2603 interactive-graph 1]] \n", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "polygon", - match: undefined, - numSides: 3, - startCoords: undefined, - }, - backgroundImageUrl: null, - range: [ - [-1, 6], - [-1, 6], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [3.5, 2], - [2.5, 4], - [1.5, 2], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a quadratic graph", async () => { - // Arrange - const {renderer} = renderQuestion(quadraticQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "quadratic", - startCoords: undefined, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [-5, 5], - [0, -5], - [5, 5], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a ray graph", async () => { - // Arrange - const {renderer} = renderQuestion(rayQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "**Move the ray so it has an endpoint at point $\\text{B}$ and goes through point $\\text{A}$. Then complete the statement below.**\n\n[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "ray", - startCoords: undefined, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [-5, 5], - [5, 5], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a segment graph", async () => { - // Arrange - const {renderer} = renderQuestion(segmentQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "Line segment $\\overline{OG}$ is rotated $180^\\circ$ about the point $(-2,4)$. \n\n**Draw the image of this rotation using the interactive graph.**\n\n*The direction of a rotation by a positive angle is counter-clockwise.* \n\n[[☃ interactive-graph 1]]\n\n", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "segment", - startCoords: undefined, - numSegments: 1, - }, - backgroundImageUrl: null, - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [ - [-5, 5], - [5, 5], - ], - ], - }, - }, - }, - }); - }); - - it("should get prompt JSON for a sinusoid graph", async () => { - // Arrange - const {renderer} = renderQuestion(sinusoidQuestion, apiOptions); - - // Act - await userEvent.tab(); - await userEvent.tab(); - await userEvent.keyboard("{arrowup}{arrowdown}"); - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "**Graph $h(x)=3\\sin(2x-\\pi)+2$ in the interactive widget.** \n*Note that one moveable point always defines an extremum point in the graph and the other point always defines a neighbouring intersection with the midline.*\n\n[[☃ interactive-graph 1]]", - widgets: { - "interactive-graph 1": { - type: "interactive-graph", - options: { - graph: { - type: "sinusoid", - startCoords: undefined, - }, - backgroundImageUrl: - "https://ka-perseus-graphie.s3.amazonaws.com/ba6cf7327a7aaed2386ca00d48b6d554a357ac57.png", - range: [ - [-10, 10], - [-10, 10], - ], - labels: ["x", "y"], - }, - userInput: { - coords: [ - [0, 0], - [3, 2], - ], - }, - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/interactive-graph.tsx b/packages/perseus/src/widgets/interactive-graph.tsx index bf70474e1e..703bdb452b 100644 --- a/packages/perseus/src/widgets/interactive-graph.tsx +++ b/packages/perseus/src/widgets/interactive-graph.tsx @@ -28,7 +28,7 @@ import { import GraphUtils from "../util/graph-utils"; import {polar} from "../util/graphie"; import {getInteractiveBoxFromSizeClass} from "../util/sizing-utils"; -import {getPromptJSON} from "../widget-ai-utils/interactive-graph/prompt-utils"; +import {getPromptJSON} from "../widget-ai-utils/interactive-graph/interactive-graph-ai-utils"; import {StatefulMafsGraph} from "./interactive-graphs"; import {getClockwiseAngle} from "./interactive-graphs/math"; @@ -55,7 +55,7 @@ import type { PerseusInteractiveGraphRubric, PerseusInteractiveGraphUserInput, } from "../validation.types"; -import type {InteractiveGraphPromptJSON} from "../widget-ai-utils/interactive-graph/prompt-utils"; +import type {InteractiveGraphPromptJSON} from "../widget-ai-utils/interactive-graph/interactive-graph-ai-utils"; import type {UnsupportedWidgetPromptJSON} from "../widget-ai-utils/unsupported-widget"; const TRASH_ICON_URI = From 6409d4b2913e51d9fd85c8e10f43daabc1e88f1c Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:47:41 -0600 Subject: [PATCH 14/27] label image --- packages/perseus/src/prompt-types.ts | 2 +- ...e.test.ts => label-image-ai-utils.test.ts} | 91 ++++++++++++++++++- ...rompt-utils.ts => label-image-ai-utils.ts} | 0 .../label-image/prompt-utils.test.ts | 91 ------------------- .../src/widgets/label-image/label-image.tsx | 4 +- 5 files changed, 93 insertions(+), 95 deletions(-) rename packages/perseus/src/widget-ai-utils/label-image/{label-image.test.ts => label-image-ai-utils.test.ts} (63%) rename packages/perseus/src/widget-ai-utils/label-image/{prompt-utils.ts => label-image-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/label-image/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index d26cc6aff5..2a07747d55 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -9,7 +9,7 @@ import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/grapher-ai-utils import type {GroupPromptJSON} from "./widget-ai-utils/group/group-ai-utils"; import type {ImagePromptJSON} from "./widget-ai-utils/image/image-ai-utils"; import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-number-ai-utils"; -import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/prompt-utils"; +import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/label-image-ai-utils"; import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/prompt-utils"; import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/prompt-utils"; import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/label-image/label-image.test.ts b/packages/perseus/src/widget-ai-utils/label-image/label-image-ai-utils.test.ts similarity index 63% rename from packages/perseus/src/widget-ai-utils/label-image/label-image.test.ts rename to packages/perseus/src/widget-ai-utils/label-image/label-image-ai-utils.test.ts index b131d5b25f..df7ff58e19 100644 --- a/packages/perseus/src/widget-ai-utils/label-image/label-image.test.ts +++ b/packages/perseus/src/widget-ai-utils/label-image/label-image-ai-utils.test.ts @@ -3,6 +3,8 @@ import {userEvent as userEventLib} from "@testing-library/user-event"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./label-image-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; import type {UserEvent} from "@testing-library/user-event"; @@ -68,7 +70,7 @@ const textQuestion: PerseusRenderer = { }, }; -describe("label image widget", () => { +describe("LabelImage AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -76,6 +78,93 @@ describe("label image widget", () => { }); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + markers: [ + { + answers: ["SUVs"], + label: "The fourth unlabeled bar line.", + x: 25, + y: 17.7, + }, + { + answers: ["Trucks"], + label: "The third unlabeled bar line.", + x: 25, + y: 35.3, + }, + { + answers: ["Cars"], + label: "The second unlabeled bar line.", + x: 25, + y: 53, + }, + { + answers: ["Vans"], + label: "The first unlabeled bar line.", + x: 25, + y: 70.3, + }, + ], + choices: ["Trucks", "Vans", "Cars", "SUVs"], + imageUrl: + "web+graphie://ka-perseus-graphie.s3.amazonaws.com/56c60c72e96cd353e4a8b5434506cd3a21e717af", + imageAlt: + "A bar graph with four bar lines that shows the horizontal axis labeled Number in the parking lot and the vertical axis labeled Vehicle Type. The horizontal axis is labeled, from left to right: 0, 10, 20, 30, 40, and 50. The vertical axis has, from the bottom to the top, four unlabeled bar lines as follows: the first unlabeled bar line extends to the middle of 0 and 10, the second unlabeled bar line extends to 40, the third unlabeled bar line extends to the middle of 20 and 30, and fourth unlabeled bar line extends to 10.", + }; + + const userInput: any = { + markers: [ + { + label: "The fourth unlabeled bar line.", + selected: ["Vans"], + }, + { + label: "The third unlabeled bar line.", + selected: ["SUVs"], + }, + ], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "label-image", + options: { + markers: [ + { + label: "The fourth unlabeled bar line.", + }, + { + label: "The third unlabeled bar line.", + }, + { + label: "The second unlabeled bar line.", + }, + { + label: "The first unlabeled bar line.", + }, + ], + choices: ["Trucks", "Vans", "Cars", "SUVs"], + imageUrl: + "web+graphie://ka-perseus-graphie.s3.amazonaws.com/56c60c72e96cd353e4a8b5434506cd3a21e717af", + imageAlt: + "A bar graph with four bar lines that shows the horizontal axis labeled Number in the parking lot and the vertical axis labeled Vehicle Type. The horizontal axis is labeled, from left to right: 0, 10, 20, 30, 40, and 50. The vertical axis has, from the bottom to the top, four unlabeled bar lines as follows: the first unlabeled bar line extends to the middle of 0 and 10, the second unlabeled bar line extends to 40, the third unlabeled bar line extends to the middle of 20 and 30, and fourth unlabeled bar line extends to 10.", + }, + userInput: { + markers: [ + { + label: "The fourth unlabeled bar line.", + selected: ["Vans"], + }, + { + label: "The third unlabeled bar line.", + selected: ["SUVs"], + }, + ], + }, + }); + }); it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(textQuestion); diff --git a/packages/perseus/src/widget-ai-utils/label-image/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/label-image/label-image-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/label-image/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/label-image/label-image-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/label-image/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/label-image/prompt-utils.test.ts deleted file mode 100644 index 635c695c6e..0000000000 --- a/packages/perseus/src/widget-ai-utils/label-image/prompt-utils.test.ts +++ /dev/null @@ -1,91 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("LabelImage getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - markers: [ - { - answers: ["SUVs"], - label: "The fourth unlabeled bar line.", - x: 25, - y: 17.7, - }, - { - answers: ["Trucks"], - label: "The third unlabeled bar line.", - x: 25, - y: 35.3, - }, - { - answers: ["Cars"], - label: "The second unlabeled bar line.", - x: 25, - y: 53, - }, - { - answers: ["Vans"], - label: "The first unlabeled bar line.", - x: 25, - y: 70.3, - }, - ], - choices: ["Trucks", "Vans", "Cars", "SUVs"], - imageUrl: - "web+graphie://ka-perseus-graphie.s3.amazonaws.com/56c60c72e96cd353e4a8b5434506cd3a21e717af", - imageAlt: - "A bar graph with four bar lines that shows the horizontal axis labeled Number in the parking lot and the vertical axis labeled Vehicle Type. The horizontal axis is labeled, from left to right: 0, 10, 20, 30, 40, and 50. The vertical axis has, from the bottom to the top, four unlabeled bar lines as follows: the first unlabeled bar line extends to the middle of 0 and 10, the second unlabeled bar line extends to 40, the third unlabeled bar line extends to the middle of 20 and 30, and fourth unlabeled bar line extends to 10.", - }; - - const userInput: any = { - markers: [ - { - label: "The fourth unlabeled bar line.", - selected: ["Vans"], - }, - { - label: "The third unlabeled bar line.", - selected: ["SUVs"], - }, - ], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "label-image", - options: { - markers: [ - { - label: "The fourth unlabeled bar line.", - }, - { - label: "The third unlabeled bar line.", - }, - { - label: "The second unlabeled bar line.", - }, - { - label: "The first unlabeled bar line.", - }, - ], - choices: ["Trucks", "Vans", "Cars", "SUVs"], - imageUrl: - "web+graphie://ka-perseus-graphie.s3.amazonaws.com/56c60c72e96cd353e4a8b5434506cd3a21e717af", - imageAlt: - "A bar graph with four bar lines that shows the horizontal axis labeled Number in the parking lot and the vertical axis labeled Vehicle Type. The horizontal axis is labeled, from left to right: 0, 10, 20, 30, 40, and 50. The vertical axis has, from the bottom to the top, four unlabeled bar lines as follows: the first unlabeled bar line extends to the middle of 0 and 10, the second unlabeled bar line extends to 40, the third unlabeled bar line extends to the middle of 20 and 30, and fourth unlabeled bar line extends to 10.", - }, - userInput: { - markers: [ - { - label: "The fourth unlabeled bar line.", - selected: ["Vans"], - }, - { - label: "The third unlabeled bar line.", - selected: ["SUVs"], - }, - ], - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/label-image/label-image.tsx b/packages/perseus/src/widgets/label-image/label-image.tsx index bb5afc0d6b..d86ed6a4a0 100644 --- a/packages/perseus/src/widgets/label-image/label-image.tsx +++ b/packages/perseus/src/widgets/label-image/label-image.tsx @@ -20,7 +20,7 @@ import {useDependencies} from "../../dependencies"; import Renderer from "../../renderer"; import {bodyXsmallBold} from "../../styles/global-styles"; import mediaQueries from "../../styles/media-queries"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/label-image/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/label-image/label-image-ai-utils"; import AnswerChoices from "./answer-choices"; import {HideAnswersToggle} from "./hide-answers-toggle"; @@ -36,7 +36,7 @@ import type { PerseusLabelImageRubric, PerseusLabelImageUserInput, } from "../../validation.types"; -import type {LabelImagePromptJSON} from "../../widget-ai-utils/label-image/prompt-utils"; +import type {LabelImagePromptJSON} from "../../widget-ai-utils/label-image/label-image-ai-utils"; import type {PropsFor} from "@khanacademy/wonder-blocks-core"; import type {CSSProperties} from "aphrodite"; From 8aa836eb20f28b79eca37a59920ca1651823866c Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:52:26 -0600 Subject: [PATCH 15/27] matcher --- packages/perseus/src/prompt-types.ts | 2 +- ...her.test.tsx => matcher-ai-utils.test.tsx} | 41 ++++++++++++++++++- .../{prompt-utils.ts => matcher-ai-utils.ts} | 0 .../matcher/prompt-utils.test.ts | 41 ------------------- .../perseus/src/widgets/matcher/matcher.tsx | 4 +- 5 files changed, 43 insertions(+), 45 deletions(-) rename packages/perseus/src/widget-ai-utils/matcher/{matcher.test.tsx => matcher-ai-utils.test.tsx} (82%) rename packages/perseus/src/widget-ai-utils/matcher/{prompt-utils.ts => matcher-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/matcher/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 2a07747d55..2dae58f10e 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -10,7 +10,7 @@ import type {GroupPromptJSON} from "./widget-ai-utils/group/group-ai-utils"; import type {ImagePromptJSON} from "./widget-ai-utils/image/image-ai-utils"; import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-number-ai-utils"; import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/label-image-ai-utils"; -import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/prompt-utils"; +import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/matcher-ai-utils"; import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/prompt-utils"; import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/prompt-utils"; import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/matcher/matcher.test.tsx b/packages/perseus/src/widget-ai-utils/matcher/matcher-ai-utils.test.tsx similarity index 82% rename from packages/perseus/src/widget-ai-utils/matcher/matcher.test.tsx rename to packages/perseus/src/widget-ai-utils/matcher/matcher-ai-utils.test.tsx index f38bd72f93..a67055c8ed 100644 --- a/packages/perseus/src/widget-ai-utils/matcher/matcher.test.tsx +++ b/packages/perseus/src/widget-ai-utils/matcher/matcher-ai-utils.test.tsx @@ -5,7 +5,10 @@ import {testDependencies} from "../../../../../testing/test-dependencies"; import * as Dependencies from "../../dependencies"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./matcher-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; +import type {PerseusMatcherUserInput} from "../../validation.types"; const question1: PerseusRenderer = { content: @@ -39,7 +42,7 @@ const question1: PerseusRenderer = { }, }; -describe("matcher widget", () => { +describe("Matcher AI utils", () => { beforeEach(() => { jest.spyOn(Dependencies, "getDependencies").mockReturnValue({ ...testDependencies, @@ -58,6 +61,42 @@ describe("matcher widget", () => { }); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + labels: { + left: "Number", + right: "Letter", + }, + left: ["1", "2", "3"], + right: ["a", "b", "c"], + orderMatters: false, + }; + + const userInput: PerseusMatcherUserInput = { + left: ["3", "1", "2"], + right: ["a", "b", "c"], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "matcher", + options: { + labels: { + left: "Number", + right: "Letter", + }, + left: ["1", "2", "3"], + right: ["a", "b", "c"], + orderMatters: false, + }, + userInput: { + left: ["3", "1", "2"], + right: ["a", "b", "c"], + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1, {isMobile: false}); diff --git a/packages/perseus/src/widget-ai-utils/matcher/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/matcher/matcher-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/matcher/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/matcher/matcher-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/matcher/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/matcher/prompt-utils.test.ts deleted file mode 100644 index c08d8f1bdf..0000000000 --- a/packages/perseus/src/widget-ai-utils/matcher/prompt-utils.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {PerseusMatcherUserInput} from "../../validation.types"; - -describe("Matcher getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - labels: { - left: "Number", - right: "Letter", - }, - left: ["1", "2", "3"], - right: ["a", "b", "c"], - orderMatters: false, - }; - - const userInput: PerseusMatcherUserInput = { - left: ["3", "1", "2"], - right: ["a", "b", "c"], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "matcher", - options: { - labels: { - left: "Number", - right: "Letter", - }, - left: ["1", "2", "3"], - right: ["a", "b", "c"], - orderMatters: false, - }, - userInput: { - left: ["3", "1", "2"], - right: ["a", "b", "c"], - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/matcher/matcher.tsx b/packages/perseus/src/widgets/matcher/matcher.tsx index 75b8f96dc6..c09c22e8bf 100644 --- a/packages/perseus/src/widgets/matcher/matcher.tsx +++ b/packages/perseus/src/widgets/matcher/matcher.tsx @@ -9,7 +9,7 @@ import Sortable from "../../components/sortable"; import {getDependencies} from "../../dependencies"; import Renderer from "../../renderer"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/matcher/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/matcher/matcher-ai-utils"; import scoreMatcher from "./score-matcher"; @@ -20,7 +20,7 @@ import type { PerseusMatcherRubric, PerseusMatcherUserInput, } from "../../validation.types"; -import type {MatcherPromptJSON} from "../../widget-ai-utils/matcher/prompt-utils"; +import type {MatcherPromptJSON} from "../../widget-ai-utils/matcher/matcher-ai-utils"; const {shuffle, seededRNG} = Util; const HACKY_CSS_CLASSNAME = "perseus-widget-matcher"; From 59e018245d1ea1e19b926af19edea947006514e1 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:54:53 -0600 Subject: [PATCH 16/27] matrix and measurer --- packages/perseus/src/prompt-types.ts | 2 +- ...matrix.test.ts => matrix-ai-utils.test.ts} | 35 ++++++++++++++++++- .../{prompt-utils.ts => matrix-ai-utils.ts} | 0 .../matrix/prompt-utils.test.ts | 34 ------------------ ...tils.test.ts => measurer-ai-utils.test.ts} | 2 +- .../{prompt-utils.ts => measurer-ai-utils.ts} | 0 .../perseus/src/widgets/matrix/matrix.tsx | 4 +-- .../perseus/src/widgets/measurer/measurer.tsx | 2 +- 8 files changed, 39 insertions(+), 40 deletions(-) rename packages/perseus/src/widget-ai-utils/matrix/{matrix.test.ts => matrix-ai-utils.test.ts} (76%) rename packages/perseus/src/widget-ai-utils/matrix/{prompt-utils.ts => matrix-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/matrix/prompt-utils.test.ts rename packages/perseus/src/widget-ai-utils/measurer/{prompt-utils.test.ts => measurer-ai-utils.test.ts} (85%) rename packages/perseus/src/widget-ai-utils/measurer/{prompt-utils.ts => measurer-ai-utils.ts} (100%) diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 2dae58f10e..1b16fb0c89 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -11,7 +11,7 @@ import type {ImagePromptJSON} from "./widget-ai-utils/image/image-ai-utils"; import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-number-ai-utils"; import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/label-image-ai-utils"; import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/matcher-ai-utils"; -import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/prompt-utils"; +import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/matrix-ai-utils"; import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/prompt-utils"; import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/matrix/matrix.test.ts b/packages/perseus/src/widget-ai-utils/matrix/matrix-ai-utils.test.ts similarity index 76% rename from packages/perseus/src/widget-ai-utils/matrix/matrix.test.ts rename to packages/perseus/src/widget-ai-utils/matrix/matrix-ai-utils.test.ts index 0daa88c072..8c284b647b 100644 --- a/packages/perseus/src/widget-ai-utils/matrix/matrix.test.ts +++ b/packages/perseus/src/widget-ai-utils/matrix/matrix-ai-utils.test.ts @@ -3,6 +3,8 @@ import {userEvent as userEventLib} from "@testing-library/user-event"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./matrix-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; import type {UserEvent} from "@testing-library/user-event"; @@ -33,7 +35,7 @@ const question: PerseusRenderer = { }, }; -describe("matrix widget", () => { +describe("Matrix AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -41,6 +43,37 @@ describe("matrix widget", () => { }); }); + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + matrixBoardSize: [4, 3], + }; + + const userInput: any = { + answers: [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + ], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "matrix", + options: { + width: 3, + height: 4, + }, + userInput: { + answerRows: [ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9, 10, 11, 12], + ], + }, + }); + }); + it("Should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/matrix/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/matrix/matrix-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/matrix/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/matrix/matrix-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/matrix/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/matrix/prompt-utils.test.ts deleted file mode 100644 index 40936e5fec..0000000000 --- a/packages/perseus/src/widget-ai-utils/matrix/prompt-utils.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Matrix getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - matrixBoardSize: [4, 3], - }; - - const userInput: any = { - answers: [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], - ], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "matrix", - options: { - width: 3, - height: 4, - }, - userInput: { - answerRows: [ - [1, 2, 3, 4], - [5, 6, 7, 8], - [9, 10, 11, 12], - ], - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/measurer/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts similarity index 85% rename from packages/perseus/src/widget-ai-utils/measurer/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts index ac0fc10b2b..31a3a9046f 100644 --- a/packages/perseus/src/widget-ai-utils/measurer/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts @@ -1,4 +1,4 @@ -import {getPromptJSON} from "./prompt-utils"; +import {getPromptJSON} from "./measurer-ai-utils"; describe("Measurer getPromptJSON", () => { it("it returns JSON with the expected format and fields", () => { diff --git a/packages/perseus/src/widget-ai-utils/measurer/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/measurer/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.ts diff --git a/packages/perseus/src/widgets/matrix/matrix.tsx b/packages/perseus/src/widgets/matrix/matrix.tsx index adbd3c985a..c888078a91 100644 --- a/packages/perseus/src/widgets/matrix/matrix.tsx +++ b/packages/perseus/src/widgets/matrix/matrix.tsx @@ -13,7 +13,7 @@ import InteractiveUtil from "../../interactive2/interactive-util"; import {ApiOptions} from "../../perseus-api"; import Renderer from "../../renderer"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/matrix/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/matrix/matrix-ai-utils"; import scoreMatrix from "./score-matrix"; @@ -23,7 +23,7 @@ import type { PerseusMatrixRubric, PerseusMatrixUserInput, } from "../../validation.types"; -import type {MatrixPromptJSON} from "../../widget-ai-utils/matrix/prompt-utils"; +import type {MatrixPromptJSON} from "../../widget-ai-utils/matrix/matrix-ai-utils"; const {assert} = InteractiveUtil; const {stringArrayOfSize} = Util; diff --git a/packages/perseus/src/widgets/measurer/measurer.tsx b/packages/perseus/src/widgets/measurer/measurer.tsx index eb839c8062..4347ec677e 100644 --- a/packages/perseus/src/widgets/measurer/measurer.tsx +++ b/packages/perseus/src/widgets/measurer/measurer.tsx @@ -5,7 +5,7 @@ import _ from "underscore"; import SvgImage from "../../components/svg-image"; import GraphUtils from "../../util/graph-utils"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/measurer/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/measurer/measurer-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import type {Coord} from "../../interactive2/types"; From 6619dfb5641e80b4eb857e39867dbfbeb7161998 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:56:32 -0600 Subject: [PATCH 17/27] number status --- packages/perseus/src/prompt-types.ts | 2 +- .../measurer/measurer-ai-utils.test.ts | 2 +- ...e.test.ts => number-line-ai-utils.test.ts} | 32 ++++++++++++++++++- ...rompt-utils.ts => number-line-ai-utils.ts} | 0 .../number-line/prompt-utils.test.ts | 31 ------------------ .../src/widgets/number-line/number-line.tsx | 4 +-- 6 files changed, 35 insertions(+), 36 deletions(-) rename packages/perseus/src/widget-ai-utils/number-line/{number-line.test.ts => number-line-ai-utils.test.ts} (71%) rename packages/perseus/src/widget-ai-utils/number-line/{prompt-utils.ts => number-line-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/number-line/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 1b16fb0c89..e41463b359 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -12,7 +12,7 @@ import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-n import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/label-image-ai-utils"; import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/matcher-ai-utils"; import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/matrix-ai-utils"; -import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/prompt-utils"; +import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/number-line-ai-utils"; import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/prompt-utils"; import type {PassagePromptJSON} from "./widget-ai-utils/passage/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts index 31a3a9046f..5addea9791 100644 --- a/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/measurer/measurer-ai-utils.test.ts @@ -1,6 +1,6 @@ import {getPromptJSON} from "./measurer-ai-utils"; -describe("Measurer getPromptJSON", () => { +describe("Measurer AI utils", () => { it("it returns JSON with the expected format and fields", () => { const resultJSON = getPromptJSON(); diff --git a/packages/perseus/src/widget-ai-utils/number-line/number-line.test.ts b/packages/perseus/src/widget-ai-utils/number-line/number-line-ai-utils.test.ts similarity index 71% rename from packages/perseus/src/widget-ai-utils/number-line/number-line.test.ts rename to packages/perseus/src/widget-ai-utils/number-line/number-line-ai-utils.test.ts index eeeb32b002..4561d18f5d 100644 --- a/packages/perseus/src/widget-ai-utils/number-line/number-line.test.ts +++ b/packages/perseus/src/widget-ai-utils/number-line/number-line-ai-utils.test.ts @@ -2,6 +2,8 @@ import {act} from "@testing-library/react"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./number-line-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; export const question: PerseusRenderer = { @@ -33,7 +35,35 @@ export const question: PerseusRenderer = { }, }; -describe("number-line widget", () => { +describe("NumberLine AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + range: [0, 10], + numDivisions: 10, + snapDivisions: 2, + }; + + const userInput: any = { + numLinePosition: 5, + numDivisions: 10, + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "number-line", + options: { + range: [0, 10], + numDivisions: 10, + snapDivisions: 2, + }, + userInput: { + numLinePosition: 5, + numDivisions: 10, + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question); diff --git a/packages/perseus/src/widget-ai-utils/number-line/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/number-line/number-line-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/number-line/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/number-line/number-line-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/number-line/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/number-line/prompt-utils.test.ts deleted file mode 100644 index 542442c495..0000000000 --- a/packages/perseus/src/widget-ai-utils/number-line/prompt-utils.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("NumberLine getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - range: [0, 10], - numDivisions: 10, - snapDivisions: 2, - }; - - const userInput: any = { - numLinePosition: 5, - numDivisions: 10, - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "number-line", - options: { - range: [0, 10], - numDivisions: 10, - snapDivisions: 2, - }, - userInput: { - numLinePosition: 5, - numDivisions: 10, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/number-line/number-line.tsx b/packages/perseus/src/widgets/number-line/number-line.tsx index 8059a0bd9a..8d39882601 100644 --- a/packages/perseus/src/widgets/number-line/number-line.tsx +++ b/packages/perseus/src/widgets/number-line/number-line.tsx @@ -12,14 +12,14 @@ import * as Changeable from "../../mixins/changeable"; import {ApiOptions} from "../../perseus-api"; import KhanColors from "../../util/colors"; import KhanMath from "../../util/math"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/number-line/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/number-line/number-line-ai-utils"; import scoreNumberLine from "./score-number-line"; import type {ChangeableProps} from "../../mixins/changeable"; import type {APIOptions, WidgetExports, FocusPath, Widget} from "../../types"; import type {PerseusNumberLineUserInput} from "../../validation.types"; -import type {NumberLinePromptJSON} from "../../widget-ai-utils/number-line/prompt-utils"; +import type {NumberLinePromptJSON} from "../../widget-ai-utils/number-line/number-line-ai-utils"; // @ts-expect-error - TS2339 - Property 'MovablePoint' does not exist on type 'typeof Graphie'. const MovablePoint = Graphie.MovablePoint; From 9e5c909dd768148c8cc451898f04156b6ae56377 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:57:48 -0600 Subject: [PATCH 18/27] orderer --- packages/perseus/src/prompt-types.ts | 2 +- ...derer.test.ts => orderer-ai-utils.test.ts} | 42 ++++++++++++++++++- .../{prompt-utils.ts => orderer-ai-utils.ts} | 0 .../orderer/prompt-utils.test.ts | 41 ------------------ .../perseus/src/widgets/orderer/orderer.tsx | 4 +- 5 files changed, 44 insertions(+), 45 deletions(-) rename packages/perseus/src/widget-ai-utils/orderer/{orderer.test.ts => orderer-ai-utils.test.ts} (62%) rename packages/perseus/src/widget-ai-utils/orderer/{prompt-utils.ts => orderer-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/orderer/prompt-utils.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index e41463b359..e414990263 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -14,7 +14,7 @@ import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/matcher-ai-utils import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/matrix-ai-utils"; import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/number-line-ai-utils"; import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; -import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/prompt-utils"; +import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/orderer-ai-utils"; import type {PassagePromptJSON} from "./widget-ai-utils/passage/prompt-utils"; import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/prompt-utils"; import type {RadioPromptJSON} from "./widget-ai-utils/radio/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/orderer/orderer.test.ts b/packages/perseus/src/widget-ai-utils/orderer/orderer-ai-utils.test.ts similarity index 62% rename from packages/perseus/src/widget-ai-utils/orderer/orderer.test.ts rename to packages/perseus/src/widget-ai-utils/orderer/orderer-ai-utils.test.ts index a45a1ae981..6ee4e430c2 100644 --- a/packages/perseus/src/widget-ai-utils/orderer/orderer.test.ts +++ b/packages/perseus/src/widget-ai-utils/orderer/orderer-ai-utils.test.ts @@ -1,5 +1,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./orderer-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; const question1: PerseusRenderer = { @@ -30,7 +32,45 @@ const question1: PerseusRenderer = { }, }; -describe("orderer widget", () => { +describe("Orderer AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + options: [ + { + content: "Third item", + images: {}, + widgets: {}, + }, + { + content: "First item", + images: {}, + widgets: {}, + }, + { + content: "Second item", + images: {}, + widgets: {}, + }, + ], + }; + + const userInput = { + current: ["First item", "Second item", "Third item"], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "orderer", + options: { + options: ["Third item", "First item", "Second item"], + }, + userInput: { + values: ["First item", "Second item", "Third item"], + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/orderer/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/orderer/orderer-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/orderer/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/orderer/orderer-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/orderer/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/orderer/prompt-utils.test.ts deleted file mode 100644 index 4e022a45fe..0000000000 --- a/packages/perseus/src/widget-ai-utils/orderer/prompt-utils.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("Orderer getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - options: [ - { - content: "Third item", - images: {}, - widgets: {}, - }, - { - content: "First item", - images: {}, - widgets: {}, - }, - { - content: "Second item", - images: {}, - widgets: {}, - }, - ], - }; - - const userInput = { - current: ["First item", "Second item", "Third item"], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "orderer", - options: { - options: ["Third item", "First item", "Second item"], - }, - userInput: { - values: ["First item", "Second item", "Third item"], - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/orderer/orderer.tsx b/packages/perseus/src/widgets/orderer/orderer.tsx index 5d62ee6972..1300b06583 100644 --- a/packages/perseus/src/widgets/orderer/orderer.tsx +++ b/packages/perseus/src/widgets/orderer/orderer.tsx @@ -12,7 +12,7 @@ import {Log} from "../../logging/log"; import {ClassNames as ApiClassNames} from "../../perseus-api"; import Renderer from "../../renderer"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/orderer/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/orderer/orderer-ai-utils"; import {scoreOrderer} from "./score-orderer"; @@ -27,7 +27,7 @@ import type { PerseusOrdererRubric, PerseusOrdererUserInput, } from "../../validation.types"; -import type {OrdererPromptJSON} from "../../widget-ai-utils/orderer/prompt-utils"; +import type {OrdererPromptJSON} from "../../widget-ai-utils/orderer/orderer-ai-utils"; import type {LinterContextProps} from "@khanacademy/perseus-linter"; type PlaceholderCardProps = { From 1d24e711e68994b502e47538775e512e13638524 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 14:58:57 -0600 Subject: [PATCH 19/27] passage --- packages/perseus/src/prompt-types.ts | 2 +- ...utils.test.ts => passage-ai-utils.test.ts} | 57 ++++++++++++++++++- .../{prompt-utils.ts => passage-ai-utils.ts} | 0 .../widget-ai-utils/passage/passage.test.ts | 54 ------------------ .../perseus/src/widgets/passage/passage.tsx | 4 +- 5 files changed, 58 insertions(+), 59 deletions(-) rename packages/perseus/src/widget-ai-utils/passage/{prompt-utils.test.ts => passage-ai-utils.test.ts} (69%) rename packages/perseus/src/widget-ai-utils/passage/{prompt-utils.ts => passage-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/passage/passage.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index e414990263..fcd5aaf74e 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -15,7 +15,7 @@ import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/matrix-ai-utils"; import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/number-line-ai-utils"; import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/orderer-ai-utils"; -import type {PassagePromptJSON} from "./widget-ai-utils/passage/prompt-utils"; +import type {PassagePromptJSON} from "./widget-ai-utils/passage/passage-ai-utils"; import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/prompt-utils"; import type {RadioPromptJSON} from "./widget-ai-utils/radio/prompt-utils"; import type {SorterPromptJSON} from "./widget-ai-utils/sorter/prompt-utils"; diff --git a/packages/perseus/src/widget-ai-utils/passage/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/passage/passage-ai-utils.test.ts similarity index 69% rename from packages/perseus/src/widget-ai-utils/passage/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/passage/passage-ai-utils.test.ts index 5f6e1ea8bc..f3792909de 100644 --- a/packages/perseus/src/widget-ai-utils/passage/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/passage/passage-ai-utils.test.ts @@ -1,6 +1,35 @@ -import {getPromptJSON} from "./prompt-utils"; +import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; -describe("Passage getPromptJSON", () => { +import {getPromptJSON} from "./passage-ai-utils"; + +import type {PerseusRenderer} from "../../perseus-types"; + +const question1: PerseusRenderer = { + content: "[[☃ passage 1]]\n\n", + images: {}, + widgets: { + "passage 1": { + alignment: "default", + graded: true, + options: { + footnotes: "", + passageText: + "Sociologists study folktales because they provide a means of understanding the distinctive values of a culture. However, the folktales in almost all cultures are adaptations of the same ancient narratives to the local milieu.\n", + passageTitle: "Why do sociologists study folktales?", + showLineNumbers: false, + static: false, + }, + static: false, + type: "passage", + version: { + major: 0, + minor: 0, + }, + }, + }, +}; + +describe("Passage AI utils", () => { it("it returns JSON with the expected format and fields", () => { const renderProps: any = { footnotes: "An example footnote", @@ -21,4 +50,28 @@ describe("Passage getPromptJSON", () => { }, }); }); + + it("should get prompt json which matches the state of the UI", async () => { + // Arrange + const {renderer} = renderQuestion(question1); + + // Act + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[☃ passage 1]]\n\n", + widgets: { + "passage 1": { + type: "passage", + options: { + footnotes: "", + passageText: + "Sociologists study folktales because they provide a means of understanding the distinctive values of a culture. However, the folktales in almost all cultures are adaptations of the same ancient narratives to the local milieu.\n", + passageTitle: "Why do sociologists study folktales?", + }, + }, + }, + }); + }); }); diff --git a/packages/perseus/src/widget-ai-utils/passage/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/passage/passage-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/passage/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/passage/passage-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/passage/passage.test.ts b/packages/perseus/src/widget-ai-utils/passage/passage.test.ts deleted file mode 100644 index 09cabc0de4..0000000000 --- a/packages/perseus/src/widget-ai-utils/passage/passage.test.ts +++ /dev/null @@ -1,54 +0,0 @@ -import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; - -import type {PerseusRenderer} from "../../perseus-types"; - -const question1: PerseusRenderer = { - content: "[[☃ passage 1]]\n\n", - images: {}, - widgets: { - "passage 1": { - alignment: "default", - graded: true, - options: { - footnotes: "", - passageText: - "Sociologists study folktales because they provide a means of understanding the distinctive values of a culture. However, the folktales in almost all cultures are adaptations of the same ancient narratives to the local milieu.\n", - passageTitle: "Why do sociologists study folktales?", - showLineNumbers: false, - static: false, - }, - static: false, - type: "passage", - version: { - major: 0, - minor: 0, - }, - }, - }, -}; - -describe("passage widget", () => { - it("should get prompt json which matches the state of the UI", async () => { - // Arrange - const {renderer} = renderQuestion(question1); - - // Act - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[☃ passage 1]]\n\n", - widgets: { - "passage 1": { - type: "passage", - options: { - footnotes: "", - passageText: - "Sociologists study folktales because they provide a means of understanding the distinctive values of a culture. However, the folktales in almost all cultures are adaptations of the same ancient narratives to the local milieu.\n", - passageTitle: "Why do sociologists study folktales?", - }, - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/passage/passage.tsx b/packages/perseus/src/widgets/passage/passage.tsx index e83cbf09c3..a3ce686c76 100644 --- a/packages/perseus/src/widgets/passage/passage.tsx +++ b/packages/perseus/src/widgets/passage/passage.tsx @@ -9,7 +9,7 @@ import HighlightableContent from "../../components/highlighting/highlightable-co import {PerseusI18nContext} from "../../components/i18n-context"; import {getDependencies} from "../../dependencies"; import Renderer from "../../renderer"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/passage/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/passage/passage-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import PassageMarkdown from "./passage-markdown"; @@ -23,7 +23,7 @@ import type { PerseusWidget, } from "../../perseus-types"; import type {WidgetExports, WidgetProps, Widget} from "../../types"; -import type {PassagePromptJSON} from "../../widget-ai-utils/passage/prompt-utils"; +import type {PassagePromptJSON} from "../../widget-ai-utils/passage/passage-ai-utils"; import type {SingleASTNode} from "@khanacademy/simple-markdown"; // A fake paragraph to measure the line height of the passage, From 4130d0d14dc4e564954e7aa864fd0485d448461c Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:00:54 -0600 Subject: [PATCH 20/27] passage-ref and phet --- packages/perseus/src/prompt-types.ts | 2 +- ...f.test.ts => passage-ref-ai-utils.test.ts} | 23 ++++++- ...rompt-utils.ts => passage-ref-ai-utils.ts} | 0 .../passage-ref/prompt-utils.test.ts | 22 ------- ...t-utils.ts => phet-simulation-ai-utils.ts} | 0 .../phet-simulation/phet-simulation.test.ts | 58 ------------------ .../phet-simulation/prompt-utils.test.ts | 61 ++++++++++++++++++- .../src/widgets/passage-ref/passage-ref.tsx | 4 +- .../phet-simulation/phet-simulation.tsx | 2 +- 9 files changed, 85 insertions(+), 87 deletions(-) rename packages/perseus/src/widget-ai-utils/passage-ref/{passage-ref.test.ts => passage-ref-ai-utils.test.ts} (68%) rename packages/perseus/src/widget-ai-utils/passage-ref/{prompt-utils.ts => passage-ref-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.test.ts rename packages/perseus/src/widget-ai-utils/phet-simulation/{prompt-utils.ts => phet-simulation-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation.test.ts diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index fcd5aaf74e..3d41f064c5 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -16,7 +16,7 @@ import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/number-li import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/orderer-ai-utils"; import type {PassagePromptJSON} from "./widget-ai-utils/passage/passage-ai-utils"; -import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/prompt-utils"; +import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/passage-ref-ai-utils"; import type {RadioPromptJSON} from "./widget-ai-utils/radio/prompt-utils"; import type {SorterPromptJSON} from "./widget-ai-utils/sorter/prompt-utils"; import type {UnsupportedWidgetPromptJSON} from "./widget-ai-utils/unsupported-widget"; diff --git a/packages/perseus/src/widget-ai-utils/passage-ref/passage-ref.test.ts b/packages/perseus/src/widget-ai-utils/passage-ref/passage-ref-ai-utils.test.ts similarity index 68% rename from packages/perseus/src/widget-ai-utils/passage-ref/passage-ref.test.ts rename to packages/perseus/src/widget-ai-utils/passage-ref/passage-ref-ai-utils.test.ts index c590c3c986..24b342c171 100644 --- a/packages/perseus/src/widget-ai-utils/passage-ref/passage-ref.test.ts +++ b/packages/perseus/src/widget-ai-utils/passage-ref/passage-ref-ai-utils.test.ts @@ -2,6 +2,8 @@ import {act} from "@testing-library/react"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./passage-ref-ai-utils"; + import type {PerseusRenderer} from "../../perseus-types"; const question1: PerseusRenderer = { @@ -24,7 +26,26 @@ const question1: PerseusRenderer = { }, }; -describe("passage-ref widget", () => { +describe("PassageRef AI utils", () => { + it("it returns JSON with the expected format and fields", () => { + const renderProps: any = { + passageNumber: 1, + referenceNumber: 1, + summaryText: "This is text summarizing the passage.", + }; + + const resultJSON = getPromptJSON(renderProps); + + expect(resultJSON).toEqual({ + type: "passage-ref", + options: { + passageNumber: 1, + referenceNumber: 1, + summaryText: "This is text summarizing the passage.", + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { // Arrange const {renderer} = renderQuestion(question1); diff --git a/packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/passage-ref/passage-ref-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/passage-ref/passage-ref-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.test.ts deleted file mode 100644 index 274e0e8fa4..0000000000 --- a/packages/perseus/src/widget-ai-utils/passage-ref/prompt-utils.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -describe("PassageRef getPromptJSON", () => { - it("it returns JSON with the expected format and fields", () => { - const renderProps: any = { - passageNumber: 1, - referenceNumber: 1, - summaryText: "This is text summarizing the passage.", - }; - - const resultJSON = getPromptJSON(renderProps); - - expect(resultJSON).toEqual({ - type: "passage-ref", - options: { - passageNumber: 1, - referenceNumber: 1, - summaryText: "This is text summarizing the passage.", - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation.test.ts b/packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation.test.ts deleted file mode 100644 index ed37321ae8..0000000000 --- a/packages/perseus/src/widget-ai-utils/phet-simulation/phet-simulation.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; - -import type {PerseusRenderer} from "../../perseus-types"; - -const question1: PerseusRenderer = { - content: - "Do this fun PhET simulation! A projectile data lab!\n[[\u2603 phet-simulation 1]]\n", - images: {}, - widgets: { - "phet-simulation 1": { - graded: false, - version: {major: 0, minor: 0}, - static: false, - type: "phet-simulation", - options: { - url: "https://phet.colorado.edu/sims/html/projectile-data-lab/latest/projectile-data-lab_all.html", - description: "Projectile Data Lab", - }, - alignment: "default", - }, - }, -}; - -describe("phet-simulation widget", () => { - beforeEach(() => { - global.fetch = jest.fn(() => - Promise.resolve({ - json: () => - Promise.resolve({ - en: {stringConstant: "localized string"}, - }), - ok: true, - }), - ) as jest.Mock; - global.URL.canParse = jest.fn(() => true) as jest.Mock; - }); - - it("should get prompt json which matches the state of the UI", async () => { - // Arrange - const {renderer} = renderQuestion(question1, {isMobile: false}); - - // Act - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: - "Do this fun PhET simulation! A projectile data lab!\n[[\u2603 phet-simulation 1]]\n", - widgets: { - "phet-simulation 1": { - type: "phet-simulation", - isSupported: false, - message: "", - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.test.ts index 775e8dda7a..07470e9704 100644 --- a/packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/phet-simulation/prompt-utils.test.ts @@ -1,6 +1,42 @@ -import {getPromptJSON} from "./prompt-utils"; +import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; + +import {getPromptJSON} from "./phet-simulation-ai-utils"; + +import type {PerseusRenderer} from "../../perseus-types"; + +const question1: PerseusRenderer = { + content: + "Do this fun PhET simulation! A projectile data lab!\n[[\u2603 phet-simulation 1]]\n", + images: {}, + widgets: { + "phet-simulation 1": { + graded: false, + version: {major: 0, minor: 0}, + static: false, + type: "phet-simulation", + options: { + url: "https://phet.colorado.edu/sims/html/projectile-data-lab/latest/projectile-data-lab_all.html", + description: "Projectile Data Lab", + }, + alignment: "default", + }, + }, +}; + +describe("PhET Simulation AI utils", () => { + beforeEach(() => { + global.fetch = jest.fn(() => + Promise.resolve({ + json: () => + Promise.resolve({ + en: {stringConstant: "localized string"}, + }), + ok: true, + }), + ) as jest.Mock; + global.URL.canParse = jest.fn(() => true) as jest.Mock; + }); -describe("PhET Simulation getPromptJSON", () => { it("it returns JSON with the expected format and fields", () => { const resultJSON = getPromptJSON(); @@ -10,4 +46,25 @@ describe("PhET Simulation getPromptJSON", () => { message: "", }); }); + + it("should get prompt json which matches the state of the UI", async () => { + // Arrange + const {renderer} = renderQuestion(question1, {isMobile: false}); + + // Act + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: + "Do this fun PhET simulation! A projectile data lab!\n[[\u2603 phet-simulation 1]]\n", + widgets: { + "phet-simulation 1": { + type: "phet-simulation", + isSupported: false, + message: "", + }, + }, + }); + }); }); diff --git a/packages/perseus/src/widgets/passage-ref/passage-ref.tsx b/packages/perseus/src/widgets/passage-ref/passage-ref.tsx index ba451f8fa8..f6f1007621 100644 --- a/packages/perseus/src/widgets/passage-ref/passage-ref.tsx +++ b/packages/perseus/src/widgets/passage-ref/passage-ref.tsx @@ -4,13 +4,13 @@ import _ from "underscore"; import {PerseusI18nContext} from "../../components/i18n-context"; import * as Changeable from "../../mixins/changeable"; import PerseusMarkdown from "../../perseus-markdown"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/passage-ref/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/passage-ref/passage-ref-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import {isPassageWidget} from "../passage/utils"; import type {PerseusPassageRefWidgetOptions} from "../../perseus-types"; import type {ChangeFn, Widget, WidgetExports, WidgetProps} from "../../types"; -import type {PassageRefPromptJSON} from "../../widget-ai-utils/passage-ref/prompt-utils"; +import type {PassageRefPromptJSON} from "../../widget-ai-utils/passage-ref/passage-ref-ai-utils"; const EN_DASH = "\u2013"; diff --git a/packages/perseus/src/widgets/phet-simulation/phet-simulation.tsx b/packages/perseus/src/widgets/phet-simulation/phet-simulation.tsx index f9e1756469..4c63ec1eec 100644 --- a/packages/perseus/src/widgets/phet-simulation/phet-simulation.tsx +++ b/packages/perseus/src/widgets/phet-simulation/phet-simulation.tsx @@ -14,7 +14,7 @@ import * as React from "react"; import {PerseusI18nContext} from "../../components/i18n-context"; import {getDependencies} from "../../dependencies"; import {phoneMargin} from "../../styles/constants"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/phet-simulation/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/phet-simulation/phet-simulation-ai-utils"; import type {PerseusPhetSimulationWidgetOptions} from "../../perseus-types"; import type {WidgetExports, WidgetProps, Widget} from "../../types"; From bd4cd44330c71c7cb90bec2460bce35e63f87f9d Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:02:25 -0600 Subject: [PATCH 21/27] plotter and python --- ...utils.test.ts => plotter-ai-utils.test.ts} | 4 +- .../{prompt-utils.ts => plotter-ai-utils.ts} | 0 .../python-program/prompt-utils.test.ts | 45 ++++++++++++++++++- .../{prompt-utils.ts => python-ai-utils.ts} | 0 .../python-program/python-program.test.ts | 42 ----------------- .../perseus/src/widgets/plotter/plotter.tsx | 2 +- .../widgets/python-program/python-program.tsx | 2 +- 7 files changed, 47 insertions(+), 48 deletions(-) rename packages/perseus/src/widget-ai-utils/plotter/{prompt-utils.test.ts => plotter-ai-utils.test.ts} (74%) rename packages/perseus/src/widget-ai-utils/plotter/{prompt-utils.ts => plotter-ai-utils.ts} (100%) rename packages/perseus/src/widget-ai-utils/python-program/{prompt-utils.ts => python-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/python-program/python-program.test.ts diff --git a/packages/perseus/src/widget-ai-utils/plotter/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/plotter/plotter-ai-utils.test.ts similarity index 74% rename from packages/perseus/src/widget-ai-utils/plotter/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/plotter/plotter-ai-utils.test.ts index 5c8ad40653..86f0d3d822 100644 --- a/packages/perseus/src/widget-ai-utils/plotter/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/plotter/plotter-ai-utils.test.ts @@ -1,6 +1,6 @@ -import {getPromptJSON} from "./prompt-utils"; +import {getPromptJSON} from "./plotter-ai-utils"; -describe("Plotter getPromptJSON", () => { +describe("Plotter AI utils", () => { it("it returns JSON with the expected format and fields", () => { const resultJSON = getPromptJSON(); diff --git a/packages/perseus/src/widget-ai-utils/plotter/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/plotter/plotter-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/plotter/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/plotter/plotter-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts index 26e6546492..d4a73293b3 100644 --- a/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts @@ -1,6 +1,27 @@ -import {getPromptJSON} from "./prompt-utils"; +import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; -describe("Python Program getPromptJSON", () => { +import {getPromptJSON} from "./python-ai-utils"; + +import type {PerseusRenderer} from "../../perseus-types"; + +export const question1: PerseusRenderer = { + content: "[[\u2603 python-program 1]]\n\n", + images: {}, + widgets: { + "python-program 1": { + version: {major: 0, minor: 0}, + static: false, + type: "python-program", + options: { + height: 400, + programID: "5207287069147136", + }, + alignment: "block", + }, + }, +}; + +describe("Python Program AI utils", () => { it("it returns JSON with the expected format and fields", () => { const resultJSON = getPromptJSON(); @@ -10,4 +31,24 @@ describe("Python Program getPromptJSON", () => { message: "", }); }); + + it("should get prompt json which matches the state of the UI", async () => { + // Arrange + const {renderer} = renderQuestion(question1, {isMobile: false}); + + // Act + const json = renderer.getPromptJSON(); + + // Assert + expect(json).toEqual({ + content: "[[\u2603 python-program 1]]\n\n", + widgets: { + "python-program 1": { + type: "python-program", + isSupported: false, + message: "", + }, + }, + }); + }); }); diff --git a/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/python-program/python-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/python-program/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/python-program/python-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/python-program/python-program.test.ts b/packages/perseus/src/widget-ai-utils/python-program/python-program.test.ts deleted file mode 100644 index 1114150530..0000000000 --- a/packages/perseus/src/widget-ai-utils/python-program/python-program.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; - -import type {PerseusRenderer} from "../../perseus-types"; - -export const question1: PerseusRenderer = { - content: "[[\u2603 python-program 1]]\n\n", - images: {}, - widgets: { - "python-program 1": { - version: {major: 0, minor: 0}, - static: false, - type: "python-program", - options: { - height: 400, - programID: "5207287069147136", - }, - alignment: "block", - }, - }, -}; - -describe("python-program widget", () => { - it("should get prompt json which matches the state of the UI", async () => { - // Arrange - const {renderer} = renderQuestion(question1, {isMobile: false}); - - // Act - const json = renderer.getPromptJSON(); - - // Assert - expect(json).toEqual({ - content: "[[\u2603 python-program 1]]\n\n", - widgets: { - "python-program 1": { - type: "python-program", - isSupported: false, - message: "", - }, - }, - }); - }); -}); diff --git a/packages/perseus/src/widgets/plotter/plotter.tsx b/packages/perseus/src/widgets/plotter/plotter.tsx index 5e557966c9..65dd21cb37 100644 --- a/packages/perseus/src/widgets/plotter/plotter.tsx +++ b/packages/perseus/src/widgets/plotter/plotter.tsx @@ -11,7 +11,7 @@ import {ClassNames as ApiClassNames} from "../../perseus-api"; import KhanColors from "../../util/colors"; import GraphUtils from "../../util/graph-utils"; import KhanMath from "../../util/math"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/plotter/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/plotter/plotter-ai-utils"; import scorePlotter from "./score-plotter"; diff --git a/packages/perseus/src/widgets/python-program/python-program.tsx b/packages/perseus/src/widgets/python-program/python-program.tsx index 61fc8cdbcf..eb688d227d 100644 --- a/packages/perseus/src/widgets/python-program/python-program.tsx +++ b/packages/perseus/src/widgets/python-program/python-program.tsx @@ -6,7 +6,7 @@ import {StyleSheet} from "aphrodite"; import * as React from "react"; import {toAbsoluteUrl} from "../../util/url-utils"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/python-program/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/python-program/python-ai-utils"; import type {FocusPath, Widget, WidgetExports} from "../../types"; import type {UnsupportedWidgetPromptJSON} from "../../widget-ai-utils/unsupported-widget"; From 8107663a666296257af8b1c7c7a5b52db1cb99c7 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:08:07 -0600 Subject: [PATCH 22/27] radio --- packages/perseus/src/prompt-types.ts | 2 +- ...-utils.test.ts => python-ai-utils.test.ts} | 0 .../radio/prompt-utils.test.ts | 55 ------------------- .../{radio.test.ts => radio-ai-utils.test.ts} | 55 ++++++++++++++++++- .../{prompt-utils.ts => radio-ai-utils.ts} | 0 .../src/widgets/radio/radio-component.tsx | 4 +- 6 files changed, 57 insertions(+), 59 deletions(-) rename packages/perseus/src/widget-ai-utils/python-program/{prompt-utils.test.ts => python-ai-utils.test.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/radio/prompt-utils.test.ts rename packages/perseus/src/widget-ai-utils/radio/{radio.test.ts => radio-ai-utils.test.ts} (66%) rename packages/perseus/src/widget-ai-utils/radio/{prompt-utils.ts => radio-ai-utils.ts} (100%) diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts index 3d41f064c5..1baa529179 100644 --- a/packages/perseus/src/prompt-types.ts +++ b/packages/perseus/src/prompt-types.ts @@ -17,7 +17,7 @@ import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/promp import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/orderer-ai-utils"; import type {PassagePromptJSON} from "./widget-ai-utils/passage/passage-ai-utils"; import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/passage-ref-ai-utils"; -import type {RadioPromptJSON} from "./widget-ai-utils/radio/prompt-utils"; +import type {RadioPromptJSON} from "./widget-ai-utils/radio/radio-ai-utils"; import type {SorterPromptJSON} from "./widget-ai-utils/sorter/prompt-utils"; import type {UnsupportedWidgetPromptJSON} from "./widget-ai-utils/unsupported-widget"; diff --git a/packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/python-program/python-ai-utils.test.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/python-program/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/python-program/python-ai-utils.test.ts diff --git a/packages/perseus/src/widget-ai-utils/radio/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/radio/prompt-utils.test.ts deleted file mode 100644 index a05196d202..0000000000 --- a/packages/perseus/src/widget-ai-utils/radio/prompt-utils.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import {getPromptJSON} from "./prompt-utils"; - -import type {PerseusRadioUserInput} from "../../validation.types"; - -describe("Radio getPromptJSON", () => { - it("should get prompt json which matches the state of the UI", () => { - const renderProps: any = { - numCorrect: 1, - countChoices: false, - deselectEnabled: false, - hasNoneOfTheAbove: false, - multipleSelect: false, - choices: [ - { - content: "Content 4", - originalIndex: 3, - }, - { - content: "Content 2", - originalIndex: 1, - }, - { - content: "Content 1", - originalIndex: 0, - }, - - { - content: "Content 3", - originalIndex: 2, - }, - ], - selectedChoices: [true, false, false, false], - }; - - const userInput: PerseusRadioUserInput = { - choicesSelected: [true, false, false, false], - }; - - const resultJSON = getPromptJSON(renderProps, userInput); - - expect(resultJSON).toEqual({ - type: "radio", - hasNoneOfTheAbove: false, - options: [ - {value: "Content 4"}, - {value: "Content 2"}, - {value: "Content 1"}, - {value: "Content 3"}, - ], - userInput: { - selectedOptions: [true, false, false, false], - }, - }); - }); -}); diff --git a/packages/perseus/src/widget-ai-utils/radio/radio.test.ts b/packages/perseus/src/widget-ai-utils/radio/radio-ai-utils.test.ts similarity index 66% rename from packages/perseus/src/widget-ai-utils/radio/radio.test.ts rename to packages/perseus/src/widget-ai-utils/radio/radio-ai-utils.test.ts index b4a883d0b3..f0b984a586 100644 --- a/packages/perseus/src/widget-ai-utils/radio/radio.test.ts +++ b/packages/perseus/src/widget-ai-utils/radio/radio-ai-utils.test.ts @@ -5,7 +5,10 @@ import {testDependencies} from "../../../../../testing/test-dependencies"; import * as Dependencies from "../../dependencies"; import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; +import {getPromptJSON} from "./radio-ai-utils"; + import type {PerseusRenderer, RadioWidget} from "../../perseus-types"; +import type {PerseusRadioUserInput} from "../../validation.types"; import type {UserEvent} from "@testing-library/user-event"; const shuffledQuestion: PerseusRenderer = { @@ -52,7 +55,7 @@ const shuffledQuestion: PerseusRenderer = { }, }; -describe("radio widget", () => { +describe("Radio AI utils", () => { let userEvent: UserEvent; beforeEach(() => { userEvent = userEventLib.setup({ @@ -64,6 +67,56 @@ describe("radio widget", () => { ); }); + it("should get prompt json which matches the state of the UI", () => { + const renderProps: any = { + numCorrect: 1, + countChoices: false, + deselectEnabled: false, + hasNoneOfTheAbove: false, + multipleSelect: false, + choices: [ + { + content: "Content 4", + originalIndex: 3, + }, + { + content: "Content 2", + originalIndex: 1, + }, + { + content: "Content 1", + originalIndex: 0, + }, + + { + content: "Content 3", + originalIndex: 2, + }, + ], + selectedChoices: [true, false, false, false], + }; + + const userInput: PerseusRadioUserInput = { + choicesSelected: [true, false, false, false], + }; + + const resultJSON = getPromptJSON(renderProps, userInput); + + expect(resultJSON).toEqual({ + type: "radio", + hasNoneOfTheAbove: false, + options: [ + {value: "Content 4"}, + {value: "Content 2"}, + {value: "Content 1"}, + {value: "Content 3"}, + ], + userInput: { + selectedOptions: [true, false, false, false], + }, + }); + }); + it("should get prompt json which matches the state of the UI", async () => { const indexToSelect = 1; const {renderer} = renderQuestion(shuffledQuestion); diff --git a/packages/perseus/src/widget-ai-utils/radio/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/radio/radio-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/radio/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/radio/radio-ai-utils.ts diff --git a/packages/perseus/src/widgets/radio/radio-component.tsx b/packages/perseus/src/widgets/radio/radio-component.tsx index 5be5ac3110..7feefac6c7 100644 --- a/packages/perseus/src/widgets/radio/radio-component.tsx +++ b/packages/perseus/src/widgets/radio/radio-component.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import {PerseusI18nContext} from "../../components/i18n-context"; import Renderer from "../../renderer"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/radio/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/radio/radio-ai-utils"; import PassageRef from "../passage-ref/passage-ref"; import BaseRadio from "./base-radio"; @@ -21,7 +21,7 @@ import type { PerseusRadioRubric, PerseusRadioUserInput, } from "../../validation.types"; -import type {RadioPromptJSON} from "../../widget-ai-utils/radio/prompt-utils"; +import type {RadioPromptJSON} from "../../widget-ai-utils/radio/radio-ai-utils"; // RenderProps is the return type for radio.jsx#transform export type RenderProps = { From 2c21b4aef3b7122866eac2b94f15af7e1d35befb Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:10:33 -0600 Subject: [PATCH 23/27] sorter and video --- packages/perseus/src/index.ts | 5 +- packages/perseus/src/prompt-types.ts | 68 ------------------- packages/perseus/src/renderer.tsx | 5 +- packages/perseus/src/server-item-renderer.tsx | 5 +- packages/perseus/src/types.ts | 2 +- .../graded-group/graded-group-ai-utils.ts | 2 +- .../group/group-ai-utils.test.ts | 2 +- .../widget-ai-utils/group/group-ai-utils.ts | 2 +- .../src/widget-ai-utils/prompt-types.ts | 68 +++++++++++++++++++ ...-utils.test.ts => sorter-ai-utils.test.ts} | 4 +- .../{prompt-utils.ts => sorter-ai-utils.ts} | 0 .../src/widget-ai-utils/unsupported-widget.ts | 2 +- .../video/prompt-utils.test.ts | 44 +++++++++++- .../{prompt-utils.ts => video-ai-utils.ts} | 0 .../src/widget-ai-utils/video/video.test.ts | 41 ----------- .../perseus/src/widgets/sorter/sorter.tsx | 4 +- packages/perseus/src/widgets/video/video.tsx | 2 +- 17 files changed, 132 insertions(+), 124 deletions(-) delete mode 100644 packages/perseus/src/prompt-types.ts create mode 100644 packages/perseus/src/widget-ai-utils/prompt-types.ts rename packages/perseus/src/widget-ai-utils/sorter/{prompt-utils.test.ts => sorter-ai-utils.test.ts} (86%) rename packages/perseus/src/widget-ai-utils/sorter/{prompt-utils.ts => sorter-ai-utils.ts} (100%) rename packages/perseus/src/widget-ai-utils/video/{prompt-utils.ts => video-ai-utils.ts} (100%) delete mode 100644 packages/perseus/src/widget-ai-utils/video/video.test.ts diff --git a/packages/perseus/src/index.ts b/packages/perseus/src/index.ts index 93b4e91bb4..b83df9cb0c 100644 --- a/packages/perseus/src/index.ts +++ b/packages/perseus/src/index.ts @@ -254,4 +254,7 @@ export type { TagsShape, } from "./multi-items/shape-types"; export type {Path} from "./multi-items/trees"; -export type {RendererPromptJSON, WidgetPromptJSON} from "./prompt-types"; +export type { + RendererPromptJSON, + WidgetPromptJSON, +} from "./widget-ai-utils/prompt-types"; diff --git a/packages/perseus/src/prompt-types.ts b/packages/perseus/src/prompt-types.ts deleted file mode 100644 index 1baa529179..0000000000 --- a/packages/perseus/src/prompt-types.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type {CategorizerPromptJSON} from "./widget-ai-utils/categorizer/categorizer-ai-utils"; -import type {DefinitionPromptJSON} from "./widget-ai-utils/definition/definition-ai-utils"; -import type {DropdownPromptJSON} from "./widget-ai-utils/dropdown/dropdown-ai-utils"; -import type {ExplanationPromptJSON} from "./widget-ai-utils/explanation/explanation-ai-utils"; -import type {ExpressionPromptJSON} from "./widget-ai-utils/expression/expression-ai-utils"; -import type {GradedGroupPromptJSON} from "./widget-ai-utils/graded-group/graded-group-ai-utils"; -import type {GradedGroupSetPromptJSON} from "./widget-ai-utils/graded-group-set/graded-group-set-ai-utils"; -import type {GrapherPromptJSON} from "./widget-ai-utils/grapher/grapher-ai-utils"; -import type {GroupPromptJSON} from "./widget-ai-utils/group/group-ai-utils"; -import type {ImagePromptJSON} from "./widget-ai-utils/image/image-ai-utils"; -import type {InputNumberPromptJSON} from "./widget-ai-utils/input-number/input-number-ai-utils"; -import type {LabelImagePromptJSON} from "./widget-ai-utils/label-image/label-image-ai-utils"; -import type {MatcherPromptJSON} from "./widget-ai-utils/matcher/matcher-ai-utils"; -import type {MatrixPromptJSON} from "./widget-ai-utils/matrix/matrix-ai-utils"; -import type {NumberLinePromptJSON} from "./widget-ai-utils/number-line/number-line-ai-utils"; -import type {NumericInputPromptJSON} from "./widget-ai-utils/numeric-input/prompt-utils"; -import type {OrdererPromptJSON} from "./widget-ai-utils/orderer/orderer-ai-utils"; -import type {PassagePromptJSON} from "./widget-ai-utils/passage/passage-ai-utils"; -import type {PassageRefPromptJSON} from "./widget-ai-utils/passage-ref/passage-ref-ai-utils"; -import type {RadioPromptJSON} from "./widget-ai-utils/radio/radio-ai-utils"; -import type {SorterPromptJSON} from "./widget-ai-utils/sorter/prompt-utils"; -import type {UnsupportedWidgetPromptJSON} from "./widget-ai-utils/unsupported-widget"; - -export type UnsupportedWidget = - | "cs-program" - | "iframe" - | "interaction" - | "interactive-graph-unsupported" - | "measurer" - | "phet-simulation" - | "plotter" - | "python-program" - | "video"; - -export type WidgetPromptJSON = - | CategorizerPromptJSON - | DefinitionPromptJSON - | DropdownPromptJSON - | ExplanationPromptJSON - | ExpressionPromptJSON - | GradedGroupPromptJSON - | GradedGroupSetPromptJSON - | GrapherPromptJSON - | GroupPromptJSON - | ImagePromptJSON - | InputNumberPromptJSON - | LabelImagePromptJSON - | MatcherPromptJSON - | MatrixPromptJSON - | NumberLinePromptJSON - | NumericInputPromptJSON - | OrdererPromptJSON - | PassagePromptJSON - | PassageRefPromptJSON - | RadioPromptJSON - | SorterPromptJSON - | UnsupportedWidgetPromptJSON; - -export type RendererPromptJSON = { - content: string; - widgets: { - [widgetId: string]: WidgetPromptJSON; - }; -}; - -export interface GetPromptJSONInterface { - getPromptJSON(): RendererPromptJSON; -} diff --git a/packages/perseus/src/renderer.tsx b/packages/perseus/src/renderer.tsx index 2b9c5452f4..2635be41c8 100644 --- a/packages/perseus/src/renderer.tsx +++ b/packages/perseus/src/renderer.tsx @@ -40,7 +40,10 @@ import type { PerseusWidgetsMap, ShowSolutions, } from "./perseus-types"; -import type {GetPromptJSONInterface, RendererPromptJSON} from "./prompt-types"; +import type { + GetPromptJSONInterface, + RendererPromptJSON, +} from "./widget-ai-utils/prompt-types"; import type {PerseusStrings} from "./strings"; import type { APIOptions, diff --git a/packages/perseus/src/server-item-renderer.tsx b/packages/perseus/src/server-item-renderer.tsx index 519eafecb2..1eb81ffe89 100644 --- a/packages/perseus/src/server-item-renderer.tsx +++ b/packages/perseus/src/server-item-renderer.tsx @@ -22,7 +22,10 @@ import Renderer from "./renderer"; import Util from "./util"; import type {PerseusItem, ShowSolutions} from "./perseus-types"; -import type {GetPromptJSONInterface, RendererPromptJSON} from "./prompt-types"; +import type { + GetPromptJSONInterface, + RendererPromptJSON, +} from "./widget-ai-utils/prompt-types"; import type { FocusPath, PerseusDependenciesV2, diff --git a/packages/perseus/src/types.ts b/packages/perseus/src/types.ts index 136fccd5a8..ec8da5f961 100644 --- a/packages/perseus/src/types.ts +++ b/packages/perseus/src/types.ts @@ -7,7 +7,6 @@ import type { PerseusWidget, PerseusWidgetsMap, } from "./perseus-types"; -import type {WidgetPromptJSON} from "./prompt-types"; import type {PerseusStrings} from "./strings"; import type {SizeClass} from "./util/sizing-utils"; import type { @@ -16,6 +15,7 @@ import type { UserInputArray, UserInputMap, } from "./validation.types"; +import type {WidgetPromptJSON} from "./widget-ai-utils/prompt-types"; import type {KeypadAPI} from "@khanacademy/math-input"; import type {AnalyticsEventHandlerFn} from "@khanacademy/perseus-core"; import type {LinterContextProps} from "@khanacademy/perseus-linter"; diff --git a/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts index 76d81250bd..8f9ab6a30a 100644 --- a/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts +++ b/packages/perseus/src/widget-ai-utils/graded-group/graded-group-ai-utils.ts @@ -1,4 +1,4 @@ -import type {RendererPromptJSON} from "../../prompt-types"; +import type {RendererPromptJSON} from "../prompt-types"; export type GradedGroupPromptJSON = RendererPromptJSON & { type: "graded-group"; diff --git a/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts index e65317a325..cccb08b76d 100644 --- a/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.test.ts @@ -3,7 +3,7 @@ import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; import {getPromptJSON} from "./group-ai-utils"; import {question1} from "./group-ai-utils.testdata"; -import type {RendererPromptJSON} from "../../prompt-types"; +import type {RendererPromptJSON} from "../prompt-types"; describe("Group AI utils", () => { it("should return a GroupPromptJSON with default values when rendererJSON is undefined", () => { diff --git a/packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts index c2cc9f5cbb..7a269c5e98 100644 --- a/packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts +++ b/packages/perseus/src/widget-ai-utils/group/group-ai-utils.ts @@ -1,4 +1,4 @@ -import type {RendererPromptJSON} from "../../prompt-types"; +import type {RendererPromptJSON} from "../prompt-types"; export type GroupPromptJSON = RendererPromptJSON & { type: "group"; diff --git a/packages/perseus/src/widget-ai-utils/prompt-types.ts b/packages/perseus/src/widget-ai-utils/prompt-types.ts new file mode 100644 index 0000000000..83aa7fabd2 --- /dev/null +++ b/packages/perseus/src/widget-ai-utils/prompt-types.ts @@ -0,0 +1,68 @@ +import type {CategorizerPromptJSON} from "./categorizer/categorizer-ai-utils"; +import type {DefinitionPromptJSON} from "./definition/definition-ai-utils"; +import type {DropdownPromptJSON} from "./dropdown/dropdown-ai-utils"; +import type {ExplanationPromptJSON} from "./explanation/explanation-ai-utils"; +import type {ExpressionPromptJSON} from "./expression/expression-ai-utils"; +import type {GradedGroupPromptJSON} from "./graded-group/graded-group-ai-utils"; +import type {GradedGroupSetPromptJSON} from "./graded-group-set/graded-group-set-ai-utils"; +import type {GrapherPromptJSON} from "./grapher/grapher-ai-utils"; +import type {GroupPromptJSON} from "./group/group-ai-utils"; +import type {ImagePromptJSON} from "./image/image-ai-utils"; +import type {InputNumberPromptJSON} from "./input-number/input-number-ai-utils"; +import type {LabelImagePromptJSON} from "./label-image/label-image-ai-utils"; +import type {MatcherPromptJSON} from "./matcher/matcher-ai-utils"; +import type {MatrixPromptJSON} from "./matrix/matrix-ai-utils"; +import type {NumberLinePromptJSON} from "./number-line/number-line-ai-utils"; +import type {NumericInputPromptJSON} from "./numeric-input/prompt-utils"; +import type {OrdererPromptJSON} from "./orderer/orderer-ai-utils"; +import type {PassagePromptJSON} from "./passage/passage-ai-utils"; +import type {PassageRefPromptJSON} from "./passage-ref/passage-ref-ai-utils"; +import type {RadioPromptJSON} from "./radio/radio-ai-utils"; +import type {SorterPromptJSON} from "./sorter/sorter-ai-utils"; +import type {UnsupportedWidgetPromptJSON} from "./unsupported-widget"; + +export type UnsupportedWidget = + | "cs-program" + | "iframe" + | "interaction" + | "interactive-graph-unsupported" + | "measurer" + | "phet-simulation" + | "plotter" + | "python-program" + | "video"; + +export type WidgetPromptJSON = + | CategorizerPromptJSON + | DefinitionPromptJSON + | DropdownPromptJSON + | ExplanationPromptJSON + | ExpressionPromptJSON + | GradedGroupPromptJSON + | GradedGroupSetPromptJSON + | GrapherPromptJSON + | GroupPromptJSON + | ImagePromptJSON + | InputNumberPromptJSON + | LabelImagePromptJSON + | MatcherPromptJSON + | MatrixPromptJSON + | NumberLinePromptJSON + | NumericInputPromptJSON + | OrdererPromptJSON + | PassagePromptJSON + | PassageRefPromptJSON + | RadioPromptJSON + | SorterPromptJSON + | UnsupportedWidgetPromptJSON; + +export type RendererPromptJSON = { + content: string; + widgets: { + [widgetId: string]: WidgetPromptJSON; + }; +}; + +export interface GetPromptJSONInterface { + getPromptJSON(): RendererPromptJSON; +} diff --git a/packages/perseus/src/widget-ai-utils/sorter/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/sorter/sorter-ai-utils.test.ts similarity index 86% rename from packages/perseus/src/widget-ai-utils/sorter/prompt-utils.test.ts rename to packages/perseus/src/widget-ai-utils/sorter/sorter-ai-utils.test.ts index 341083ca12..6d8fa17169 100644 --- a/packages/perseus/src/widget-ai-utils/sorter/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/sorter/sorter-ai-utils.test.ts @@ -1,8 +1,8 @@ -import {getPromptJSON} from "./prompt-utils"; +import {getPromptJSON} from "./sorter-ai-utils"; import type {PerseusSorterUserInput} from "../../validation.types"; -describe("Sorter getPromptJSON", () => { +describe("Sorter AI utils", () => { it("it returns JSON with the expected format and fields", () => { const userInput: PerseusSorterUserInput = { options: ["Pickles", "Tomato", "Onion", "Lettuce"], diff --git a/packages/perseus/src/widget-ai-utils/sorter/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/sorter/sorter-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/sorter/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/sorter/sorter-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/unsupported-widget.ts b/packages/perseus/src/widget-ai-utils/unsupported-widget.ts index c9c60cc1c7..5c25434a1e 100644 --- a/packages/perseus/src/widget-ai-utils/unsupported-widget.ts +++ b/packages/perseus/src/widget-ai-utils/unsupported-widget.ts @@ -1,4 +1,4 @@ -import type {UnsupportedWidget} from "../prompt-types"; +import type {UnsupportedWidget} from "./prompt-types"; export type UnsupportedWidgetPromptJSON = { type: UnsupportedWidget; diff --git a/packages/perseus/src/widget-ai-utils/video/prompt-utils.test.ts b/packages/perseus/src/widget-ai-utils/video/prompt-utils.test.ts index 9d9e80d901..66b6806568 100644 --- a/packages/perseus/src/widget-ai-utils/video/prompt-utils.test.ts +++ b/packages/perseus/src/widget-ai-utils/video/prompt-utils.test.ts @@ -1,6 +1,30 @@ -import {getPromptJSON} from "./prompt-utils"; +import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; -describe("Video getPromptJSON", () => { +import {getPromptJSON} from "./video-ai-utils"; + +import type {PerseusRenderer} from "../../perseus-types"; +import type {UnsupportedWidgetPromptJSON} from "../unsupported-widget"; + +export const question: PerseusRenderer = { + content: + "Watch the Biogeography: Where Life Lives video to find the answer.\n\n[[\u2603 video 1]]\n\n", + images: {}, + widgets: { + "video 1": { + graded: true, + version: {major: 0, minor: 0}, + static: false, + type: "video", + options: { + static: false, + location: "biogeography-where-life-lives", + }, + alignment: "block", + }, + }, +}; + +describe("Video AI utils", () => { it("it returns JSON with the expected format and fields", () => { const resultJSON = getPromptJSON(); @@ -10,4 +34,20 @@ describe("Video getPromptJSON", () => { message: "", }); }); + + it("should get prompt json which matches the state of the UI", async () => { + // Arrange + const {renderer} = renderQuestion(question, {isMobile: false}); + const widget = renderer.getWidgetInstance("video 1"); + + // Act + const json = widget?.getPromptJSON?.() as UnsupportedWidgetPromptJSON; + + // Assert + expect(json).toEqual({ + type: "video", + isSupported: false, + message: "", + }); + }); }); diff --git a/packages/perseus/src/widget-ai-utils/video/prompt-utils.ts b/packages/perseus/src/widget-ai-utils/video/video-ai-utils.ts similarity index 100% rename from packages/perseus/src/widget-ai-utils/video/prompt-utils.ts rename to packages/perseus/src/widget-ai-utils/video/video-ai-utils.ts diff --git a/packages/perseus/src/widget-ai-utils/video/video.test.ts b/packages/perseus/src/widget-ai-utils/video/video.test.ts deleted file mode 100644 index 7c7a351cd3..0000000000 --- a/packages/perseus/src/widget-ai-utils/video/video.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import {renderQuestion} from "../../widgets/__testutils__/renderQuestion"; - -import type {PerseusRenderer} from "../../perseus-types"; -import type {UnsupportedWidgetPromptJSON} from "../unsupported-widget"; - -export const question: PerseusRenderer = { - content: - "Watch the Biogeography: Where Life Lives video to find the answer.\n\n[[\u2603 video 1]]\n\n", - images: {}, - widgets: { - "video 1": { - graded: true, - version: {major: 0, minor: 0}, - static: false, - type: "video", - options: { - static: false, - location: "biogeography-where-life-lives", - }, - alignment: "block", - }, - }, -}; - -describe("cs-program widget", () => { - it("should get prompt json which matches the state of the UI", async () => { - // Arrange - const {renderer} = renderQuestion(question, {isMobile: false}); - const widget = renderer.getWidgetInstance("video 1"); - - // Act - const json = widget?.getPromptJSON?.() as UnsupportedWidgetPromptJSON; - - // Assert - expect(json).toEqual({ - type: "video", - isSupported: false, - message: "", - }); - }); -}); diff --git a/packages/perseus/src/widgets/sorter/sorter.tsx b/packages/perseus/src/widgets/sorter/sorter.tsx index 247a018811..aba485d2b8 100644 --- a/packages/perseus/src/widgets/sorter/sorter.tsx +++ b/packages/perseus/src/widgets/sorter/sorter.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import Sortable from "../../components/sortable"; import Util from "../../util"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/sorter/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/sorter/sorter-ai-utils"; import scoreSorter from "./score-sorter"; @@ -14,7 +14,7 @@ import type { PerseusSorterRubric, PerseusSorterUserInput, } from "../../validation.types"; -import type {SorterPromptJSON} from "../../widget-ai-utils/sorter/prompt-utils"; +import type {SorterPromptJSON} from "../../widget-ai-utils/sorter/sorter-ai-utils"; const {shuffle} = Util; diff --git a/packages/perseus/src/widgets/video/video.tsx b/packages/perseus/src/widgets/video/video.tsx index 559d103b80..20385bcf45 100644 --- a/packages/perseus/src/widgets/video/video.tsx +++ b/packages/perseus/src/widgets/video/video.tsx @@ -11,7 +11,7 @@ import {PerseusI18nContext} from "../../components/i18n-context"; import {getDependencies} from "../../dependencies"; import * as Changeable from "../../mixins/changeable"; import a11y from "../../util/a11y"; -import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/video/prompt-utils"; +import {getPromptJSON as _getPromptJSON} from "../../widget-ai-utils/video/video-ai-utils"; import scoreNoop from "../__shared__/score-noop"; import VideoTranscriptLink from "./video-transcript-link"; From 4ac09ffcc6942c95a7229ae7f255375def8a6792 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:23:57 -0600 Subject: [PATCH 24/27] update REAME --- .../perseus/src/widget-ai-utils/README.md | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/packages/perseus/src/widget-ai-utils/README.md b/packages/perseus/src/widget-ai-utils/README.md index 0aac86c8cc..69c23f8475 100644 --- a/packages/perseus/src/widget-ai-utils/README.md +++ b/packages/perseus/src/widget-ai-utils/README.md @@ -1,15 +1,25 @@ -# What are these utilities for? +# Widget AI utils -We want to have a representation of Perseus widgets that LLMs can understand. -They understand JSON pretty well! This is a set of utility functions for each -widget that get the minimum amount of data for an LLM to understand the state -of the widget, as well as the current user input. +> [!CAUTION] +> This code is not meant to be interwoven with Perseus' core logic. +> We want to keep it as isolated as possible from the rest of Perseus. + +> [!CAUTION] +> Please be cautious making changes within this folder, +> it's an external API consumed by the team working on Khanmigo. + +## Motivation + +1. The team working on Khanmigo needed access to Perseus data for prompt engineering - including both external-facing things (like the ItemData and UserInput) and internal things (like RenderProps) +2. The Perseus team didn't want to write a blank check exposing all of our internal data because that would make it difficult for us to safely make internal changes +3. The goal of the helpers in this folder is to have a set of functions that can take _all_ the information the Khanmigo team could possibly want and return only the pieces the Khanmigo team needs + +## What are these utilities for? + +We want to have a representation of Perseus widgets that LLMs can understand. They understand JSON pretty well! This is a set of utility functions for each widget that get the minimum amount of data for an LLM to understand the state of the widget, as well as the current user input. ### Why don't we just pass the raw widget json? We do not want to have the an external API relying on the internal -representation of Perseus widgets. That would couple the widgets implementation -to an external API, which would be subject to breakage. This approach balances -having an explicit API, while keeping Perseus widgets mostly unaware of the -LLM prompt requirements. +representation of Perseus widgets. That would couple the widgets implementation to an external API, which would be subject to breakage. This approach balances having an explicit API, while keeping Perseus widgets mostly unaware of the LLM prompt requirements. From 7c966570af352dea2d1890ab9c30901b5750516f Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 15:32:40 -0600 Subject: [PATCH 25/27] changeset --- .changeset/nasty-bikes-travel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nasty-bikes-travel.md diff --git a/.changeset/nasty-bikes-travel.md b/.changeset/nasty-bikes-travel.md new file mode 100644 index 0000000000..dd76f3b00a --- /dev/null +++ b/.changeset/nasty-bikes-travel.md @@ -0,0 +1,5 @@ +--- +"@khanacademy/perseus": patch +--- + +Rename AI util files From a446e01f68b35034ef444c6a8020a381a66c0f3f Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 16:19:08 -0600 Subject: [PATCH 26/27] run lint fix --- packages/perseus/src/renderer.tsx | 8 ++++---- packages/perseus/src/server-item-renderer.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/perseus/src/renderer.tsx b/packages/perseus/src/renderer.tsx index 2635be41c8..41e383a641 100644 --- a/packages/perseus/src/renderer.tsx +++ b/packages/perseus/src/renderer.tsx @@ -40,10 +40,6 @@ import type { PerseusWidgetsMap, ShowSolutions, } from "./perseus-types"; -import type { - GetPromptJSONInterface, - RendererPromptJSON, -} from "./widget-ai-utils/prompt-types"; import type {PerseusStrings} from "./strings"; import type { APIOptions, @@ -56,6 +52,10 @@ import type { WidgetProps, } from "./types"; import type {UserInputArray, UserInputMap} from "./validation.types"; +import type { + GetPromptJSONInterface, + RendererPromptJSON, +} from "./widget-ai-utils/prompt-types"; import type {KeypadAPI} from "@khanacademy/math-input"; import type {LinterContextProps} from "@khanacademy/perseus-linter"; diff --git a/packages/perseus/src/server-item-renderer.tsx b/packages/perseus/src/server-item-renderer.tsx index 1eb81ffe89..41476dc8d7 100644 --- a/packages/perseus/src/server-item-renderer.tsx +++ b/packages/perseus/src/server-item-renderer.tsx @@ -22,15 +22,15 @@ import Renderer from "./renderer"; import Util from "./util"; import type {PerseusItem, ShowSolutions} from "./perseus-types"; -import type { - GetPromptJSONInterface, - RendererPromptJSON, -} from "./widget-ai-utils/prompt-types"; import type { FocusPath, PerseusDependenciesV2, SharedRendererProps, } from "./types"; +import type { + GetPromptJSONInterface, + RendererPromptJSON, +} from "./widget-ai-utils/prompt-types"; import type {KeypadAPI} from "@khanacademy/math-input"; import type { KeypadContextRendererInterface, From 37b72428827bb0ca772d84172276651614e09c32 Mon Sep 17 00:00:00 2001 From: Matthew Curtis Date: Thu, 14 Nov 2024 16:19:55 -0600 Subject: [PATCH 27/27] respond to Thirds feedback --- packages/perseus/src/widget-ai-utils/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/perseus/src/widget-ai-utils/README.md b/packages/perseus/src/widget-ai-utils/README.md index 69c23f8475..c5625e5556 100644 --- a/packages/perseus/src/widget-ai-utils/README.md +++ b/packages/perseus/src/widget-ai-utils/README.md @@ -21,5 +21,5 @@ We want to have a representation of Perseus widgets that LLMs can understand. Th ### Why don't we just pass the raw widget json? -We do not want to have the an external API relying on the internal +We do not want to have the external API relying on the internal representation of Perseus widgets. That would couple the widgets implementation to an external API, which would be subject to breakage. This approach balances having an explicit API, while keeping Perseus widgets mostly unaware of the LLM prompt requirements.