Skip to content

Commit

Permalink
Merge branch 'main' into feat/sanity
Browse files Browse the repository at this point in the history
  • Loading branch information
tomwisecodes authored Sep 23, 2024
2 parents df4b57c + f3aecfa commit 2016fdb
Show file tree
Hide file tree
Showing 12 changed files with 77 additions and 31 deletions.
7 changes: 7 additions & 0 deletions CHANGE_LOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## [1.7.2](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.7.1...v1.7.2) (2024-09-23)


### Bug Fixes

* use NextResponse.json() in case of middleware errors ([#150](https://github.com/oaknational/oak-ai-lesson-assistant/issues/150)) ([73259a2](https://github.com/oaknational/oak-ai-lesson-assistant/commit/73259a258d7ce571bb1e8de464a631f55acfd684))

## [1.7.1](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.7.0...v1.7.1) (2024-09-17)


Expand Down
2 changes: 1 addition & 1 deletion apps/nextjs/src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ function handleError(error: unknown): Response {
wrappedError.cause = error;
Sentry.captureException(wrappedError, { originalException: error });

return NextResponse.error();
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
}

const nextMiddleware: NextMiddleware = async (request, event) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,10 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder {
: undefined,
lessonPlanJsonSchema: JSON.stringify(LessonPlanJsonSchema),
llmResponseJsonSchema: JSON.stringify(LLMResponseJsonSchema),
isUsingStructuredOutput:
process.env.NEXT_PUBLIC_STRUCTURED_OUTPUTS_ENABLED === "true"
? true
: false,
};

return template(args);
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/models/lessonPlans.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ export class LessonPlans {
responseMode: "generate",
lessonPlanJsonSchema: JSON.stringify(LessonPlanJsonSchema),
llmResponseJsonSchema: JSON.stringify(LLMResponseJsonSchema),
isUsingStructuredOutput:
process.env.NEXT_PUBLIC_STRUCTURED_OUTPUTS_ENABLED === "true"
? true
: false,
});

const systemPrompt = compiledTemplate;
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/prompts/lesson-assistant/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export interface TemplateProps {
americanisms?: object[];
lessonPlanJsonSchema: string;
llmResponseJsonSchema: string;
isUsingStructuredOutput: boolean;
}

type TemplatePart = (props: TemplateProps) => string;
Expand Down Expand Up @@ -68,7 +69,7 @@ export const getPromptParts = (props: TemplateProps): TemplatePart[] => {
: undefined,
americanToBritishSection,
languageAndVoice,
schema,
props.isUsingStructuredOutput ? undefined : schema,
response,
signOff,
];
Expand All @@ -85,5 +86,7 @@ export const generatePromptPartsHash = (props: TemplateProps): string => {
const parts = getPromptParts(props);
const partsString = parts.map((part) => part.toString()).join("");
const hash = crypto.createHash("md5").update(partsString).digest("hex");
return `${props.responseMode}-${props.useRag ? "rag" : "noRag"}-${props.baseLessonPlan ? "basedOn" : "notBasedOn"}-${hash}`;
return `${props.responseMode}-${props.useRag ? "rag" : "noRag"}-${
props.baseLessonPlan ? "basedOn" : "notBasedOn"
}-${hash}`;
};
14 changes: 10 additions & 4 deletions packages/core/src/prompts/lesson-assistant/parts/body.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { TemplateProps } from "..";

export const body = ({ lessonPlan }: TemplateProps) => {
export const body = ({ lessonPlan, responseMode }: TemplateProps) => {
const { keyStage } = lessonPlan ?? {};
return `HOW TO WRITE A GOOD LESSON PLAN
A well-thought-out lesson plan should:
* Be age-appropriate for pupils studying in the UK${keyStage ? ` at Key Stage ${keyStage}` : ""}.
* Be age-appropriate for pupils studying in the UK${
keyStage ? ` at Key Stage ${keyStage}` : ""
}.
* Include the key learning points to take away from the lesson
* A check for the prior knowledge that the pupils have. We need to know that the pupils know certain things before we can take the next step in teaching them something that is based on that knowledge.
* Address common misconceptions about the topic
Expand Down Expand Up @@ -74,8 +76,12 @@ This example should instead appear as "A plant cell differs from an animal cell
QUIZZES
The lesson plan should begin with a Starter Quiz and end with an Exit Quiz.
Only generate these when requested in the instructions.
${
responseMode === "interactive"
? `Only generate these when requested in the instructions.
`
: ""
}
STARTER QUIZ
The Starter Quiz, which is presented to pupils at the start of the lesson should check the pupils' prior knowledge before starting the lesson.
The Starter Quiz should be based on the prior knowledge and potential misconceptions only within the prior knowledge.
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/prompts/lesson-assistant/parts/context.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { TemplateProps } from "..";

const interactiveOnly = `You will receive instructions about which part of the schema to generate at each step of the process.
This is because the lesson plan is a complex document that is best generated in stages, and you will be asked to create each stage in sequence with separate requests.`;

export const context = ({
lessonPlan: { subject, keyStage },
responseMode,
}: TemplateProps) => `You are Aila, a chatbot hosted on Oak National Academy's AI Experiments website, helping a teacher in a UK school to create a lesson plan (unless otherwise specified by the user) in British English about how a particular lesson should be designed and delivered by a teacher in a typical classroom environment.
The audience you should be writing for is another teacher in the school with whom you will be sharing your plan.
The pupils who will take part in the lesson are studying ${subject} at UK Key Stage ${keyStage}.
Expand All @@ -10,7 +14,6 @@ You will be provided with a lesson title, topic, key stage and subject on which
If a base lesson plan has been provided, use the values from this JSON document to derive these values.
Otherwise you should use the values provided by the user.
You will also be provided with a schema for the structure of the lesson plan that you should follow.
You will receive instructions about which part of the schema to generate at each step of the process.
This is because the lesson plan is a complex document that is best generated in stages, and you will be asked to create each stage in sequence with separate requests.
${responseMode === "interactive" ? interactiveOnly : ""}
At the end of the process, you will have generated a complete lesson plan that can be delivered by a teacher in a UK school.
The teacher who you are talking to will then be able to download the lesson plan, a set of presentation slides constructed from the lesson plan, and a set of worksheets that can be used to deliver the lesson.`;
17 changes: 10 additions & 7 deletions packages/core/src/prompts/lesson-assistant/parts/protocol.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import { TemplateProps } from "..";

const STRUCTURED_OUTPUTS_ENABLED =
process.env.NEXT_PUBLIC_STRUCTURED_OUTPUTS_ENABLED === "true" ? true : false;

const responseFormatWithStructuredOutputs = `{"response":"llmMessage", patches:[{},{}...], prompt:{}}`;
const responseFormatWithoutStructuredOutputs = `A series of JSON documents separated using the JSON Text Sequences specification, where each row is separated by the ␞ character and ends with a new line character.
Your response should be a series of patches followed by one and only one prompt to the user.`;

const responseFormat = STRUCTURED_OUTPUTS_ENABLED
? responseFormatWithStructuredOutputs
: responseFormatWithoutStructuredOutputs;
const responseFormat = ({
isUsingStructuredOutput,
}: {
isUsingStructuredOutput: boolean;
}) =>
isUsingStructuredOutput
? responseFormatWithStructuredOutputs
: responseFormatWithoutStructuredOutputs;

export const protocol = ({
isUsingStructuredOutput,
llmResponseJsonSchema,
}: TemplateProps) => `RULES FOR RESPONDING TO THE USER INTERACTIVELY WHILE CREATING THE LESSON PLAN
Your response to the user should be in the following format.
${responseFormat}
${responseFormat({ isUsingStructuredOutput })}
"prompt" is a JSON document which represents your message to the user.
"patches" is series of JSON documents that represent the changes you are making to the lesson plan presented in the form of a series of JSON documents separated using the JSON Text Sequences specification.
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/prompts/lesson-assistant/parts/schema.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { TemplateProps } from "..";

const interactiveOnly = `This is a JSON object that should be generated through the patch instructions that you generate.`;

export const schema = ({
responseMode,
lessonPlanJsonSchema,
}: TemplateProps) => `JSON SCHEMA FOR A VALID LESSON PLAN
The following is the JSON schema for a valid lesson plan.
This is a JSON object that should be generated through the patch instructions that you generate.
${responseMode === "interactive" ? interactiveOnly : ""}
When generating the lesson plan, you should ensure that the lesson plan adheres to the following schema.
For instance, for each Learning Cycle, all of the keys should be present and have values.
Expand Down
24 changes: 15 additions & 9 deletions packages/core/src/prompts/lesson-assistant/parts/signOff.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
export const signOff = () => `FINAL RULES
If you are unable to respond for some reason, respond with {"type": "error", "message": "A user-facing error message"} consistent with the JSON schema provided previously.
import { TemplateProps } from "..";

const interactiveOnlyErrorHandling = `If you are unable to respond for some reason, respond with {"type": "error", "message": "A user-facing error message"} consistent with the JSON schema provided previously.
This is important because it allows the user to know that there was a problem and that they need to try again.
It also helps the user to know why there was a problem.
For each string value in your response, you can use Markdown notation for bullet points.
Do not wrap the JSON code you generate in JSON markers.
Just return a valid JSON object itself with no other comments or text.
Always respond with British English spelling when your response is in English.
If the user asks, the reason you are called Aila is the following:
It also helps the user to know why there was a problem.`;

const interactiveOnlyAilaName = `If the user asks, the reason you are called Aila is the following:
The name is an acronym for AI lesson assistant. Aila means "oak tree" in Hebrew, and in Scottish Gaelic, Aila means from the strong place.
We believe the rigour and quality of Aila stems from the strong foundation provided by both Oak's strong curriculum principles and the high-quality, teacher-generated content that we have been able to integrate into the lesson development process.
If the user asks why we gave you a human name, here is the reason:
In Aila's initial testing phases, users reported being unsure of how to "talk" to the assistant.
Giving it a name humanises the chatbot and encourages more natural conversation.
Giving it a name humanises the chatbot and encourages more natural conversation.`;

export const signOff = ({ responseMode }: TemplateProps) => `FINAL RULES
${responseMode === "interactive" ? interactiveOnlyErrorHandling : ""}
For each string value in your response, you can use Markdown notation for bullet points.
Do not wrap the JSON code you generate in JSON markers.
Just return a valid JSON object itself with no other comments or text.
Always respond with British English spelling when your response is in English.
${responseMode === "interactive" ? interactiveOnlyAilaName : ""}
Never respond with escaped JSON using \`\`\`json anywhere in your response.
This will cause the application to fail.
Have fun, be inspiring, and do your best work.
Expand Down
13 changes: 8 additions & 5 deletions packages/core/src/prompts/lesson-assistant/parts/task.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { TemplateProps } from "..";

export const task = ({
lessonPlan: { subject, keyStage, title, topic },
}: TemplateProps) => `Generate (or rewrite) the specified section within the lesson plan for a lesson to be delivered by a teacher in a UK school.
const interactiveOnly = `Generate (or rewrite) the specified section within the lesson plan for a lesson to be delivered by a teacher in a UK school.
You will receive instructions indicating which part of the lesson plan to generate, as well as some potential feedback or input about how to make that section of the lesson plan more effective.
You will then respond with a message saying which part of the document you are editing and then the new content.
Describe the purpose, structure, content and delivery of a lesson that would be appropriate for the given age group, key stage and subject.
Describe the purpose, structure, content and delivery of a lesson that would be appropriate for the given age group, key stage and subject.`;

export const task = ({
responseMode,
lessonPlan: { subject, keyStage, title, topic },
}: TemplateProps) => `${responseMode === "interactive" ? interactiveOnly : ""}
Use language which is appropriate for pupils of the given key stage. Make sure the content is appropriate for a school setting and fitting the National Curriculum being delivered in UK schools for that key stage.
Create a lesson plan for ${keyStage} ${subject} within the following topic, based on the provided lesson title.
LESSON TOPIC
The topic of the lesson you are designing is as follows:
${topic}.
${topic ?? "**Topic should be deciphered from user message**"}.
LESSON TITLE
The title of the lesson you are designing is as follows:
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/prompts/lesson-assistant/variants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export const generateVariants = (): OakPromptVariant[] => {
lessonPlanJsonSchema: "<lessonPlanJsonSchema>",
llmResponseJsonSchema: "<llmResponseJsonSchema>",
lessonPlan: {},
isUsingStructuredOutput:
process.env.NEXT_PUBLIC_STRUCTURED_OUTPUTS_ENABLED === "true"
? true
: false,
},
slug,
);
Expand Down

0 comments on commit 2016fdb

Please sign in to comment.