From 1bdf99a5ce8be819107e36295accf67aed6e4d25 Mon Sep 17 00:00:00 2001 From: MG Date: Wed, 16 Oct 2024 16:56:49 +0100 Subject: [PATCH 001/127] fix: ingest config migration fix (#243) --- .../20241016143309_ingest_config_fix/migration.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 packages/db/prisma/migrations/20241016143309_ingest_config_fix/migration.sql diff --git a/packages/db/prisma/migrations/20241016143309_ingest_config_fix/migration.sql b/packages/db/prisma/migrations/20241016143309_ingest_config_fix/migration.sql new file mode 100644 index 000000000..b19b848e4 --- /dev/null +++ b/packages/db/prisma/migrations/20241016143309_ingest_config_fix/migration.sql @@ -0,0 +1,7 @@ +-- Remove the mistakenly added column from the ingest_lesson table +ALTER TABLE "ingest"."ingest_lesson" +DROP COLUMN IF EXISTS config; + +-- Add the config column to the correct ingest table +ALTER TABLE "ingest"."ingest" +ADD COLUMN config JSONB NOT NULL; From 73ffd2993a215abfa9cdd1c0706dbf7106f9e006 Mon Sep 17 00:00:00 2001 From: MG Date: Wed, 16 Oct 2024 16:57:22 +0100 Subject: [PATCH 002/127] feat: relevant lessons [AI-607] (#238) --- packages/aila/src/core/AilaServices.ts | 4 +- packages/aila/src/core/chat/AilaChat.ts | 31 ++++++----- .../builders/AilaLessonPromptBuilder.ts | 51 +++++++++++++++---- .../features/persistence/AilaPersistence.ts | 19 +++---- packages/aila/src/protocol/schema.ts | 10 ++++ .../minifyLessonPlanForRelevantLessons.ts | 4 +- .../aila/src/utils/rag/fetchRagContent.ts | 23 ++++----- 7 files changed, 91 insertions(+), 51 deletions(-) diff --git a/packages/aila/src/core/AilaServices.ts b/packages/aila/src/core/AilaServices.ts index 4b5712edb..185905119 100644 --- a/packages/aila/src/core/AilaServices.ts +++ b/packages/aila/src/core/AilaServices.ts @@ -7,7 +7,7 @@ import { AilaThreatDetectionFeature, } from "../features/types"; import { MessagePart } from "../protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "../protocol/schema"; +import { AilaRagRelevantLesson, LooseLessonPlan } from "../protocol/schema"; import { Message } from "./chat"; import { AilaOptionsWithDefaultFallbackValues } from "./index"; import { AilaPlugin } from "./plugins"; @@ -31,6 +31,8 @@ export interface AilaChatService { readonly userId: string | undefined; readonly id: string; readonly messages: Message[]; + get relevantLessons(): AilaRagRelevantLesson[]; + set relevantLessons(lessons: AilaRagRelevantLesson[]); readonly parsedMessages: MessagePart[][]; readonly isShared: boolean | undefined; loadChat({ store }: { store: string }): Promise; diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 659399586..7c49b6ace 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -19,6 +19,7 @@ import { TextDocumentSchema, parseMessageParts, } from "../../protocol/jsonPatchProtocol"; +import { AilaRagRelevantLesson } from "../../protocol/schema"; import { LLMService } from "../llm/LLMService"; import { OpenAIService } from "../llm/OpenAIService"; import { AilaPromptBuilder } from "../prompt/AilaPromptBuilder"; @@ -28,16 +29,17 @@ import { PatchEnqueuer } from "./PatchEnqueuer"; import { Message } from "./types"; export class AilaChat implements AilaChatService { - private _id: string; - private _messages: Message[]; + private readonly _id: string; + private readonly _messages: Message[]; + private _relevantLessons: AilaRagRelevantLesson[]; private _isShared: boolean | undefined; - private _userId: string | undefined; - private _aila: AilaServices; + private readonly _userId: string | undefined; + private readonly _aila: AilaServices; private _generation?: AilaGeneration; private _chunks?: string[]; - private _patchEnqueuer: PatchEnqueuer; - private _llmService: LLMService; - private _promptBuilder: AilaPromptBuilder; + private readonly _patchEnqueuer: PatchEnqueuer; + private readonly _llmService: LLMService; + private readonly _promptBuilder: AilaPromptBuilder; constructor({ id, @@ -66,6 +68,7 @@ export class AilaChat implements AilaChatService { }); this._patchEnqueuer = new PatchEnqueuer(); this._promptBuilder = promptBuilder ?? new AilaLessonPromptBuilder(aila); + this._relevantLessons = []; } public get aila() { @@ -76,11 +79,6 @@ export class AilaChat implements AilaChatService { return this._id; } - public set id(value: string) { - this._id = value; - this._aila.analytics?.initialiseAnalyticsContext(); - } - public get userId(): string | undefined { return this._userId; } @@ -97,6 +95,14 @@ export class AilaChat implements AilaChatService { return this._messages.map((m) => parseMessageParts(m.content)); } + public get relevantLessons() { + return this._relevantLessons; + } + + public set relevantLessons(lessons: AilaRagRelevantLesson[]) { + this._relevantLessons = lessons; + } + public getPatchEnqueuer(): PatchEnqueuer { return this._patchEnqueuer; } @@ -279,6 +285,7 @@ export class AilaChat implements AilaChatService { const persistedChat = await persistenceFeature.loadChat(); if (persistedChat) { + this._relevantLessons = persistedChat.relevantLessons ?? []; this._isShared = persistedChat.isShared; } } diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index 8636b27ad..9fca4bf42 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -14,7 +14,10 @@ import { import { findAmericanisms } from "../../../utils/language/findAmericanisms"; import { compressedLessonPlanForRag } from "../../../utils/lessonPlan/compressedLessonPlanForRag"; import { fetchLessonPlan } from "../../../utils/lessonPlan/fetchLessonPlan"; -import { fetchRagContent } from "../../../utils/rag/fetchRagContent"; +import { + fetchRagContent, + RagLessonPlan, +} from "../../../utils/rag/fetchRagContent"; import { AilaServices } from "../../AilaServices"; import { AilaPromptBuilder } from "../AilaPromptBuilder"; @@ -25,8 +28,18 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { public async build(): Promise { const relevantLessonPlans = await this.fetchRelevantLessonPlans(); + this._aila.chat.relevantLessons = relevantLessonPlans.ragLessonPlans.map( + (lesson) => ({ + lessonPlanId: lesson.id, + title: lesson.title, + }), + ); + const baseLessonPlan = await this.fetchBaseLessonPlan(); - return this.systemPrompt(relevantLessonPlans, baseLessonPlan); + return this.systemPrompt( + relevantLessonPlans.stringifiedRelevantLessonPlans, + baseLessonPlan, + ); } private async fetchBaseLessonPlan(): Promise { @@ -40,18 +53,22 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { } } - private async fetchRelevantLessonPlans(): Promise { + private async fetchRelevantLessonPlans(): Promise<{ + ragLessonPlans: RagLessonPlan[]; + stringifiedRelevantLessonPlans: string; + }> { const noRelevantLessonPlans = "None"; const { chatId, userId } = this._aila; - if (!this._aila?.options.useRag) { - return noRelevantLessonPlans; - } - if (!chatId) { - return noRelevantLessonPlans; + if (!this._aila?.options.useRag || !chatId) { + return { + ragLessonPlans: [], + stringifiedRelevantLessonPlans: noRelevantLessonPlans, + }; } + const { title, subject, keyStage, topic } = this._aila?.lessonPlan ?? {}; - let relevantLessonPlans = noRelevantLessonPlans; + let relevantLessonPlans: RagLessonPlan[] = []; await tryWithErrorReporting(async () => { relevantLessonPlans = await fetchRagContent({ title: title ?? "unknown", @@ -69,7 +86,21 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { }, "Did not fetch RAG content. Continuing"); console.log("Fetched relevant lesson plans", relevantLessonPlans.length); - return relevantLessonPlans; + const stringifiedRelevantLessonPlans = JSON.stringify( + relevantLessonPlans, + null, + 2, + ); + + console.log( + "Got RAG content, length:", + stringifiedRelevantLessonPlans.length, + ); + + return { + ragLessonPlans: relevantLessonPlans, + stringifiedRelevantLessonPlans, + }; } private systemPrompt( diff --git a/packages/aila/src/features/persistence/AilaPersistence.ts b/packages/aila/src/features/persistence/AilaPersistence.ts index 9fa355c39..bb9994b3f 100644 --- a/packages/aila/src/features/persistence/AilaPersistence.ts +++ b/packages/aila/src/features/persistence/AilaPersistence.ts @@ -1,9 +1,9 @@ import { GenerationStatus } from "@prisma/client"; import invariant from "tiny-invariant"; -import { AilaChatService, AilaError, AilaServices, Message } from "../../core"; +import { AilaChatService, AilaError, AilaServices } from "../../core"; import { AilaOptionsWithDefaultFallbackValues } from "../../core/types"; -import { AilaPersistedChat, LooseLessonPlan } from "../../protocol/schema"; +import { AilaPersistedChat } from "../../protocol/schema"; import { AilaGeneration } from "../generation"; export abstract class AilaPersistence { @@ -34,7 +34,7 @@ export abstract class AilaPersistence { } protected createChatPayload(): ChatPersistencePayload { - const { id, userId, messages, isShared } = this._chat; + const { id, userId, messages, isShared, relevantLessons } = this._chat; invariant(userId, "userId is required for chat persistence"); @@ -52,6 +52,7 @@ export abstract class AilaPersistence { isShared, path: `/aila/${id}`, lessonPlan: lesson.plan, + relevantLessons, messages: messages.filter((m) => ["assistant", "user"].includes(m.role)), options, }; @@ -114,20 +115,12 @@ export abstract class AilaPersistence { abstract upsertGeneration(generation?: AilaGeneration): Promise; } -export interface ChatPersistencePayload { - id?: string; - userId: string; - title: string; +export type ChatPersistencePayload = AilaPersistedChat & { subject: string; keyStage: string; topic: string; - createdAt: number; - isShared: boolean | undefined; - path: string; - lessonPlan: LooseLessonPlan; - messages: Message[]; options: AilaOptionsWithDefaultFallbackValues; -} +}; export interface GenerationPersistencePayload { id?: string; diff --git a/packages/aila/src/protocol/schema.ts b/packages/aila/src/protocol/schema.ts index 18b0bbf36..390842fff 100644 --- a/packages/aila/src/protocol/schema.ts +++ b/packages/aila/src/protocol/schema.ts @@ -573,6 +573,15 @@ export const LessonPlanJsonSchema = zodToJsonSchema( "lessonPlanSchema", ); +const AilaRagRelevantLessonSchema = z.object({ + // @todo add this after next ingest + // oakLessonId: z.number(), + lessonPlanId: z.string(), + title: z.string(), +}); + +export type AilaRagRelevantLesson = z.infer; + export const chatSchema = z .object({ id: z.string(), @@ -580,6 +589,7 @@ export const chatSchema = z title: z.string(), userId: z.string(), lessonPlan: LessonPlanSchemaWhilstStreaming, + relevantLessons: z.array(AilaRagRelevantLessonSchema).optional(), isShared: z.boolean().optional(), createdAt: z.union([z.date(), z.number()]), startingMessage: z.string().optional(), diff --git a/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts b/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts index b95ef238e..deed91501 100644 --- a/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts +++ b/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts @@ -1,6 +1,6 @@ import { LessonPlan as DatabaseLessonPlan } from "@oakai/db"; -import { LooseLessonPlan } from "../../protocol/schema"; +import { CompletedLessonPlan } from "../../protocol/schema"; export function minifyLessonPlanForRelevantLessons( lessonPlan: DatabaseLessonPlan, @@ -8,6 +8,6 @@ export function minifyLessonPlanForRelevantLessons( const { id } = lessonPlan; // eslint-disable-next-line @typescript-eslint/no-unused-vars const { exitQuiz, starterQuiz, ...rest } = - lessonPlan.content as LooseLessonPlan; + lessonPlan.content as CompletedLessonPlan; // @todo, after next ingest, start parsing the content properly return { id, ...rest }; } diff --git a/packages/aila/src/utils/rag/fetchRagContent.ts b/packages/aila/src/utils/rag/fetchRagContent.ts index 1c66a6c7a..f13be709b 100644 --- a/packages/aila/src/utils/rag/fetchRagContent.ts +++ b/packages/aila/src/utils/rag/fetchRagContent.ts @@ -2,8 +2,16 @@ import { RAG } from "@oakai/core/src/rag"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; +import { CompletedLessonPlan } from "../../protocol/schema"; import { minifyLessonPlanForRelevantLessons } from "../lessonPlan/minifyLessonPlanForRelevantLessons"; +export type RagLessonPlan = Omit< + CompletedLessonPlan, + "starterQuiz" | "exitQuiz" +> & { + id: string; +}; + export async function fetchRagContent({ title, subject, @@ -24,9 +32,7 @@ export async function fetchRagContent({ prisma: PrismaClientWithAccelerate; chatId: string; userId?: string; -}) { - let content = "[]"; - +}): Promise { const rag = new RAG(prisma, { chatId, userId }); const ragLessonPlans = await tryWithErrorReporting( () => { @@ -45,14 +51,5 @@ export async function fetchRagContent({ "info", ); - if (ragLessonPlans) { - const minifiedLessons = ragLessonPlans.map((l) => { - return minifyLessonPlanForRelevantLessons(l); - }); - content = JSON.stringify(minifiedLessons, null, 2); - } - - console.log("Got RAG content, length:", content.length); - - return content; + return ragLessonPlans?.map(minifyLessonPlanForRelevantLessons) ?? []; } From 6bd8c40b5b40a56dfb94ae3aace86efbeb77e50c Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Fri, 18 Oct 2024 12:41:44 +0100 Subject: [PATCH 003/127] feat: add dev-turbo mode to use turbopack (#248) --- .vscode/settings.json | 1 + apps/nextjs/next.config.js | 24 +++++++++ apps/nextjs/package.json | 3 +- apps/nextjs/scripts/increase-listeners.js | 18 +++++++ apps/nextjs/src/middleware.test.ts | 11 ---- .../__snapshots__/csp.test.ts.snap | 6 +-- apps/nextjs/src/middlewares/csp.test.ts | 52 +++---------------- apps/nextjs/src/middlewares/csp.ts | 13 ++--- apps/nextjs/tsconfig.test.json | 3 +- package.json | 1 + .../src/utils/language/findAmericanisms.ts | 3 +- packages/aila/tsconfig.json | 6 ++- packages/aila/tsconfig.test.json | 7 ++- turbo.json | 4 ++ 14 files changed, 78 insertions(+), 74 deletions(-) create mode 100644 apps/nextjs/scripts/increase-listeners.js diff --git a/.vscode/settings.json b/.vscode/settings.json index bee03c4b1..f21612b29 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -134,6 +134,7 @@ "transpiles", "trivago", "trpc", + "Turbopack", "turborepo", "uidotdev", "unjudged", diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index d966e5b36..6c17525b0 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -4,6 +4,7 @@ const { RELEASE_STAGE_PRODUCTION, RELEASE_STAGE_TESTING, } = require("./scripts/build_config_helpers.js"); +const path = require("path"); const { PHASE_PRODUCTION_BUILD, PHASE_TEST } = require("next/constants"); @@ -54,6 +55,29 @@ const getConfig = async (phase) => { * @see https://docs.sentry.io/platforms/javascript/guides/nextjs/migration/v7-to-v8/#updated-sdk-initialization */ instrumentationHook: true, + serverComponentsExternalPackages: [`require-in-the-middle`], + turbo: { + resolveAlias: { + "#next/navigation": { + storybook: path.join( + __dirname, + "src", + "mocks", + "next", + "navigation", + ), + default: "next/navigation", + }, + "#oakai/db": { + storybook: path.join(__dirname, "src", "mocks", "oakai", "db"), + default: "@oakai/db", + }, + "#clerk/nextjs": { + storybook: path.join(__dirname, "src", "mocks", "clerk", "nextjs"), + default: "@clerk/nextjs", + }, + }, + }, }, reactStrictMode: true, swcMinify: true, diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index bdad3d7f5..0bbbbcdc7 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -8,7 +8,8 @@ "check": "tsc --noEmit", "clean": "rm -rf .next .turbo node_modules", "dev": "concurrently \"pnpm dev-server\" \"node scripts/local-dev.mjs\"", - "dev-server": "pnpm with-env next dev --port 2525 | pino-pretty -C", + "dev-turbo": "pnpm with-env node scripts/increase-listeners.js next dev --port 2525 --turbo | pino-pretty -C", + "dev-server": "pnpm with-env node scripts/increase-listeners.js next dev --port 2525 | pino-pretty -C", "dev-trace-deprecation": "NODE_OPTIONS=\"--trace-deprecation\" next dev --port 2525 | pino-pretty -C", "lint": "next lint", "lint-fix": "next lint --fix", diff --git a/apps/nextjs/scripts/increase-listeners.js b/apps/nextjs/scripts/increase-listeners.js new file mode 100644 index 000000000..7d9e0c25a --- /dev/null +++ b/apps/nextjs/scripts/increase-listeners.js @@ -0,0 +1,18 @@ +/**** + Because we have a reasonably complex Next.js project now, + we're sometimes running into the default max listeners limit. + This script increases the limit to 20, which should be enough + so that we don't run into this issue. + + Potentially, if we decide to move to Turbopack for compilation + in local development, we could remove this script. + +***/ + +// Increase the limit of max listeners +require("events").EventEmitter.defaultMaxListeners = 20; + +// Run the original command +require("child_process").spawn(process.argv[2], process.argv.slice(3), { + stdio: "inherit", +}); diff --git a/apps/nextjs/src/middleware.test.ts b/apps/nextjs/src/middleware.test.ts index a60f94b39..03b57b45f 100644 --- a/apps/nextjs/src/middleware.test.ts +++ b/apps/nextjs/src/middleware.test.ts @@ -3,14 +3,6 @@ import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; import { handleError } from "./middleware"; import { CspConfig, addCspHeaders, buildCspHeaders } from "./middlewares/csp"; -jest.mock("./middlewares/csp", () => { - const originalModule = jest.requireActual("./middlewares/csp"); - return { - ...originalModule, - generateNonce: jest.fn(() => "mocked-nonce"), - }; -}); - describe("handleError", () => { let mockRequest: NextRequest; let mockEvent: NextFetchEvent; @@ -194,9 +186,6 @@ describe("addCspHeaders", () => { expect(cspHeader).toContain("*.hubspot.com"); expect(cspHeader).toContain("https://img.clerk.com"); expect(cspHeader).toContain("https://res.cloudinary.com"); - - // Check for nonce - expect(cspHeader).toMatch(/'nonce-[a-zA-Z0-9+/=]{24}'/); }); it("includes Clerk policies when enabled", () => { diff --git a/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap b/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap index f94f7ab1d..af91c803f 100644 --- a/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap +++ b/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap @@ -3,7 +3,7 @@ exports[`CSP Policies Snapshot should match development environment snapshot 1`] = ` "default-src 'self' media-src 'self' 'self' https://*.mux.com https://stream.mux.com blob: -script-src 'self' 'nonce-AAAAAAAAAAAAAAAAAAAAAA==' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com +script-src 'self' 'nonce-REPLACED_NONCE' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com style-src 'self' 'unsafe-inline' https://*.mux.com connect-src 'self' *.thenational.academy *.hubspot.com *.clerk.accounts.dev https://api.avo.app/ https://eu.i.posthog.com https://europe-west2-oak-ai-beta-staging.cloudfunctions.net https://mux.com https://*.mux.com https://stream.mux.com https://inferred.litix.io worker-src 'self' blob: @@ -20,7 +20,7 @@ report-uri https://sentry.io/report&sentry_environment=test&sentry_release=1.0.0 exports[`CSP Policies Snapshot should match preview environment snapshot 1`] = ` "default-src 'self' media-src 'self' 'self' https://*.mux.com https://stream.mux.com blob: -script-src 'self' 'nonce-AAAAAAAAAAAAAAAAAAAAAA==' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://vercel.live https://vercel.com https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com +script-src 'self' 'nonce-REPLACED_NONCE' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://vercel.live https://vercel.com https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com style-src 'self' 'unsafe-inline' https://vercel.live/ https://*.mux.com connect-src 'self' *.thenational.academy *.hubspot.com https://vercel.live/ https://vercel.com *.pusher.com *.pusherapp.com *.clerk.accounts.dev https://api.avo.app/ https://mux.com https://*.mux.com https://stream.mux.com https://inferred.litix.io worker-src 'self' blob: @@ -38,7 +38,7 @@ upgrade-insecure-requests" exports[`CSP Policies Snapshot should match production environment snapshot 1`] = ` "default-src 'self' media-src 'self' 'self' https://*.mux.com https://stream.mux.com blob: -script-src 'self' 'nonce-AAAAAAAAAAAAAAAAAAAAAA==' 'strict-dynamic' https: http: 'unsafe-inline' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com +script-src 'self' 'nonce-REPLACED_NONCE' 'strict-dynamic' https: http: 'unsafe-inline' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com style-src 'self' 'unsafe-inline' https://*.mux.com connect-src 'self' *.thenational.academy *.hubspot.com https://mux.com https://*.mux.com https://stream.mux.com https://inferred.litix.io worker-src 'self' blob: diff --git a/apps/nextjs/src/middlewares/csp.test.ts b/apps/nextjs/src/middlewares/csp.test.ts index a6afe104c..4877c2619 100644 --- a/apps/nextjs/src/middlewares/csp.test.ts +++ b/apps/nextjs/src/middlewares/csp.test.ts @@ -2,43 +2,6 @@ import { NextRequest } from "next/server"; import { addCspHeaders, CspConfig } from "./csp"; -const mockedCrypto = { - randomBytes: jest.fn(() => ({ - toString: jest.fn(() => "mocked-nonce"), - })), -}; - -// Mock the global require function -const originalRequire = jest.requireActual("module"); -// eslint-disable-next-line @typescript-eslint/no-explicit-any -(global as any).require = Object.assign( - jest.fn((moduleName: string) => { - if (moduleName === "crypto") { - return mockedCrypto; - } - return originalRequire(moduleName); - }), - { - resolve: originalRequire.resolve, - cache: originalRequire.cache, - extensions: originalRequire.extensions, - main: originalRequire.main, - }, -); - -// Mock the global crypto object -// eslint-disable-next-line @typescript-eslint/no-explicit-any -(global as any).crypto = { - getRandomValues: jest.fn((array) => { - for (let i = 0; i < array.length; i++) { - array[i] = i % 256; // Fill with predictable values - } - return array; - }), - subtle: {}, - randomUUID: jest.fn(() => "mocked-uuid"), -}; - const environments = ["development", "production", "preview"] as const; type Environment = (typeof environments)[number]; @@ -66,7 +29,13 @@ function generatePoliciesForEnvironment(env: Environment): string { const result = addCspHeaders(mockResponse, mockRequest, config); const cspHeader = result.headers.get("Content-Security-Policy") || ""; - return cspHeader + + const sanitizedCspHeader = cspHeader.replace( + /'nonce-[^']+'/g, + "'nonce-REPLACED_NONCE'", + ); + + return sanitizedCspHeader .split(";") .map((policy) => policy.trim()) .join("\n"); @@ -77,13 +46,6 @@ describe("CSP Policies Snapshot", () => { jest.clearAllMocks(); }); - afterAll(() => { - jest.restoreAllMocks(); - // Restore the original require function - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (global as any).require = originalRequire; - }); - environments.forEach((env) => { it(`should match ${env} environment snapshot`, () => { const generatedPolicies = generatePoliciesForEnvironment(env); diff --git a/apps/nextjs/src/middlewares/csp.ts b/apps/nextjs/src/middlewares/csp.ts index 477161660..2073f5c5a 100644 --- a/apps/nextjs/src/middlewares/csp.ts +++ b/apps/nextjs/src/middlewares/csp.ts @@ -1,21 +1,14 @@ import { NextRequest, NextResponse } from "next/server"; +import { v4 as uuidv4 } from "uuid"; function generateNonce(): string { if (typeof crypto !== "undefined" && crypto.getRandomValues) { const array = new Uint8Array(16); crypto.getRandomValues(array); return btoa(String.fromCharCode.apply(null, array as unknown as number[])); - } else if (typeof require !== "undefined") { - // We are running this in a test Node.js environment - // for testing purposes - // eslint-disable-next-line @typescript-eslint/no-var-requires - const crypto = require("crypto"); - return crypto.randomBytes(16).toString("base64"); } else { - // Fallback for environments where neither is available - throw new Error( - "Unable to generate nonce: No secure random number generator available", - ); + // Use uuid library to generate a random value + return uuidv4(); } } diff --git a/apps/nextjs/tsconfig.test.json b/apps/nextjs/tsconfig.test.json index e0b045baf..6610850b2 100644 --- a/apps/nextjs/tsconfig.test.json +++ b/apps/nextjs/tsconfig.test.json @@ -34,7 +34,8 @@ "tests/**/*.ts", "**/*.test.ts", "**/*.test.tsx", - "jest.static.d.ts" + "jest.static.d.ts", + "src/**/*.d.ts" ], "exclude": ["node_modules"] } diff --git a/package.json b/package.json index bd10044b1..b7fc5a405 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "db-push-force": "turbo db-push-force", "db-seed": "turbo db-seed", "dev": "FORCE_COLOR=1 turbo dev --parallel --filter=!db", + "dev-turbo": "FORCE_COLOR=1 turbo dev-turbo --parallel --filter=!db", "doppler:pull:dev": "doppler secrets download --config dev --no-file --format env > .env", "doppler:pull:stg": "doppler secrets download --config stg --no-file --format env > .env", "doppler:run:stg": "doppler run -c stg --silent", diff --git a/packages/aila/src/utils/language/findAmericanisms.ts b/packages/aila/src/utils/language/findAmericanisms.ts index 8740ea5e2..40418f849 100644 --- a/packages/aila/src/utils/language/findAmericanisms.ts +++ b/packages/aila/src/utils/language/findAmericanisms.ts @@ -1,8 +1,9 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// import { textify } from "@oakai/core/src/models/lessonPlans"; import translator from "american-british-english-translator"; import { LessonPlanKeys, LooseLessonPlan } from "../../protocol/schema"; -import "./american-british-english-translator.d.ts"; export type AmericanismIssueBySection = { section: string; diff --git a/packages/aila/tsconfig.json b/packages/aila/tsconfig.json index 99187cadf..324f100bc 100644 --- a/packages/aila/tsconfig.json +++ b/packages/aila/tsconfig.json @@ -13,7 +13,11 @@ "moduleResolution": "bundler", "esModuleInterop": true, "types": ["jest"], - "typeRoots": ["./../../node_modules/@types", "./src/types"], + "typeRoots": [ + "./../../node_modules/@types", + "./src/types", + "./src/utils/language/*.d.ts" + ], "paths": { "@/lib/*": ["./src/lib/*"], "@/utils/*": ["./src/utils/*"] diff --git a/packages/aila/tsconfig.test.json b/packages/aila/tsconfig.test.json index d57e36d68..f588d0ca2 100644 --- a/packages/aila/tsconfig.test.json +++ b/packages/aila/tsconfig.test.json @@ -14,10 +14,15 @@ "isolatedModules": true, "noEmit": true, "types": ["jest", "node", "@testing-library/jest-dom"], + "typeRoots": [ + "./../../node_modules/@types", + "./src/types", + "./src/utils/language" + ], "paths": { "@/*": ["./src/*"] } }, - "include": ["src/**/*.ts", "tests/**/*.ts", "**/*.test.ts"], + "include": ["src/**/*.ts", "tests/**/*.ts", "**/*.test.ts", "src/**/*.d.ts"], "exclude": ["node_modules"] } diff --git a/turbo.json b/turbo.json index 6de2c66ff..acd13bb56 100644 --- a/turbo.json +++ b/turbo.json @@ -17,6 +17,10 @@ "dependsOn": ["^db-generate"], "cache": false }, + "dev-turbo": { + "dependsOn": ["^db-generate"], + "cache": false + }, "build": { "cache": true, "dependsOn": ["^build", "^db-generate"], From 075c54caac1e3b156c0c81de80eafe45ac778ff6 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:10:59 +0200 Subject: [PATCH 004/127] test: check streaming status directly (#249) --- .../AppComponents/Chat/chat-lhs-header.tsx | 11 +++++++++++ apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts | 16 +++++++++++++--- apps/nextjs/tests-e2e/tests/banned-users.test.ts | 6 ++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index fb5b9ff82..6ec4b22a8 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -2,6 +2,8 @@ import React from "react"; import { useRouter } from "#next/navigation"; +import { useLessonChat } from "@/components/ContextProviders/ChatProvider"; + import AiIcon from "../../AiIcon"; import ChatButton from "./ui/chat-button"; @@ -17,10 +19,19 @@ const ChatLhsHeader = ({ isDemoUser, }: Readonly) => { const router = useRouter(); + const chat = useLessonChat(); return ( <>
+ {process.env.NEXT_PUBLIC_ENVIRONMENT !== "production" && ( +
+ {chat.ailaStreamingStatus} +
+ )} { diff --git a/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts b/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts index a4f2b2b40..563adb244 100644 --- a/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts +++ b/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts @@ -1,10 +1,20 @@ import { expect, Page, test } from "@playwright/test"; +import { AilaStreamingStatus } from "@/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; + +export async function expectStreamingStatus( + page: Page, + status: AilaStreamingStatus, + args?: { timeout: number }, +) { + const statusElement = page.getByTestId("chat-aila-streaming-status"); + await expect(statusElement).toHaveText(status, args); +} + export async function waitForGeneration(page: Page, generationTimeout: number) { return await test.step("Wait for generation", async () => { - const loadingElement = page.getByTestId("chat-stop"); - await expect(loadingElement).toBeVisible({ timeout: 10000 }); - await expect(loadingElement).not.toBeVisible({ + await expectStreamingStatus(page, "RequestMade"); + await expectStreamingStatus(page, "Idle", { timeout: generationTimeout, }); }); diff --git a/apps/nextjs/tests-e2e/tests/banned-users.test.ts b/apps/nextjs/tests-e2e/tests/banned-users.test.ts index aefebe583..e37e697c1 100644 --- a/apps/nextjs/tests-e2e/tests/banned-users.test.ts +++ b/apps/nextjs/tests-e2e/tests/banned-users.test.ts @@ -3,7 +3,7 @@ import { test, expect } from "@playwright/test"; import { TEST_BASE_URL } from "../config/config"; import { prepareUser } from "../helpers/auth"; import { bypassVercelProtection } from "../helpers/vercel"; -import { applyLlmFixtures } from "./aila-chat/helpers"; +import { applyLlmFixtures, expectStreamingStatus } from "./aila-chat/helpers"; const TOXIC_TAG = "mod:tox"; @@ -36,9 +36,7 @@ test("Users are banned after 3 toxic lessons", async ({ page }) => { await test.step("Wait for streaming", async () => { await page.waitForURL(/\/aila\/.+/); - - const loadingElement = page.getByTestId("chat-stop"); - await expect(loadingElement).toBeVisible({ timeout: 10000 }); + await expectStreamingStatus(page, "RequestMade", { timeout: 10000 }); }); await test.step("Check account locked", async () => { From 762453904bf6d9e9571f1daaf6df9b2dd6d3eb80 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:15:24 +0200 Subject: [PATCH 005/127] chore: centralised, filterable logging (#244) --- apps/nextjs/jest.setup.js | 2 - apps/nextjs/next.config.js | 7 +- .../ai-apps/common/parseLocalStorageData.ts | 10 +- .../src/app/admin/aila/[chatId]/page.tsx | 5 +- apps/nextjs/src/app/api/chat/chatHandler.ts | 5 +- .../chat/fixtures/FixtureRecordLLMService.ts | 9 +- .../fixtures/FixtureRecordOpenAiClient.ts | 5 +- .../chat/fixtures/FixtureReplayLLMService.ts | 5 +- .../fixtures/FixtureReplayOpenAiClient.ts | 5 +- .../nextjs/src/app/api/chat/fixtures/index.ts | 12 ++- .../src/app/api/chat/webActionsPlugin.ts | 5 +- .../src/app/api/trpc/chat/[trpc]/route.ts | 5 +- .../src/app/api/trpc/main/[trpc]/route.ts | 5 +- .../app/api/trpc/test-support/[trpc]/route.ts | 5 +- .../lesson-planner/preview/[slug]/page.tsx | 5 +- .../app/quiz-designer/preview/[slug]/page.tsx | 5 +- .../app/quiz-designer/quiz-designer-page.tsx | 9 +- .../[[...sign-in]]/DetectStuckBannedUser.tsx | 5 +- .../Chat/Chat/hooks/useAilaStreamingStatus.ts | 5 +- .../AppComponents/Chat/Chat/utils/index.ts | 9 +- .../AppComponents/Chat/chat-message/index.tsx | 5 +- .../AppComponents/Chat/chat-share-dialog.tsx | 5 +- .../AppComponents/Chat/chat-start.tsx | 7 +- .../Chat/drop-down-section/modify-button.tsx | 5 +- .../JudgementFeedbackDialog.tsx | 10 +- .../GenerationFeedbackDialog.tsx | 2 +- .../download/DownloadAllButton.tsx | 7 +- .../ContextProviders/ChatProvider.tsx | 11 +- .../ContentOptions/DemoInterstitialDialog.tsx | 5 +- .../ContentOptions/ReportContentDialog.tsx | 5 +- .../useExportAdditionalMaterials.ts | 5 +- .../useExportAllLessonAssets.ts | 5 +- .../ExportsDialogs/useExportLessonPlanDoc.ts | 5 +- .../ExportsDialogs/useExportLessonSlides.ts | 5 +- .../useExportQuizDesignerSlides.ts | 6 +- .../ExportsDialogs/useExportQuizDoc.ts | 5 +- .../useExportWorksheetSlides.ts | 5 +- .../components/ImageAltGenerator/ImageRow.tsx | 7 +- .../hooks/surveys/usePosthogFeedbackSurvey.ts | 9 +- apps/nextjs/src/hooks/useCopyToClipboard.ts | 8 +- apps/nextjs/src/hooks/useGeneration.ts | 5 +- .../src/hooks/useQuestionsForJudgement.ts | 15 +-- .../src/lib/cookie-consent/consentClient.ts | 7 +- .../nextjs/src/lib/hooks/use-enter-submit.tsx | 6 +- .../nextjs/src/middlewares/auth.middleware.ts | 10 +- .../src/middlewares/middlewareErrorLogging.ts | 3 +- apps/nextjs/src/mocks/analytics/provider.tsx | 13 ++- apps/nextjs/src/mocks/next/navigation.ts | 13 ++- apps/nextjs/src/utils/scrollToRef.ts | 16 +-- .../nextjs/src/utils/serverSideFeatureFlag.ts | 5 +- packages/aila/src/core/chat/PatchEnqueuer.ts | 6 +- packages/aila/src/core/llm/OpenAIService.ts | 6 +- .../builders/AilaLessonPromptBuilder.ts | 5 +- .../adapters/DatadogAnalyticsAdapter.ts | 5 +- .../reporters/SentryErrorReporter.ts | 7 +- .../src/features/generation/AilaGeneration.ts | 5 +- .../src/features/moderation/AilaModeration.ts | 5 +- .../moderation/moderators/MockModerator.ts | 5 +- .../moderation/moderators/OpenAiModerator.ts | 8 +- .../persistence/adaptors/prisma/index.ts | 7 +- packages/aila/src/features/rag/AilaRag.ts | 5 +- .../aila/src/helpers/errorReporting/index.ts | 5 +- .../lib/openai/OpenAICompletionWithLogging.ts | 8 +- .../aila/src/protocol/jsonPatchProtocol.ts | 61 +++-------- .../export/exportAdditionalMaterialsDoc.ts | 5 +- packages/api/src/export/exportLessonPlan.ts | 5 +- packages/api/src/export/exportLessonSlides.ts | 5 +- packages/api/src/export/exportQuizDoc.ts | 5 +- packages/api/src/export/exportWorksheets.ts | 5 +- packages/api/src/middleware/adminAuth.ts | 8 +- packages/api/src/middleware/auth.ts | 9 +- packages/api/src/middleware/testSupport.ts | 6 +- packages/api/src/router/admin.ts | 7 +- packages/api/src/router/app.ts | 9 +- packages/api/src/router/cloudinary.ts | 6 +- packages/api/src/router/exports.ts | 41 +++---- packages/api/src/router/generations.ts | 18 ++-- packages/api/src/router/judgements.ts | 6 +- packages/api/src/router/moderations.ts | 5 +- .../api/src/router/testSupport/prepareUser.ts | 5 +- packages/api/src/trpc.ts | 5 +- packages/core/src/client.ts | 4 +- .../functions/demo/populateDemoStatuses.ts | 14 ++- .../requestImageDescription.ts | 14 ++- .../core/src/functions/lesson/generatePlan.ts | 5 +- .../core/src/functions/lesson/summarise.ts | 5 +- .../core/src/functions/lessonPlan/embedAll.ts | 5 +- packages/core/src/middleware/eventLogger.ts | 6 +- packages/core/src/models/generations.ts | 6 +- packages/core/src/models/lessonPlans.ts | 7 +- packages/core/src/models/lessons.ts | 31 +++--- packages/core/src/models/promptVariants.ts | 5 +- packages/core/src/models/prompts.ts | 6 +- packages/core/src/models/safetyViolations.ts | 6 +- packages/core/src/models/snippets.ts | 11 +- packages/core/src/models/transcript.ts | 13 ++- packages/core/src/rag/index.ts | 47 ++++---- packages/core/src/tracing/baseTracing.ts | 14 +-- .../core/src/utils/getCaptionsFromFile.ts | 7 +- .../rateLimiting/userBasedRateLimiter.ts | 10 +- .../workers/generations/requestGeneration.ts | 14 ++- packages/db/index.ts | 30 ++++-- packages/db/package.json | 1 + packages/exports/package.json | 1 + .../exports/src/exportAdditionalMaterials.ts | 6 +- packages/exports/src/exportDocsWorksheet.ts | 6 +- packages/ingest/package.json | 1 + .../src/captions/getCaptionsByFileName.ts | 7 +- .../embedding/handleEmbeddingBatchSuccess.ts | 11 +- .../handleLessonPlanBatchSuccess.ts | 7 +- .../src/import-lessons/importLessons.ts | 7 +- .../import-lessons/importLessonsFromCSV.ts | 5 +- .../ingest/src/openai-batches/startBatch.ts | 8 +- .../src/openai-batches/writeBatchFile.ts | 6 +- .../src/utils/splitJsonlByRowsOrSize.ts | 7 +- packages/logger/index.ts | 100 +++++++++++++----- packages/logger/package.json | 1 + packages/logger/structuredLogger.ts | 27 +++++ pnpm-lock.yaml | 94 ++++++++++------ 119 files changed, 791 insertions(+), 390 deletions(-) create mode 100644 packages/logger/structuredLogger.ts diff --git a/apps/nextjs/jest.setup.js b/apps/nextjs/jest.setup.js index 3fc22247f..61958adc9 100644 --- a/apps/nextjs/jest.setup.js +++ b/apps/nextjs/jest.setup.js @@ -1,7 +1,5 @@ require("@testing-library/jest-dom"); -console.log("Use Testing library jest-dom/extend-expect"); - // Mock Next.js Image component jest.mock("next/image", () => ({ __esModule: true, diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 6c17525b0..6d89b5d07 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -41,10 +41,9 @@ const getConfig = async (phase) => { isProductionBuild = releaseStage === RELEASE_STAGE_PRODUCTION; appVersion = getAppVersion({ isProductionBuild }); - console.log(` - - Found release stage: "${releaseStage}"`); - console.log(`Found app version: "${appVersion}"`); + console.log( + `Release stage: "${releaseStage}". App version: "${appVersion}"`, + ); } /** @type {import('next').NextConfig} */ diff --git a/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts b/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts index 8d25a8cb7..5bd6ce109 100644 --- a/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts +++ b/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts @@ -1,14 +1,16 @@ -import browserLogger from "@oakai/logger/browser"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { z } from "zod"; +const log = aiLogger("ui"); + export function parseLocalStorageData( storageKey: string, schema: S, ): z.infer | null { const storedData = localStorage.getItem(storageKey); - browserLogger.debug( + log.info( "Attempting to parse localStorage key=%s data=%o", storageKey, storedData, @@ -20,9 +22,9 @@ export function parseLocalStorageData( return schema.parse(jsonParsedData); } } catch (err) { - browserLogger.error(err); + log.error(err); if (storedData !== "") { - console.error("Failed to parse session from localStorage", storedData); + log.error("Failed to parse session from localStorage", storedData); Sentry.captureException( new Error("Failed to parse session from localStorage"), { extra: { originalError: err, storedData } }, diff --git a/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx b/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx index 4203abf4e..6daac1d72 100644 --- a/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx +++ b/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx @@ -2,6 +2,7 @@ import { useUser } from "#clerk/nextjs"; import { redirect } from "#next/navigation"; +import { aiLogger } from "@oakai/logger"; import LoadingWheel from "@/components/LoadingWheel"; import { trpc } from "@/utils/trpc"; @@ -14,6 +15,8 @@ interface AdminChatProps { }; } +const log = aiLogger("admin"); + export default function AdminChat({ params }: Readonly) { const user = useUser(); const { chatId } = params; @@ -31,7 +34,7 @@ export default function AdminChat({ params }: Readonly) { return ; } - console.log("chat", chat); + log.info("chat", chat); if (!chat) { return
No chat found
; diff --git a/apps/nextjs/src/app/api/chat/chatHandler.ts b/apps/nextjs/src/app/api/chat/chatHandler.ts index 6703d3dae..3283a993d 100644 --- a/apps/nextjs/src/app/api/chat/chatHandler.ts +++ b/apps/nextjs/src/app/api/chat/chatHandler.ts @@ -11,6 +11,7 @@ import { withTelemetry, } from "@oakai/core/src/tracing/serverTracing"; import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; // #TODO StreamingTextResponse is deprecated. If we choose to adopt the "ai" package // more fully, we should refactor to support its approach to streaming // but this could be a significant change given we have our record-separator approach @@ -26,6 +27,8 @@ import { } from "./fixtures"; import { fetchAndCheckUser } from "./user"; +const log = aiLogger("chat"); + export const maxDuration = 300; const prisma: PrismaClientWithAccelerate = globalPrisma; @@ -106,7 +109,7 @@ function handleConnectionAborted(req: NextRequest) { const abortController = new AbortController(); req.signal.addEventListener("abort", () => { - console.log("Client has disconnected"); + log.info("Client has disconnected"); abortController.abort(); }); return abortController; diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts index 306e32345..d4d9f5b8e 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts @@ -1,9 +1,12 @@ import { Message } from "@oakai/aila"; import { LLMService } from "@oakai/aila/src/core/llm/LLMService"; import { OpenAIService } from "@oakai/aila/src/core/llm/OpenAIService"; +import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; import { ZodSchema } from "zod"; +const log = aiLogger("fixtures"); + export class FixtureRecordLLMService implements LLMService { name = "FixureRecordLLM"; private _openAIService: OpenAIService; @@ -54,17 +57,17 @@ export class FixtureRecordLLMService implements LLMService { null, 2, ); - console.log("Fixtures: Writing formatted to", formattedUrl); + log.info("Writing formatted to", formattedUrl); await fs.writeFile(formattedUrl, formatted); } catch (e) { - console.error("Error writing formatted file", e); + log.error("Error writing formatted file", e); } const chunksUrl = `${process.cwd()}/tests-e2e/recordings/${fixtureName}.chunks.txt`; const encodedChunks = chunks .map((c) => c.replaceAll("\n", "__NEWLINE__")) .join("\n"); - console.log("Fixtures: Writing chunks to", chunksUrl); + log.info("Writing chunks to", chunksUrl); await fs.writeFile(chunksUrl, encodedChunks); controller.close(); diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts index 2a90ba60d..921e184f1 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts @@ -1,9 +1,12 @@ import { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; import { createOpenAIClient } from "@oakai/core/src/llm/openai"; +import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; import OpenAI from "openai"; import { ChatCompletionCreateParamsNonStreaming } from "openai/resources/index.mjs"; +const log = aiLogger("fixtures"); + export class FixtureRecordOpenAiClient implements OpenAILike { constructor( public fixtureName: string, @@ -30,7 +33,7 @@ export class FixtureRecordOpenAiClient implements OpenAILike { const responseText = JSON.stringify(response, null, 2); const fileUrl = `${process.cwd()}/tests-e2e/recordings/${this.fixtureName}.moderation.json`; - console.log("Fixtures: Writing moderation to", fileUrl); + log.info("Writing moderation to", fileUrl); await fs.writeFile(fileUrl, responseText); return response; diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayLLMService.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayLLMService.ts index 861de72bd..86c989ce9 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayLLMService.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayLLMService.ts @@ -1,12 +1,15 @@ import { MockLLMService } from "@oakai/aila/src/core/llm/MockLLMService"; +import { aiLogger } from "@oakai/logger"; import fs from "fs"; +const log = aiLogger("fixtures"); + export class FixtureReplayLLMService extends MockLLMService { name = "FixureReplayLLM"; constructor(fixtureName: string) { const fileUrl = `${process.cwd()}/tests-e2e/recordings/${fixtureName}.chunks.txt`; - console.log("Fixtures: Loading chunks from", fileUrl); + log.info("Loading chunks from", fileUrl); const fixture = fs.readFileSync(fileUrl, "utf8"); const chunks = fixture diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts index c063f37f9..895a9ea33 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts @@ -1,7 +1,10 @@ import { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; +import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; import OpenAI from "openai"; +const log = aiLogger("fixtures"); + export class FixtureReplayOpenAiClient implements OpenAILike { constructor(public fixtureName: string) {} @@ -9,7 +12,7 @@ export class FixtureReplayOpenAiClient implements OpenAILike { completions: { create: async () => { const fileUrl = `${process.cwd()}/tests-e2e/recordings/${this.fixtureName}.moderation.json`; - console.log("Fixtures: Loading moderation from", fileUrl); + log.info("Loading moderation from", fileUrl); const fixture = await fs.readFile(fileUrl, "utf8"); return JSON.parse(fixture) as OpenAI.Chat.ChatCompletion; }, diff --git a/apps/nextjs/src/app/api/chat/fixtures/index.ts b/apps/nextjs/src/app/api/chat/fixtures/index.ts index ef26c4500..b5d8c0562 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/index.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/index.ts @@ -1,8 +1,12 @@ +import { aiLogger } from "@oakai/logger"; + import { FixtureRecordLLMService } from "./FixtureRecordLLMService"; import { FixtureRecordOpenAiClient } from "./FixtureRecordOpenAiClient"; import { FixtureReplayLLMService } from "./FixtureReplayLLMService"; import { FixtureReplayOpenAiClient } from "./FixtureReplayOpenAiClient"; +const log = aiLogger("fixtures"); + const fixturesEnabled = process.env.AILA_FIXTURES_ENABLED === "true"; export function getFixtureLLMService(headers: Headers, chatId: string) { @@ -18,12 +22,12 @@ export function getFixtureLLMService(headers: Headers, chatId: string) { } if (fixtureMode === "record") { - console.log("Using fixtureMode=record"); + log.info("Using fixtureMode=record"); return new FixtureRecordLLMService(fixtureName, chatId); } if (fixtureMode === "replay") { - console.log("Using fixtureMode=replay"); + log.info("Using fixtureMode=replay"); return new FixtureReplayLLMService(fixtureName); } } @@ -44,12 +48,12 @@ export function getFixtureModerationOpenAiClient( } if (fixtureMode === "record") { - console.log("Using moderation fixtureMode=record"); + log.info("Using moderation fixtureMode=record"); return new FixtureRecordOpenAiClient(fixtureName, chatId); } if (fixtureMode === "replay") { - console.log("Using moderation fixtureMode=replay"); + log.info("Using moderation fixtureMode=replay"); return new FixtureReplayOpenAiClient(fixtureName); } } diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 07f134291..2d24a3aa2 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -7,8 +7,11 @@ import { } from "@oakai/core"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; import { PrismaClientWithAccelerate } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { waitUntil } from "@vercel/functions"; +const log = aiLogger("chat"); + type PluginCreator = ( prisma: PrismaClientWithAccelerate, SafetyViolations?: typeof defaultSafetyViolations, @@ -78,7 +81,7 @@ export const createWebActionsPlugin: PluginCreator = ( ); } catch (error) { if (error instanceof UserBannedError) { - console.log("User is banned, queueing account lock message"); + log.info("User is banned, queueing account lock message"); enqueue({ type: "action", action: "SHOW_ACCOUNT_LOCKED", diff --git a/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts index f11c7af07..d38a54bf4 100644 --- a/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts @@ -1,11 +1,14 @@ import { createContext } from "@oakai/api/src/context"; import { chatAppRouter } from "@oakai/api/src/router/chat"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; +const log = aiLogger("trpc"); + const handler = (req: NextRequest, res: NextResponse) => fetchRequestHandler({ endpoint: "/api/trpc/chat", @@ -16,7 +19,7 @@ const handler = (req: NextRequest, res: NextResponse) => }, onError: (e) => { if (process.env.NODE_ENV === "development") { - console.error(e); + log.error(e); } Sentry.captureException(e.error); }, diff --git a/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts index dffcdbf25..41b409442 100644 --- a/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts @@ -1,11 +1,14 @@ import { createContext } from "@oakai/api/src/context"; import { oakAppRouter } from "@oakai/api/src/router"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; +const log = aiLogger("trpc"); + const handler = (req: NextRequest, res: NextResponse) => fetchRequestHandler({ endpoint: "/api/trpc/main", @@ -16,7 +19,7 @@ const handler = (req: NextRequest, res: NextResponse) => }, onError: (e) => { if (process.env.NODE_ENV === "development") { - console.error(e); + log.error(e); } Sentry.captureException(e.error); }, diff --git a/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts index cf55ac04a..9d1b4bfa1 100644 --- a/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts @@ -1,12 +1,15 @@ import { createContext } from "@oakai/api/src/context"; import { testSupportRouter as testSupportRouterInternal } from "@oakai/api/src/router/testSupport"; import { router } from "@oakai/api/src/trpc"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; import { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; +const log = aiLogger("trpc"); + const testSupportEnabled = process.env.NODE_ENV === "development" || process.env.VERCEL_ENV === "preview"; @@ -25,7 +28,7 @@ const handler = (req: NextRequest, res: NextResponse) => }, onError: (e) => { if (process.env.NODE_ENV === "development") { - console.error(e); + log.error(e); } Sentry.captureException(e.error); }, diff --git a/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx b/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx index 629814f3a..f626780f6 100644 --- a/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx +++ b/apps/nextjs/src/app/lesson-planner/preview/[slug]/page.tsx @@ -1,8 +1,11 @@ import { Apps } from "@oakai/core"; import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { LessonPlanPreview } from "./preview"; +const log = aiLogger("chat"); + async function getData(slug: string) { const appsModel = new Apps(prisma); @@ -25,7 +28,7 @@ export default async function QuizPreviewPage({ }: { params: { slug: string }; }) { - console.log("params", params); + log.info("params", params); const planSections = await getData(params.slug); diff --git a/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx b/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx index 6cf2ba3d6..c5c79051d 100644 --- a/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx +++ b/apps/nextjs/src/app/quiz-designer/preview/[slug]/page.tsx @@ -1,8 +1,11 @@ import { Apps } from "@oakai/core"; import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import QuizPreview from "./preview"; +const log = aiLogger("qd"); + async function getData(slug: string) { const appsModel = new Apps(prisma); const sharedData = await appsModel.getSharedContent(slug); @@ -24,7 +27,7 @@ export default async function QuizPreviewPage({ }: { params: { slug: string }; }) { - console.log("params", params); + log.info("params", params); const questions = await getData(params.slug); diff --git a/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx b/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx index 95e9521a8..466f8671d 100644 --- a/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx +++ b/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx @@ -3,6 +3,7 @@ import { memo, useCallback, useEffect, useReducer, useState } from "react"; import { useUser } from "@clerk/nextjs"; +import { aiLogger } from "@oakai/logger"; import { quizAppReducer } from "ai-apps/quiz-designer/state/reducer"; import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import { useQuizSession } from "hooks/useQuizSession"; @@ -17,6 +18,8 @@ import { RestoreDialogRoot } from "@/components/AppComponents/common/RestoreDial import Layout from "@/components/Layout"; import { trpc } from "@/utils/trpc"; +const log = aiLogger("qd"); + export const initialState: QuizAppState = { status: QuizAppStatus.Initial, sessionId: null, @@ -93,12 +96,12 @@ const StatePersistence = ({ state }: Readonly<{ state: QuizAppState }>) => { if (state.status === QuizAppStatus.EditingQuestions) { const formatState = JSON.stringify(state); - console.log("Store state in local storage"); + log.info("Store state in local storage"); localStorage.setItem("quizData", formatState); } if (state.sessionId) { - console.log("Update session state", { state }); + log.info("Update session state", { state }); updateSessionStateMutationCall({ sessionId: state.sessionId, output: restOfState, @@ -112,7 +115,7 @@ const StatePersistence = ({ state }: Readonly<{ state: QuizAppState }>) => { const MemoizedStatePersistence = memo( StatePersistence, (oldProps, newProps) => { - console.log({ oldProps, newProps }); + log.info({ oldProps, newProps }); return equals(oldProps.state, newProps.state); }, ); diff --git a/apps/nextjs/src/app/sign-in/[[...sign-in]]/DetectStuckBannedUser.tsx b/apps/nextjs/src/app/sign-in/[[...sign-in]]/DetectStuckBannedUser.tsx index 594785f70..0fcefe1cb 100644 --- a/apps/nextjs/src/app/sign-in/[[...sign-in]]/DetectStuckBannedUser.tsx +++ b/apps/nextjs/src/app/sign-in/[[...sign-in]]/DetectStuckBannedUser.tsx @@ -3,8 +3,11 @@ import { useEffect, useRef } from "react"; import { useClerk } from "@clerk/nextjs"; +import { aiLogger } from "@oakai/logger"; import { useRouter } from "next/navigation"; +const log = aiLogger("auth"); + // Clerk has a bug where banned users aren't properly handled when returning from Google auth // Instead, they see an indefinite loading spinner // We've asked for clarity here: https://discord.com/channels/856971667393609759/1255553827386687498 @@ -33,7 +36,7 @@ const DetectStuckBannedUser = () => { if (spinnerActive && firstFactorStuck) { if (isStuck.current) { // After 500ms the user is still stuck - console.log("Detected stuck user. Reloading"); + log.info("Detected stuck user. Reloading"); // The page will be /sign-in/factor-two... // Redirect back to /sign-in root to reset clerk UI and show the correct error diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts index a83084c4b..429271400 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts @@ -1,7 +1,10 @@ import { useMemo, useEffect } from "react"; +import { aiLogger } from "@oakai/logger"; import { Message } from "ai"; +const log = aiLogger("chat"); + export type AilaStreamingStatus = | "Loading" | "RequestMade" @@ -43,7 +46,7 @@ export const useAilaStreamingStatus = ({ }, [isLoading, messages]); useEffect(() => { - console.log("ailaStreamingStatus set:", ailaStreamingStatus); + log.info("ailaStreamingStatus set:", ailaStreamingStatus); }, [ailaStreamingStatus]); return ailaStreamingStatus; diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts index 4821fa645..1f4cac571 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts @@ -1,6 +1,9 @@ import { type LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import { aiLogger } from "@oakai/logger"; import { type Message } from "ai/react"; +const log = aiLogger("chat"); + export function findMessageIdFromContent({ content }: { content: string }) { return content .split(`␞`) @@ -16,10 +19,10 @@ export function findMessageIdFromContent({ content }: { content: string }) { } export function findLatestServerSideState(workingMessages: Message[]) { - console.log("Finding latest server-side state", { workingMessages }); + log.info("Finding latest server-side state", { workingMessages }); const lastMessage = workingMessages[workingMessages.length - 1]; if (!lastMessage?.content.includes(`"type":"state"`)) { - console.log("No server state found"); + log.info("No server state found"); return; } const state: LooseLessonPlan = lastMessage.content @@ -35,7 +38,7 @@ export function findLatestServerSideState(workingMessages: Message[]) { .filter((i) => i !== null) .filter((i) => i.type === "state") .map((i) => i.value)[0]; - console.log("Got latest server state", { state }); + log.info("Got latest server state", { state }); return state; } diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx index 2a025e86c..dec946012 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx @@ -18,6 +18,7 @@ import { } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { isSafe } from "@oakai/core/src/utils/ailaModeration/helpers"; import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import { aiLogger } from "@oakai/logger"; import { Message } from "ai"; import { MemoizedReactMarkdownWithStyles } from "@/components/AppComponents/Chat/markdown"; @@ -29,6 +30,8 @@ import { ModerationModalHelpers } from "../../FeedbackForms/ModerationFeedbackMo import { AilaStreamingStatus } from "../Chat/hooks/useAilaStreamingStatus"; import { isModeration } from "./protocol"; +const log = aiLogger("chat"); + export interface ChatMessageProps { chatId: string; // Needed for when we refactor to use a moderation provider message: Message; @@ -242,7 +245,7 @@ function ChatMessagePart({ }>; if (!PartComponent) { - console.log("Unknown part type", part.document.type, JSON.stringify(part)); // eslint-disable-line no-console + log.info("Unknown part type", part.document.type, JSON.stringify(part)); return null; } diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx index 5292dbec7..bd47903b4 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx @@ -3,6 +3,7 @@ import * as React from "react"; import { toast } from "react-hot-toast"; +import { aiLogger } from "@oakai/logger"; import { type DialogProps } from "@radix-ui/react-dialog"; import { Button } from "@/components/AppComponents/Chat/ui/button"; @@ -21,6 +22,8 @@ import { trpc } from "@/utils/trpc"; import { constructSharePath } from "./Chat/utils"; +const log = aiLogger("chat"); + interface ChatShareDialogProps extends DialogProps { chat: SideBarChatItem; onCopy: () => void; @@ -56,7 +59,7 @@ export function ChatShareDialog({ toast.success("Link copied to clipboard"); }, onError(error) { - console.error(error); // TODO sentry? + log.error(error); // TODO sentry? toast.error("Failed to share chat"); }, }); diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx index ffa837f55..5e749e674 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useState } from "react"; import { useRouter } from "#next/navigation"; import { useUser } from "@clerk/nextjs"; +import { aiLogger } from "@oakai/logger"; import { Flex } from "@radix-ui/themes"; import { Button } from "@/components/AppComponents/Chat/ui/button"; @@ -18,6 +19,8 @@ import ChatPanelDisclaimer from "./chat-panel-disclaimer"; import { ChatStartForm } from "./chat-start-form"; import EmptyScreenAccordion from "./empty-screen-accordian"; +const log = aiLogger("chat"); + const exampleMessages = [ { heading: "History • Key stage 3 • The end of Roman Britain ", @@ -49,7 +52,7 @@ export function ChatStart() { }, ); - console.log("App session created:", result); + log.info("App session created:", result); trackEvent("chat:send_message", { id: result.id, message, @@ -57,7 +60,7 @@ export function ChatStart() { router.push(`/aila/${result.id}`); } catch (error) { - console.error("Error creating app session:", error); + log.error("Error creating app session:", error); } }, [ diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx index beeb61659..0b6b4890d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx @@ -2,6 +2,7 @@ import { useRef, useState } from "react"; import { getLastAssistantMessage } from "@oakai/aila/src/helpers/chat/getLastAssistantMessage"; import type { AilaUserModificationAction } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { OakBox, OakP, OakRadioGroup } from "@oaknational/oak-components"; import { TextArea } from "@radix-ui/themes"; @@ -12,6 +13,8 @@ import ActionButton from "./action-button"; import { DropDownFormWrapper, FeedbackOption } from "./drop-down-form-wrapper"; import { SmallRadioButton } from "./small-radio-button"; +const log = aiLogger("chat"); + const modifyOptions = [ { label: "Make it easier", @@ -157,7 +160,7 @@ function handleLabelText({ text: string; section: string; }): string { - console.log("section", section); + log.info("section", section); if ( section === "Misconceptions" || section === "Key learning points" || diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx index 3c378d8fc..84a140eed 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { useUser } from "#clerk/nextjs"; -import logger from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import * as Dialog from "@radix-ui/react-dialog"; import { trpc } from "@/utils/trpc"; @@ -11,6 +11,8 @@ import FeedbackDialog from "../common/FeedbackDialog"; export const FeedbackDialogRoot = Dialog.Root; export const FeedbackDialogTrigger = Dialog.Trigger; +const log = aiLogger("feedback"); + type FeedbackDialogProps = { flaggedItem?: string; closeDialog: () => void; @@ -40,12 +42,12 @@ function JudgementFeedbackDialog({ if (!email) { // This case really shouldn't happen as the user should be logged in - logger.error("User attempted to give feedback without an email address"); + log.error("User attempted to give feedback without an email address"); return null; } if (!flaggedItems) { - logger.error("User attempted to give feedback but flaggedItem was null"); + log.error("User attempted to give feedback but flaggedItem was null"); return null; } @@ -67,7 +69,7 @@ function JudgementFeedbackDialog({ if (typeof result === "boolean" && result) { setHasSubmitted(true); } else { - console.log("Error submitting feedback", result); + log.error("Error submitting feedback", result); } }; diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx index b1fecb4b1..b28b5199e 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { useUser } from "#clerk/nextjs"; import { GenerationPart } from "@oakai/core/src/types"; -import logger from "@oakai/logger"; +import { structuredLogger as logger } from "@oakai/logger"; import * as Dialog from "@radix-ui/react-dialog"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx index c5ccc18b3..50aa10624 100644 --- a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx +++ b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import { aiLogger } from "@oakai/logger"; import { Box } from "@radix-ui/themes"; import * as Sentry from "@sentry/nextjs"; import Link from "next/link"; @@ -18,6 +19,8 @@ import LessonIcon from "../../SVGParts/LessonIcon"; import QuizIcon from "../../SVGParts/QuizIcon"; import SlidesIcon from "../../SVGParts/SlidesIcon"; +const log = aiLogger("chat"); + const allexportLinksObject = z.object({ lessonSlides: z.string(), lessonPlan: z.string(), @@ -98,7 +101,7 @@ export const DownloadAllButton = ({ const isFeatureEnabled = useClientSideFeatureFlag("download-all-button"); if (!isFeatureEnabled) { - console.log("Download all button is disabled"); + log.info("Download all button is disabled"); return null; } @@ -122,7 +125,7 @@ export const DownloadAllButton = ({ lessonPlanLink: parsedData.lessonPlan, }); } catch (error) { - console.error(error); + log.error(error); Sentry.captureException(error); } } diff --git a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx index 71889f014..6d0ec025e 100644 --- a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx @@ -19,6 +19,7 @@ import { import { isToxic } from "@oakai/core/src/utils/ailaModeration/helpers"; import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { Moderation } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { Message, nanoid } from "ai"; import { ChatRequestOptions, CreateMessage } from "ai"; @@ -39,6 +40,8 @@ import { isModeration, } from "../AppComponents/Chat/chat-message/protocol"; +const log = aiLogger("chat"); + export type ChatContextProps = { id: string; chat: AilaPersistedChat | undefined; @@ -197,11 +200,11 @@ export function ChatProvider({ id, children }: Readonly) { Sentry.captureException(new Error("Use chat error"), { extra: { originalError: error }, }); - console.error("UseChat error", { error, messages }); + log.error("UseChat error", { error, messages }); setHasFinished(true); }, onResponse(response) { - console.log("Chat: On Response"); + log.info("Chat: On Response"); chatAreaRef.current?.scrollTo(0, chatAreaRef.current?.scrollHeight); if (response.status === 401) { @@ -216,7 +219,7 @@ export function ChatProvider({ id, children }: Readonly) { } }, onFinish(response) { - console.log("Chat: On Finish", new Date().toISOString(), { + log.info("Chat: On Finish", new Date().toISOString(), { response, path, }); @@ -306,7 +309,7 @@ export function ChatProvider({ id, children }: Readonly) { }); } } catch (error) { - console.error("Error handling queued action:", error); + log.error("Error handling queued action:", error); } finally { isExecutingAction.current = false; } diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx index bd8972f01..f87187eab 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx @@ -1,5 +1,6 @@ import { useCallback, useEffect, useState } from "react"; +import { aiLogger } from "@oakai/logger"; import { Flex } from "@radix-ui/themes"; import { captureMessage } from "@sentry/nextjs"; @@ -7,6 +8,8 @@ import Button from "@/components/Button"; import { useDemoUser } from "@/components/ContextProviders/Demo"; import LoadingWheel from "@/components/LoadingWheel"; +const log = aiLogger("demo"); + function friendlyNumber( appSessionsRemaining: number | undefined, appSessionsPerMonth: number, @@ -79,7 +82,7 @@ const CreatingChatDialog = ({ try { await submit(); } catch (error) { - console.error("Error creating demo lesson:", error); + log.error("Error creating demo lesson:", error); setIsSubmitting(false); } }, [submit]); diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx index 81e7a3d8b..586d65226 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; +import { aiLogger } from "@oakai/logger"; import { Flex } from "@radix-ui/themes"; import { Message } from "ai"; import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey"; @@ -7,6 +8,8 @@ import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey import ChatButton from "@/components/AppComponents/Chat/ui/chat-button"; import { Icon } from "@/components/Icon"; +const log = aiLogger("chat"); + type ShareChatProps = { chatId: string | undefined; closeDialog: () => void; @@ -34,7 +37,7 @@ const ReportContentDialog = ({ } async function onSubmit(e?: React.FormEvent) { - console.log("submitting"); + log.info("submitting"); e?.preventDefault(); setUserHasSubmitted(true); submitSurveyWithOutClosing({ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts index 8fb137344..05a2a92e1 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState, useCallback } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,6 +11,8 @@ import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; +const log = aiLogger("exports"); + export function useExportAdditionalMaterials({ onStart, lesson, @@ -49,7 +52,7 @@ export function useExportAdditionalMaterials({ }); setChecked(true); } catch (error) { - console.error("Error during check:", error); + log.error("Error during check:", error); } } }, [ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts b/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts index 3abf1bdd5..e52c96c2f 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts @@ -5,12 +5,15 @@ import { exportSlidesFullLessonSchema, } from "@oakai/exports/browser"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; +const log = aiLogger("exports"); + export function useExportAllLessonAssets({ onStart, lesson, @@ -44,7 +47,7 @@ export function useExportAllLessonAssets({ if (!active) { return; } - console.log("STARTING"); + log.info("STARTING"); if (!debouncedParseResult?.success) { Sentry.captureException( diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts index 33a7692be..8d84e546c 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocLessonPlanSchema } from "@oakai/exports/browser"; import { LessonPlanDocInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,6 +11,8 @@ import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; +const log = aiLogger("exports"); + export function useExportLessonPlanDoc({ onStart, lesson, @@ -49,7 +52,7 @@ export function useExportLessonPlanDoc({ }); setChecked(true); } catch (error) { - console.error("Error during check:", error); + log.error("Error during check:", error); } } }, [ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts index 42e345898..c76e5e89b 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,6 +11,8 @@ import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; +const log = aiLogger("exports"); + export function useExportLessonSlides({ onStart, lesson, @@ -49,7 +52,7 @@ export function useExportLessonSlides({ }); setChecked(true); } catch (error) { - console.error("Error during check:", error); + log.error("Error during check:", error); } } }, [ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts index 2ec65bf48..2d9783519 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts @@ -4,12 +4,15 @@ import { ExportableQuizAppState, exportableQuizAppStateSchema, } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; +const log = aiLogger("exports"); + export function useExportQuizDesignerSlides({ onStart, quiz, @@ -44,7 +47,8 @@ export function useExportQuizDesignerSlides({ if (!active) { return; } - console.log("STARTING"); + + log.info("STARTING"); if (!debouncedParseResult?.success) { Sentry.captureException( diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts index a441ce716..20c39daf4 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocQuizSchema } from "@oakai/exports/browser"; import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,6 +11,8 @@ import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; +const log = aiLogger("exports"); + export function useExportQuizDoc({ onStart, quizType, @@ -55,7 +58,7 @@ export function useExportQuizDoc({ }); setChecked(true); } catch (error) { - console.error("Error during check:", error); + log.error("Error during check:", error); } } }, [ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts index 59a00f41f..82624cda4 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts @@ -2,6 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesWorksheetSchema } from "@oakai/exports/browser"; import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,6 +11,8 @@ import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; +const log = aiLogger("exports"); + export function useExportWorksheetSlides({ onStart, lesson, @@ -49,7 +52,7 @@ export function useExportWorksheetSlides({ }); setChecked(true); } catch (error) { - console.error("Error during check:", error); + log.error("Error during check:", error); } } }, [ diff --git a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx index f58f8de00..f115314bb 100644 --- a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx +++ b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx @@ -1,16 +1,19 @@ import { useCallback, useState } from "react"; +import { useRouter } from "#next/navigation"; +import { aiLogger } from "@oakai/logger"; import { Box, Flex } from "@radix-ui/themes"; import { Resource } from "ai-apps/image-alt-generation/types"; import encode from "base64url"; import Image from "next/image"; -import { useRouter } from "#next/navigation"; import { trpc } from "@/utils/trpc"; import Button from "../Button"; import LoadingWheel from "../LoadingWheel"; +const log = aiLogger("ui"); + const ImageRow = ({ resource, isPreview, @@ -76,7 +79,7 @@ const ImageRow = ({ }; const encodedResource = encode(JSON.stringify(shareData)); - console.log("encodedResource", encodedResource); + log.info("encodedResource", encodedResource); router.push(`/image-alt-generations/${encodedResource}`); } diff --git a/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts b/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts index f0805b570..279a9bee8 100644 --- a/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts +++ b/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts @@ -1,10 +1,13 @@ import { useEffect, useState, useCallback } from "react"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/react"; import { Survey } from "posthog-js"; import useAnalytics from "@/lib/analytics/useAnalytics"; +const log = aiLogger("feedback"); + type UsePosthogFeedbackSurveyProps = { closeDialog?: () => void; surveyName: @@ -32,7 +35,7 @@ export const usePosthogFeedbackSurvey = ({ if (!matchingSurvey) { const error = new Error(`Survey with name ${surveyName} not found`); - console.error(error); + log.error(error); Sentry.captureException(error); return; @@ -64,7 +67,7 @@ export const usePosthogFeedbackSurvey = ({ }); } else { const error = new Error(`Survey not found: ${surveyName}`); - console.error(error); + log.error(error); Sentry.captureException(error); } closeDialog?.(); @@ -81,7 +84,7 @@ export const usePosthogFeedbackSurvey = ({ }); } else { const error = new Error(`Survey not found: ${surveyName}`); - console.error(error); + log.error(error); Sentry.captureException(error); } }, diff --git a/apps/nextjs/src/hooks/useCopyToClipboard.ts b/apps/nextjs/src/hooks/useCopyToClipboard.ts index 9efaa4324..5551210a6 100644 --- a/apps/nextjs/src/hooks/useCopyToClipboard.ts +++ b/apps/nextjs/src/hooks/useCopyToClipboard.ts @@ -1,5 +1,9 @@ import { useEffect, useState } from "react"; +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("ui"); + export function useCopyToClipboard(text: string) { const [copySuccess, setCopySuccess] = useState(false); @@ -7,10 +11,10 @@ export function useCopyToClipboard(text: string) { navigator.clipboard.writeText(text).then( () => { setCopySuccess(true); - console.log("Link copied to clipboard!"); + log.info("Link copied to clipboard!"); }, (err) => { - console.log("Unable to copy to clipboard", err); + log.info("Unable to copy to clipboard", err); }, ); }; diff --git a/apps/nextjs/src/hooks/useGeneration.ts b/apps/nextjs/src/hooks/useGeneration.ts index 37caa4b72..604354c86 100644 --- a/apps/nextjs/src/hooks/useGeneration.ts +++ b/apps/nextjs/src/hooks/useGeneration.ts @@ -3,6 +3,7 @@ import { useCallback, useEffect, useReducer } from "react"; import { RateLimitInfo } from "@oakai/api/src/types"; import type { SerializedGeneration } from "@oakai/core/src/models/serializers"; import { GenerationStatus } from "@oakai/db/prisma/client"; +import { aiLogger } from "@oakai/logger"; import { default as browserLogger, default as logger, @@ -15,6 +16,8 @@ import { DeepPartial } from "@/utils/types/DeepPartial"; import { RouterInputs, trpc } from "../utils/trpc"; import { useDidTransition } from "./useDidTransition"; +const log = aiLogger("generation"); + export type AdditionalUseGenerationOptions = { timeout: number; stream?: boolean; @@ -70,7 +73,7 @@ export const useGeneration = ( UGErrorCode.PARSE_ERROR, err as Error, ); - console.log("Badly formatted response", { data }); + log.error("Badly formatted response", { data }); dispatch({ type: UGActionType.GenerationFailed, error: error, diff --git a/apps/nextjs/src/hooks/useQuestionsForJudgement.ts b/apps/nextjs/src/hooks/useQuestionsForJudgement.ts index c17f947d4..4bbc058a7 100644 --- a/apps/nextjs/src/hooks/useQuestionsForJudgement.ts +++ b/apps/nextjs/src/hooks/useQuestionsForJudgement.ts @@ -1,10 +1,13 @@ import { useState } from "react"; +import { aiLogger } from "@oakai/logger"; import { optionSchema } from "ai-apps/comparative-judgement/state/types"; import { z } from "zod"; import { trpc } from "@/utils/trpc"; +const log = aiLogger("judgements"); + type FlaggedOrSkipped = "FLAGGED" | "SKIPPED"; type FlagOrSkipQuestion = { @@ -73,10 +76,10 @@ const useQuestionsForJudgement = ({ }; const result = await flagOrSkipQuestion.mutateAsync(flaggedInfo); if (result) { - console.log("Flagged question"); + log.info("Flagged question"); await clearReasonForChoice(); } else { - console.log("Failure flag did not work"); + log.info("Failure flag did not work"); } }; @@ -88,10 +91,10 @@ const useQuestionsForJudgement = ({ const result = await flagOrSkipQuestion.mutateAsync(skippedInfo); if (result) { - console.log("Skipped question", result); + log.info("Skipped question", result); await clearReasonForChoice(); } else { - console.log("Failure skip did not work"); + log.info("Failure skip did not work"); } }; @@ -113,10 +116,10 @@ const useQuestionsForJudgement = ({ const result = await selectedQuestion.mutateAsync(selectedQuestionInfo); if (result) { - console.log("success"); + log.info("success"); await clearReasonForChoice(); } else { - console.log("failure"); + log.info("failure"); } }; diff --git a/apps/nextjs/src/lib/cookie-consent/consentClient.ts b/apps/nextjs/src/lib/cookie-consent/consentClient.ts index e0e3dccf0..efa5dea69 100644 --- a/apps/nextjs/src/lib/cookie-consent/consentClient.ts +++ b/apps/nextjs/src/lib/cookie-consent/consentClient.ts @@ -1,7 +1,10 @@ +import { aiLogger } from "@oakai/logger"; import { OakConsentClient } from "@oaknational/oak-consent-client"; import * as Sentry from "@sentry/nextjs"; -const appSlug = "ai-beta" +const log = aiLogger("consent"); + +const appSlug = "ai-beta"; const policiesUrl = process.env.NEXT_PUBLIC_OAK_CONSENT_POLICIES_URL as string; const consentLogUrl = process.env.NEXT_PUBLIC_OAK_CONSENT_LOG_URL as string; const userLogUrl = process.env.NEXT_PUBLIC_OAK_CONSENT_USER_LOG_URL as string; @@ -16,7 +19,7 @@ export const consentClient = new OakConsentClient({ consentLogUrl, userLogUrl, onError: (error) => { - console.log("Oak consent client error", error); + log.error("Oak consent client error", error); Sentry.captureException(error); }, }); diff --git a/apps/nextjs/src/lib/hooks/use-enter-submit.tsx b/apps/nextjs/src/lib/hooks/use-enter-submit.tsx index 8afc84b5d..3ec713799 100644 --- a/apps/nextjs/src/lib/hooks/use-enter-submit.tsx +++ b/apps/nextjs/src/lib/hooks/use-enter-submit.tsx @@ -1,5 +1,9 @@ import { useRef, type RefObject } from "react"; +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("ui"); + export function useEnterSubmit(): { formRef: RefObject; onKeyDown: (event: React.KeyboardEvent) => void; @@ -23,7 +27,7 @@ export function useEnterSubmit(): { throw new Error("Form submission not supported"); } } catch (error) { - console.error("Failed to submit form:", error); + log.error("Failed to submit form:", error); } event.preventDefault(); } diff --git a/apps/nextjs/src/middlewares/auth.middleware.ts b/apps/nextjs/src/middlewares/auth.middleware.ts index 9251fc8b6..12a4bd937 100644 --- a/apps/nextjs/src/middlewares/auth.middleware.ts +++ b/apps/nextjs/src/middlewares/auth.middleware.ts @@ -3,10 +3,13 @@ import { clerkMiddleware, createRouteMatcher, } from "@clerk/nextjs/server"; +import { aiLogger } from "@oakai/logger"; import { NextFetchEvent, NextRequest, NextResponse } from "next/server"; import { sentrySetUser } from "@/lib/sentry/sentrySetUser"; +const log = aiLogger("middleware:auth"); + declare global { interface CustomJwtSessionClaims { labs: { @@ -93,11 +96,8 @@ const needsToCompleteOnboarding = (sessionClaims: CustomJwtSessionClaims) => { return !labs.isOnboarded || labs.isDemoUser === null; }; -const LOG = false; const logger = (request: NextRequest) => (message: string) => { - if (LOG) { - console.log(`[AUTH] ${request.url} ${message}`); - } + log.info(`${request.url} ${message}`); }; function conditionallyProtectRoute( @@ -152,7 +152,7 @@ export async function authMiddleware( return response; } } catch (error) { - console.error({ event: "middleware.auth.error", error }); + log.error({ event: "middleware.auth.error", error }); throw new Error("Error in authMiddleware", { cause: error }); } diff --git a/apps/nextjs/src/middlewares/middlewareErrorLogging.ts b/apps/nextjs/src/middlewares/middlewareErrorLogging.ts index b063a4931..56b725aaf 100644 --- a/apps/nextjs/src/middlewares/middlewareErrorLogging.ts +++ b/apps/nextjs/src/middlewares/middlewareErrorLogging.ts @@ -1,3 +1,4 @@ +import { structuredLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { NextFetchEvent, NextRequest } from "next/server"; @@ -33,7 +34,7 @@ export async function logError( if (rootError instanceof SyntaxError) { // Log the error so it is picked up by the log drain (eg. DataDog) - console.warn({ + structuredLogger.warn({ event: "middleware.syntaxError", error: rootError.message, url: requestInfo.url, diff --git a/apps/nextjs/src/mocks/analytics/provider.tsx b/apps/nextjs/src/mocks/analytics/provider.tsx index f1d27824d..f1121582b 100644 --- a/apps/nextjs/src/mocks/analytics/provider.tsx +++ b/apps/nextjs/src/mocks/analytics/provider.tsx @@ -4,6 +4,7 @@ See the readme for why this is needed. */ import React from "react"; +import { aiLogger } from "@oakai/logger"; import { PostHog } from "posthog-js"; import Avo from "@/lib/avo/Avo"; @@ -13,10 +14,12 @@ import { analyticsContext, } from "./../../components/ContextProviders/AnalyticsProvider"; +const log = aiLogger("analytics"); + const mockTrack: typeof Avo = new Proxy(Avo, { get: (target, prop) => { return (...args: unknown[]) => { - console.log(`Mock Avo.${String(prop)} called with:`, target, ...args); + log.info(`Mock Avo.${String(prop)} called with:`, target, ...args); }; }, }); @@ -24,16 +27,16 @@ const mockTrack: typeof Avo = new Proxy(Avo, { const mockAnalyticsContext: AnalyticsContext = { track: mockTrack, trackEvent: (eventName: string, properties?: Record) => { - console.log("Mock trackEvent called:", eventName, properties); + log.info("Mock trackEvent called:", eventName, properties); }, identify: (userId: string, properties: { email?: string }) => { - console.log("Mock identify called:", userId, properties); + log.info("Mock identify called:", userId, properties); }, page: (path: string) => { - console.log("Mock page view:", path); + log.info("Mock page view:", path); }, reset: () => { - console.log("Mock reset called"); + log.info("Mock reset called"); }, posthogAiBetaClient: { isFeatureEnabled: () => true, diff --git a/apps/nextjs/src/mocks/next/navigation.ts b/apps/nextjs/src/mocks/next/navigation.ts index 577b82d72..1eef566f1 100644 --- a/apps/nextjs/src/mocks/next/navigation.ts +++ b/apps/nextjs/src/mocks/next/navigation.ts @@ -1,21 +1,24 @@ -/* +/* Mocks next/navigation for use in Storybook. -See the readme for more context on why this is needed. +See the readme for more context on why this is needed. */ +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("testing"); export const useRouter = () => ({ push: (path: string) => { - console.log("Mocked push to", path); + log.info("Mocked push to", path); }, redirect: (path: string) => { - console.log("Mocked redirect to", path); + log.info("Mocked redirect to", path); }, replace: () => {}, prefetch: () => {}, }); export const redirect = (path: string) => { - console.log("Mocked redirect to", path); + log.info("Mocked redirect to", path); }; export const usePathname = () => "/"; diff --git a/apps/nextjs/src/utils/scrollToRef.ts b/apps/nextjs/src/utils/scrollToRef.ts index 6e060d7ec..b07de0797 100644 --- a/apps/nextjs/src/utils/scrollToRef.ts +++ b/apps/nextjs/src/utils/scrollToRef.ts @@ -1,5 +1,9 @@ import { RefObject } from "react"; +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("ui"); + export interface ScrollToRefParams { ref: RefObject; containerRef: RefObject; @@ -40,24 +44,24 @@ export const scrollToRef = ({ } // Debug: Log progress and scroll position - // console.log(`Time Elapsed: ${timeElapsed}`); - // console.log(`Progress: ${progress}`); - // console.log(`ScrollTop: ${containerRef.current.scrollTop}`); + // log.info(`Time Elapsed: ${timeElapsed}`); + // log.info(`Progress: ${progress}`); + // log.info(`ScrollTop: ${containerRef.current.scrollTop}`); if (progress < 1) { requestAnimationFrame(scroll); } else { // Debug: Log completion - console.log("Scrolling completed"); + log.info("Scrolling completed"); } }; requestAnimationFrame(scroll); } catch (error) { - console.error("Error scrolling to ref", error); + log.error("Error scrolling to ref", error); } } else { // Debug: Log ref or containerRef not found - console.error("Ref or containerRef not found"); + log.error("Ref or containerRef not found"); } }; diff --git a/apps/nextjs/src/utils/serverSideFeatureFlag.ts b/apps/nextjs/src/utils/serverSideFeatureFlag.ts index 365740228..a76152b43 100644 --- a/apps/nextjs/src/utils/serverSideFeatureFlag.ts +++ b/apps/nextjs/src/utils/serverSideFeatureFlag.ts @@ -1,7 +1,10 @@ import { auth, clerkClient } from "@clerk/nextjs/server"; import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; +import { aiLogger } from "@oakai/logger"; import { kv } from "@vercel/kv"; +const log = aiLogger("feature-flags"); + export async function serverSideFeatureFlag( featureFlagId: string, ): Promise { @@ -54,7 +57,7 @@ export async function serverSideFeatureFlag( return isFeatureFlagEnabled; } catch (e) { - console.error("Error checking feature flag:", e); + log.error("Error checking feature flag:", e); return false; } } diff --git a/packages/aila/src/core/chat/PatchEnqueuer.ts b/packages/aila/src/core/chat/PatchEnqueuer.ts index 415ba2e0b..36f210cb8 100644 --- a/packages/aila/src/core/chat/PatchEnqueuer.ts +++ b/packages/aila/src/core/chat/PatchEnqueuer.ts @@ -1,5 +1,9 @@ +import { aiLogger } from "@oakai/logger"; + import { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; +const log = aiLogger("aila:protocol"); + export class PatchEnqueuer { private encoder: TextEncoder; private controller?: ReadableStreamDefaultController; @@ -37,7 +41,7 @@ export class PatchEnqueuer { try { this.controller.enqueue(encodedPatch); } catch (error) { - console.error("Error enqueuing patch", error); + log.error("Error enqueuing patch", error); throw error; } }); diff --git a/packages/aila/src/core/llm/OpenAIService.ts b/packages/aila/src/core/llm/OpenAIService.ts index 154a73ce3..af157bac7 100644 --- a/packages/aila/src/core/llm/OpenAIService.ts +++ b/packages/aila/src/core/llm/OpenAIService.ts @@ -1,12 +1,15 @@ import { OpenAIProvider } from "@ai-sdk/openai"; import { HeliconeChatMeta } from "@oakai/core/src/llm/helicone"; import { createVercelOpenAIClient } from "@oakai/core/src/llm/openai"; +import { aiLogger } from "@oakai/logger"; import { streamObject, streamText } from "ai"; import { ZodSchema } from "zod"; import { Message } from "../chat"; import { LLMService } from "./LLMService"; +const log = aiLogger("aila:llm"); + const STRUCTURED_OUTPUTS_ENABLED = process.env.NEXT_PUBLIC_STRUCTURED_OUTPUTS_ENABLED === "true" ? true : false; export class OpenAIService implements LLMService { @@ -65,7 +68,8 @@ export class OpenAIService implements LLMService { const reader = stream.getReader(); const { value } = await reader.read(); const timeToFirstToken = Date.now() - startTime; - console.log(`Time to first token: ${timeToFirstToken}ms`); + + log.info(`Time to first token: ${timeToFirstToken}ms`); const newStream = new ReadableStream({ start(controller) { diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index 9fca4bf42..8a30cc27e 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -3,6 +3,7 @@ import { template, } from "@oakai/core/src/prompts/lesson-assistant"; import { prisma as globalPrisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { DEFAULT_RAG_LESSON_PLANS } from "../../../constants"; import { tryWithErrorReporting } from "../../../helpers/errorReporting"; @@ -21,6 +22,8 @@ import { import { AilaServices } from "../../AilaServices"; import { AilaPromptBuilder } from "../AilaPromptBuilder"; +const log = aiLogger("aila:prompt"); + export class AilaLessonPromptBuilder extends AilaPromptBuilder { constructor(aila: AilaServices) { super(aila); @@ -85,7 +88,7 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { }); }, "Did not fetch RAG content. Continuing"); - console.log("Fetched relevant lesson plans", relevantLessonPlans.length); + log.info("Fetched relevant lesson plans", relevantLessonPlans.length); const stringifiedRelevantLessonPlans = JSON.stringify( relevantLessonPlans, null, diff --git a/packages/aila/src/features/analytics/adapters/DatadogAnalyticsAdapter.ts b/packages/aila/src/features/analytics/adapters/DatadogAnalyticsAdapter.ts index 9e5e33d2b..6b234e182 100644 --- a/packages/aila/src/features/analytics/adapters/DatadogAnalyticsAdapter.ts +++ b/packages/aila/src/features/analytics/adapters/DatadogAnalyticsAdapter.ts @@ -1,7 +1,10 @@ +import { aiLogger } from "@oakai/logger"; import { getEncoding } from "js-tiktoken"; import { AnalyticsAdapter } from "./AnalyticsAdapter"; +const log = aiLogger("aila:analytics"); + export class DatadogAnalyticsAdapter extends AnalyticsAdapter { private _startedAt: number = Date.now(); @@ -95,7 +98,7 @@ export class DatadogAnalyticsAdapter extends AnalyticsAdapter { body: JSON.stringify(body), }); - console.log("Datadog status", result.status); + log.info("Datadog status", result.status); } } diff --git a/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts b/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts index 0cb7416dd..048e35c25 100644 --- a/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts +++ b/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts @@ -1,15 +1,18 @@ +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { AilaErrorSeverity, AilaErrorBreadcrumb } from "../types"; import { AilaErrorReporter } from "./AilaErrorReporter"; +const log = aiLogger("aila:errors"); + export class SentryErrorReporter extends AilaErrorReporter { public captureException( error: Error, level?: AilaErrorSeverity, context?: Record, ): void { - console.error(error); + log.error(error); Sentry.withScope(function (scope) { scope.setLevel(level ?? "error"); Sentry.captureException(error, { extra: context }); @@ -17,7 +20,7 @@ export class SentryErrorReporter extends AilaErrorReporter { } public captureMessage(message: string, level: AilaErrorSeverity): void { - console.log(message); + log.info(message); Sentry.captureMessage(message, level as Sentry.SeverityLevel); } diff --git a/packages/aila/src/features/generation/AilaGeneration.ts b/packages/aila/src/features/generation/AilaGeneration.ts index 8a04c3f14..d96b154a0 100644 --- a/packages/aila/src/features/generation/AilaGeneration.ts +++ b/packages/aila/src/features/generation/AilaGeneration.ts @@ -4,6 +4,7 @@ import { generateAilaPromptVersionVariantSlug, } from "@oakai/core/src/prompts/lesson-assistant/variants"; import { prisma, Prompt } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { kv } from "@vercel/kv"; import { getEncoding } from "js-tiktoken"; @@ -11,6 +12,8 @@ import { AilaServices } from "../../core"; import { AilaChat } from "../../core/chat"; import { AilaGenerationStatus } from "./types"; +const log = aiLogger("generation"); + export class AilaGeneration { private _aila: AilaServices; private _id: string; @@ -206,7 +209,7 @@ export class AilaGeneration { const created = await prompts.setCurrent(variantSlug, true); promptId = created?.id; } catch (e) { - console.error("Error creating prompt", e); + log.error("Error creating prompt", e); } } diff --git a/packages/aila/src/features/moderation/AilaModeration.ts b/packages/aila/src/features/moderation/AilaModeration.ts index 7f3bf6a97..6aa716190 100644 --- a/packages/aila/src/features/moderation/AilaModeration.ts +++ b/packages/aila/src/features/moderation/AilaModeration.ts @@ -11,6 +11,7 @@ import { PrismaClientWithAccelerate, prisma as globalPrisma, } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import invariant from "tiny-invariant"; import { AilaServices } from "../../core"; @@ -23,6 +24,8 @@ import { AilaModerationFeature } from "../types"; import { AilaModerator } from "./moderators"; import { OpenAiModerator } from "./moderators/OpenAiModerator"; +const log = aiLogger("aila:moderation"); + export class AilaModeration implements AilaModerationFeature { private _prisma: PrismaClientWithAccelerate; private _moderations: Moderations; @@ -161,7 +164,7 @@ export class AilaModeration implements AilaModerationFeature { lastUserMessage?.content, ); if (mockModerationResult) { - console.log("Returning mockModerationResult", mockModerationResult); + log.info("Returning mockModerationResult", mockModerationResult); return mockModerationResult; } } diff --git a/packages/aila/src/features/moderation/moderators/MockModerator.ts b/packages/aila/src/features/moderation/moderators/MockModerator.ts index bcd3bea20..a11bbb0dc 100644 --- a/packages/aila/src/features/moderation/moderators/MockModerator.ts +++ b/packages/aila/src/features/moderation/moderators/MockModerator.ts @@ -1,7 +1,10 @@ import { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import { aiLogger } from "@oakai/logger"; import { AilaModerator, AilaModerationError } from "."; +const log = aiLogger("aila:testing"); + export class MockModerator extends AilaModerator { private _mockedResults: ModerationResult[] = []; @@ -12,7 +15,7 @@ export class MockModerator extends AilaModerator { // eslint-disable-next-line @typescript-eslint/no-unused-vars async moderate(input: string): Promise { const result = this._mockedResults.shift(); - console.log("Mock moderation: ", input, result); + log.info("Mock moderation: ", input, result); if (!result) { throw new AilaModerationError("No more mocked results"); diff --git a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts index d3d49c470..41e601977 100644 --- a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts +++ b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts @@ -4,6 +4,7 @@ import { ModerationResult, moderationResponseSchema, } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import { aiLogger } from "@oakai/logger"; import OpenAI from "openai"; import { ChatCompletion, @@ -18,6 +19,8 @@ import { } from "../../../constants"; import { AilaServices } from "../../../core"; +const log = aiLogger("aila:moderation"); + export type OpenAiModeratorArgs = { chatId: string; userId: string | undefined; @@ -121,7 +124,8 @@ export class OpenAiModerator extends AilaModerator { }, ); - console.log( + const log = aiLogger("aila:moderation:response"); + log.info( "Moderation response: ", JSON.stringify(moderationResponse, null, 2), ); @@ -171,7 +175,7 @@ export class OpenAiModerator extends AilaModerator { try { return await this._moderate(input, 0); } catch (error) { - console.log("Moderation error: ", error); + log.error("Moderation error: ", error); if (error instanceof AilaModerationError) { throw error; } diff --git a/packages/aila/src/features/persistence/adaptors/prisma/index.ts b/packages/aila/src/features/persistence/adaptors/prisma/index.ts index 60b3e3687..48fa4df82 100644 --- a/packages/aila/src/features/persistence/adaptors/prisma/index.ts +++ b/packages/aila/src/features/persistence/adaptors/prisma/index.ts @@ -3,6 +3,7 @@ import { PrismaClientWithAccelerate, prisma as globalPrisma, } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { AilaPersistence } from "../.."; import { @@ -13,6 +14,8 @@ import { import { AilaPersistedChat, chatSchema } from "../../../../protocol/schema"; import { AilaGeneration } from "../../../generation"; +const log = aiLogger("aila:persistence"); + export class AilaPrismaPersistence extends AilaPersistence { private _prisma: PrismaClientWithAccelerate; @@ -54,7 +57,7 @@ export class AilaPrismaPersistence extends AilaPersistence { async upsertChat(): Promise { const payload = this.createChatPayload(); if (!payload.id || !payload.userId) { - console.log("No ID or userId found for chat. Not persisting."); + log.info("No ID or userId found for chat. Not persisting."); return; } @@ -80,7 +83,7 @@ export class AilaPrismaPersistence extends AilaPersistence { const payload = this.createGenerationPayload(generation); if (!payload.id || !payload.userId) { - console.log( + log.info( "No ID or userId found for generation. Not persisting.", generation.id, ); diff --git a/packages/aila/src/features/rag/AilaRag.ts b/packages/aila/src/features/rag/AilaRag.ts index 0756e913e..213bba4a7 100644 --- a/packages/aila/src/features/rag/AilaRag.ts +++ b/packages/aila/src/features/rag/AilaRag.ts @@ -1,12 +1,15 @@ import { RAG } from "@oakai/core/src/rag"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { prisma as globalPrisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { AilaServices } from "../../core"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; import { LooseLessonPlan } from "../../protocol/schema"; import { minifyLessonPlanForRelevantLessons } from "../../utils/lessonPlan/minifyLessonPlanForRelevantLessons"; +const log = aiLogger("aila:rag"); + export class AilaRag { private _aila: AilaServices; private _rag: RAG; @@ -70,7 +73,7 @@ export class AilaRag { content = JSON.stringify(minifiedLessons, null, 2); } - console.log("Got RAG content, length:", content.length); + log.info("Got RAG content, length:", content.length); return content; } diff --git a/packages/aila/src/helpers/errorReporting/index.ts b/packages/aila/src/helpers/errorReporting/index.ts index 2a0265ced..6bb450243 100644 --- a/packages/aila/src/helpers/errorReporting/index.ts +++ b/packages/aila/src/helpers/errorReporting/index.ts @@ -2,8 +2,11 @@ // and instead these methods should take an Aila instance // Then, we can use the errorReporting feature instance // to report errors using the correct error reporting back-end +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +const log = aiLogger("aila:errors"); + export function reportErrorToSentry( error: unknown, { @@ -18,7 +21,7 @@ export function reportErrorToSentry( extra?: Record; }, ) { - console.error(message, error); + log.error(message, error); if (reportToSentry) { Sentry.captureMessage(message, level); diff --git a/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts b/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts index be93e7f0e..52dd9d339 100644 --- a/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts +++ b/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts @@ -2,9 +2,12 @@ // refactoring - we will probably delete it? import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; import { createOpenAIClient } from "@oakai/core/src/llm/openai"; +import { aiLogger } from "@oakai/logger"; import OpenAI from "openai"; import type { PostHog } from "posthog-node"; +const log = aiLogger("aila:llm"); + export interface OpenAICompletionWithLoggingOptions { chatId?: string; prompt?: string; @@ -69,7 +72,8 @@ export async function OpenAICompletionWithLogging( await performCompletionAndFetchMetricsPayload(payload, options); const metrics = await reportMetrics(metricsPayload); await reportCompletionAnalyticsEvent(metricsPayload); - console.log("Open AI Metrics", metrics); + + log.info("Open AI Metrics", metrics); return { completion, metrics }; } @@ -169,7 +173,7 @@ export async function reportMetrics(payload: MetricsPayload) { body: JSON.stringify(body), }); - console.log("Datadog status", result.status); + log.info("Datadog status", result.status); } return { diff --git a/packages/aila/src/protocol/jsonPatchProtocol.ts b/packages/aila/src/protocol/jsonPatchProtocol.ts index 099048e74..cb3a0e9f7 100644 --- a/packages/aila/src/protocol/jsonPatchProtocol.ts +++ b/packages/aila/src/protocol/jsonPatchProtocol.ts @@ -1,4 +1,5 @@ import { moderationCategoriesSchema } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { Operation, @@ -28,6 +29,8 @@ import { QuizSchemaWithoutLength, } from "./schema"; +const log = aiLogger("aila:protocol"); + export const PatchString = z.object({ op: z.union([z.literal("add"), z.literal("replace")]), path: z.union([ @@ -538,7 +541,7 @@ export function tryParsePart( const { type } = obj as { type: string }; // Assert the message part type is allowed if (!MessagePartDocumentSchemaByType[type as MessagePartType]) { - console.error("Invalid message part type", type); + log.error("Invalid message part type", type); return { type: "unknown", value: JSON.stringify, @@ -561,7 +564,7 @@ export function tryParsePatch(obj: object): PatchDocument | UnknownDocument { const patchDocument: PatchDocument = parsed.data; return patchDocument; } else { - console.log("Unable to parse patch", parsed, parsed.error); + log.info("Unable to parse patch", parsed, parsed.error); return { type: "unknown", value: JSON.stringify(obj), error: parsed.error }; } } @@ -643,7 +646,7 @@ export function parseMessageRow(row: string, index: number): MessagePart[] { } return result; } catch (e) { - console.error("LLM Message parsing error", e); + log.error("LLM Message parsing error", e); return [ { type: "message-part", @@ -702,15 +705,12 @@ export function extractPatches( } } catch (e) { if (e instanceof ZodError) { - console.log( - "Failed to parse patch due to Zod validation errors:", - part, - ); + log.info("Failed to parse patch due to Zod validation errors:", part); e.errors.forEach((error, index) => { - console.log(`Error ${index + 1}:`); - console.log(` Path: ${error.path.join(".")}`); - console.log(` Message: ${error.message}`); - if (error.code) console.log(` Code: ${error.code}`); + log.info(`Error ${index + 1}:`); + log.info(` Path: ${error.path.join(".")}`); + log.info(` Message: ${error.message}`); + if (error.code) log.info(` Code: ${error.code}`); // eslint-disable-next-line @typescript-eslint/no-explicit-any const errorValue = error.path.reduce((obj: any, key) => { if (obj && typeof obj === "object" && key in obj) { @@ -718,10 +718,10 @@ export function extractPatches( } return undefined; }, part); - console.error(` Invalid value:`, errorValue); + log.error(` Invalid value:`, errorValue); }); } else { - console.error("Failed to parse patch:", e); + log.error("Failed to parse patch:", e); } Sentry.withScope(function (scope) { @@ -760,7 +760,7 @@ export function applyLessonPlanPatch( if (command.type !== "patch") return lessonPlan; const patch = command.value as Operation; if (!isValidPatch(patch)) { - console.error("Invalid patch"); + log.error("Invalid patch"); return; } @@ -776,7 +776,7 @@ export function applyLessonPlanPatch( extra["operation"] = e.operation; extra["tree"] = e.tree; } - console.error("Failed to apply patch", patch, e); + log.error("Failed to apply patch", patch, e); Sentry.withScope(function (scope) { scope.setLevel("info"); Sentry.captureException(e, { extra }); @@ -801,34 +801,3 @@ export function applyLessonPlanPatch( } return updatedLessonPlan; } - -export function parseJsonSafely(jsonStr: string, logging: boolean = false) { - function log(...args: unknown[]) { - if (logging) { - console.log("JSON", ...args); - } - } - if (!jsonStr.trim().startsWith("{") || !jsonStr.includes('":')) { - log("Not JSON", jsonStr); - return null; // Early return if it doesn't look like JSON - } - while (jsonStr.length > 0) { - try { - log("Parse", { jsonStr }); - // Attempt to parse the JSON - return JSON.parse(jsonStr); - } catch (error) { - if (error instanceof SyntaxError) { - log("Syntax error, try with reduced length", error); - // If there's a syntax error, remove the last character and try again - jsonStr = jsonStr.substring(0, jsonStr.length - 1); - } else { - // If the error is not a syntax error, rethrow it - throw error; - } - } - } - - // Return null if no valid JSON could be extracted - return null; -} diff --git a/packages/api/src/export/exportAdditionalMaterialsDoc.ts b/packages/api/src/export/exportAdditionalMaterialsDoc.ts index a02597dbe..72715be6d 100644 --- a/packages/api/src/export/exportAdditionalMaterialsDoc.ts +++ b/packages/api/src/export/exportAdditionalMaterialsDoc.ts @@ -3,6 +3,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportAdditionalMaterials } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { @@ -13,6 +14,8 @@ import { reportErrorResult, } from "../router/exports"; +const log = aiLogger("exports"); + export async function exportAdditionalMaterialsDoc({ input, ctx, @@ -76,7 +79,7 @@ export async function exportAdditionalMaterialsDoc({ snapshotId: lessonSnapshot.id, userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportAdditionalMaterialsDoc", diff --git a/packages/api/src/export/exportLessonPlan.ts b/packages/api/src/export/exportLessonPlan.ts index 554954df9..444a80c6c 100644 --- a/packages/api/src/export/exportLessonPlan.ts +++ b/packages/api/src/export/exportLessonPlan.ts @@ -3,6 +3,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocLessonPlan } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { @@ -13,6 +14,8 @@ import { reportErrorResult, } from "../router/exports"; +const log = aiLogger("exports"); + export async function exportLessonPlan({ input, ctx, @@ -82,7 +85,7 @@ export async function exportLessonPlan({ lessonPlan: input.data, userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportWorksheetDocs", diff --git a/packages/api/src/export/exportLessonSlides.ts b/packages/api/src/export/exportLessonSlides.ts index 4eac1fd85..b5fc68782 100644 --- a/packages/api/src/export/exportLessonSlides.ts +++ b/packages/api/src/export/exportLessonSlides.ts @@ -3,6 +3,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportSlidesFullLesson } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { @@ -13,6 +14,8 @@ import { reportErrorResult, } from "../router/exports"; +const log = aiLogger("exports"); + export async function exportLessonSlides({ input, ctx, @@ -81,7 +84,7 @@ export async function exportLessonSlides({ snapshotId: lessonSnapshot.id, userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportWorksheetDocs", diff --git a/packages/api/src/export/exportQuizDoc.ts b/packages/api/src/export/exportQuizDoc.ts index 5b4700a66..f23f2ab0b 100644 --- a/packages/api/src/export/exportQuizDoc.ts +++ b/packages/api/src/export/exportQuizDoc.ts @@ -3,6 +3,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocQuiz } from "@oakai/exports"; import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { z } from "zod"; @@ -14,6 +15,8 @@ import { reportErrorResult, } from "../router/exports"; +const log = aiLogger("exports"); + const lessonSnapshotSchema = z.object({}).passthrough(); type LessonSnapshot = z.infer; @@ -87,7 +90,7 @@ export async function exportQuizDoc({ snapshotId: lessonSnapshot.id, userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportWorksheetDocs", diff --git a/packages/api/src/export/exportWorksheets.ts b/packages/api/src/export/exportWorksheets.ts index 0dc5c21f7..fd7093edf 100644 --- a/packages/api/src/export/exportWorksheets.ts +++ b/packages/api/src/export/exportWorksheets.ts @@ -3,6 +3,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocsWorksheet } from "@oakai/exports"; import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { @@ -13,6 +14,8 @@ import { reportErrorResult, } from "../router/exports"; +const log = aiLogger("exports"); + export async function exportWorksheets({ input, ctx, @@ -80,7 +83,7 @@ export async function exportWorksheets({ snapshotId: lessonSnapshot.id, userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportWorksheetDocs", diff --git a/packages/api/src/middleware/adminAuth.ts b/packages/api/src/middleware/adminAuth.ts index 8149f110c..0c9cba918 100644 --- a/packages/api/src/middleware/adminAuth.ts +++ b/packages/api/src/middleware/adminAuth.ts @@ -1,10 +1,12 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; -import logger from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; import { t } from "../trpc"; +const log = aiLogger("auth"); + /** * Currently this is (1) an expensive check that makes a round trip to Clerk, * and (2) a naive check that checks email domain matches Oak's. @@ -13,7 +15,7 @@ import { t } from "../trpc"; */ const isAdminMiddleware = t.middleware(async ({ next, ctx }) => { if (!ctx.auth.userId) { - logger.debug({ auth: ctx.auth, url: ctx.req.url }, `User not an admin`); + log.info({ auth: ctx.auth, url: ctx.req.url }, `User not an admin`); throw new TRPCError({ code: "UNAUTHORIZED", message: "Not an admin", @@ -27,7 +29,7 @@ const isAdminMiddleware = t.middleware(async ({ next, ctx }) => { email.emailAddress.endsWith("@thenational.academy"), ) ) { - logger.debug({ auth: ctx.auth, url: ctx.req.url }, `User not an admin`); + log.info({ auth: ctx.auth, url: ctx.req.url }, `User not an admin`); throw new TRPCError({ code: "UNAUTHORIZED", message: "Not an admin", diff --git a/packages/api/src/middleware/auth.ts b/packages/api/src/middleware/auth.ts index f8fa6a55e..00129ea5e 100644 --- a/packages/api/src/middleware/auth.ts +++ b/packages/api/src/middleware/auth.ts @@ -1,16 +1,15 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; -import logger from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; import { t } from "../trpc"; import { applyApiKeyMiddleware } from "./apiKeyAuth"; +const log = aiLogger("auth"); + export const isLoggedInMiddleware = t.middleware(async ({ next, ctx }) => { if (!ctx.auth.userId) { - logger.debug( - { auth: ctx.auth, url: ctx.req.url }, - `User not authenticated`, - ); + log.info({ auth: ctx.auth, url: ctx.req.url }, `User not authenticated`); throw new TRPCError({ code: "UNAUTHORIZED", message: "Not authenticated", diff --git a/packages/api/src/middleware/testSupport.ts b/packages/api/src/middleware/testSupport.ts index 8223add03..dca1e4eee 100644 --- a/packages/api/src/middleware/testSupport.ts +++ b/packages/api/src/middleware/testSupport.ts @@ -1,8 +1,10 @@ -import logger from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; import { publicProcedure, t } from "../trpc"; +const log = aiLogger("testing"); + /** * Middleware to limit test support routes to testable environments like Vercel previews */ @@ -21,7 +23,7 @@ const isTestMiddleware = t.middleware(async ({ next, ctx }) => { }); } - logger.error("testSupport: Not in a testable environment"); + log.error("testSupport: Not in a testable environment"); throw new TRPCError({ code: "UNAUTHORIZED", message: "Not authenticated", diff --git a/packages/api/src/router/admin.ts b/packages/api/src/router/admin.ts index 90c499741..f05ed17aa 100644 --- a/packages/api/src/router/admin.ts +++ b/packages/api/src/router/admin.ts @@ -1,5 +1,6 @@ import { Moderations, SafetyViolations } from "@oakai/core"; import { Moderation } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { z } from "zod"; import { getSessionModerations } from "../../../aila/src/features/moderation/getSessionModerations"; @@ -10,6 +11,8 @@ import { import { adminProcedure } from "../middleware/adminAuth"; import { router } from "../trpc"; +const log = aiLogger("admin"); + export const adminRouter = router({ getModerations: adminProcedure .input(z.object({ id: z.string() })) @@ -61,7 +64,7 @@ export const adminRouter = router({ const { userId: invalidatedBy } = auth; // invalidate toxic moderation - console.log("Invalidating moderation", { moderationId, invalidatedBy }); + log.info("Invalidating moderation", { moderationId, invalidatedBy }); const moderations = new Moderations(prisma); await moderations.invalidateModeration({ moderationId, @@ -69,7 +72,7 @@ export const adminRouter = router({ }); // remove associated safety violation (and potentially unban user) - console.log("Removing safety violation", { moderationId }); + log.info("Removing safety violation", { moderationId }); const safetyViolations = new SafetyViolations(prisma); await safetyViolations.removeViolationsByRecordId(moderationId); }), diff --git a/packages/api/src/router/app.ts b/packages/api/src/router/app.ts index 70095f11c..6e3264b9d 100644 --- a/packages/api/src/router/app.ts +++ b/packages/api/src/router/app.ts @@ -2,13 +2,15 @@ import { Apps } from "@oakai/core"; import { serializeApp } from "@oakai/core/src/models/serializers"; import { sendEmailRequestingMoreGenerations } from "@oakai/core/src/utils/sendEmailRequestingMoreGenerations"; import { prisma } from "@oakai/db"; -import logger from "@oakai/logger"; +import { structuredLogger as logger, aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; import { protectedProcedure } from "../middleware/auth"; import { publicProcedure, router } from "../trpc"; +const log = aiLogger("app"); + export const appRouter = router({ all: publicProcedure.query(async ({ ctx }) => { const appModel = new Apps(ctx.prisma); @@ -69,7 +71,8 @@ export const appRouter = router({ userId, }, }); - console.log("session", session); + + log.info("session", session); return session; } catch (error) { logger.error("Error creating session", error); @@ -178,7 +181,7 @@ export const appRouter = router({ avgGenerationTimeResult.map((res) => [res.name, res.value]), ); logger.info("timingsKeyedByName", timingsKeyedByName); - console.log("timingsKeyedByName", timingsKeyedByName); + log.info("timingsKeyedByName", timingsKeyedByName); return timingsKeyedByName; } catch (error) { logger.error("Error fetching timings", error); diff --git a/packages/api/src/router/cloudinary.ts b/packages/api/src/router/cloudinary.ts index ff3cb0b31..9475bc403 100644 --- a/packages/api/src/router/cloudinary.ts +++ b/packages/api/src/router/cloudinary.ts @@ -1,10 +1,13 @@ import { requestImageDescription } from "@oakai/core/src/functions/generation/imageAltTextRequestGeneration.ts/requestImageDescription"; +import { aiLogger } from "@oakai/logger"; import { v2 as cloudinary } from "cloudinary"; import { z } from "zod"; import { protectedProcedure } from "../middleware/auth"; import { router } from "../trpc"; +const log = aiLogger("cloudinary"); + cloudinary.config({ cloud_name: process.env.CLOUDINARY_CLOUD_NAME, api_key: process.env.CLOUDINARY_API_KEY, @@ -78,7 +81,8 @@ export const cloudinaryRouter = router({ const response = await cloudinary.api.update(publicId, { context: `alt=${alt}|gptGenerated=true`, }); - console.log("Response from cloudinary", response); + + log.info("Response from cloudinary", response); return true; }), }); diff --git a/packages/api/src/router/exports.ts b/packages/api/src/router/exports.ts index aa5d4e909..f191de358 100644 --- a/packages/api/src/router/exports.ts +++ b/packages/api/src/router/exports.ts @@ -15,6 +15,7 @@ import { exportableQuizAppStateSchema, } from "@oakai/exports/src/schema/input.schema"; import { DeepPartial } from "@oakai/exports/src/types"; +import { aiLogger } from "@oakai/logger"; import { LessonExportType } from "@prisma/client"; import * as Sentry from "@sentry/nextjs"; import { kv } from "@vercel/kv"; @@ -30,6 +31,8 @@ import { exportWorksheets } from "../export/exportWorksheets"; import { protectedProcedure } from "../middleware/auth"; import { router } from "../trpc"; +const log = aiLogger("exports"); + const JsonSchemaString = JSON.stringify(LessonPlanJsonSchema); const LESSON_JSON_SCHEMA_HASH = crypto @@ -361,7 +364,7 @@ export const exportsRouter = router({ snapshot: data, }); if (!existingSnapshot) { - console.log("No existing snapshot found"); + log.info("No existing snapshot found"); return; } @@ -378,7 +381,7 @@ export const exportsRouter = router({ return output; } } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -404,7 +407,7 @@ export const exportsRouter = router({ snapshot: data, }); if (!existingSnapshot) { - console.log("No existing snapshot found"); + log.info("No existing snapshot found"); return; } // find the latest export for this snapshot @@ -421,7 +424,7 @@ export const exportsRouter = router({ return output; } } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -494,7 +497,7 @@ export const exportsRouter = router({ snapshotId: "lessonSnapshot.id", userEmail, onStateChange: (state) => { - console.log(state); + log.info(state); Sentry.addBreadcrumb({ category: "exportWorksheetSlides", @@ -583,7 +586,7 @@ export const exportsRouter = router({ snapshot: data, }); if (!existingSnapshot) { - console.log("No existing snapshot found"); + log.info("No existing snapshot found"); return; } @@ -600,7 +603,7 @@ export const exportsRouter = router({ return output; } } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -647,7 +650,7 @@ export const exportsRouter = router({ snapshot: data, }); if (!existingSnapshot) { - console.log("No existing snapshot found"); + log.info("No existing snapshot found"); return; } @@ -664,7 +667,7 @@ export const exportsRouter = router({ return output; } } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -713,7 +716,7 @@ export const exportsRouter = router({ snapshot: lessonSnapshot, }); if (!existingSnapshot) { - console.log("No existing snapshot found"); + log.info("No existing snapshot found"); return; } @@ -733,7 +736,7 @@ export const exportsRouter = router({ return output; } } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -754,7 +757,7 @@ export const exportsRouter = router({ try { return await exportLessonPlan({ input, ctx }); } catch (error) { - console.error("Error checking if download exists:", error); + log.error("Error checking if download exists:", error); const message = "Failed to check if download exists"; return { error, @@ -848,7 +851,7 @@ export const exportsRouter = router({ return allExports; } catch (error) { - console.error("Error generating all asset exports:", error); + log.error("Error generating all asset exports:", error); return { error, message: "Failed to generate all asset exports", @@ -884,7 +887,7 @@ export const exportsRouter = router({ } = input; if (!userEmail) { - console.error("User email not found"); + log.error("User email not found"); return false; } @@ -914,7 +917,7 @@ Oak National Academy`, return emailSent ? true : false; } catch (error) { - console.error("Error sending email:", error); + log.error("Error sending email:", error); return false; } }), @@ -934,7 +937,7 @@ Oak National Academy`, const { title, link, lessonTitle } = input; if (!userEmail) { - console.error("User email not found"); + log.error("User email not found"); return false; } @@ -958,7 +961,7 @@ Oak National Academy`, return emailSent ? true : false; } catch (error) { - console.error("Error sending email:", error); + log.error("Error sending email:", error); return false; } }), @@ -966,8 +969,8 @@ Oak National Academy`, .input(z.object({ id: z.string() })) .mutation(async ({ input }) => { const data = await kv.get(input.id); - console.log("***id", input.id); - console.log("***data", data); + log.info("***id", input.id); + log.info("***data", data); return data; }), checkDownloadAllStatus: protectedProcedure diff --git a/packages/api/src/router/generations.ts b/packages/api/src/router/generations.ts index 66c12399a..906ec75b7 100644 --- a/packages/api/src/router/generations.ts +++ b/packages/api/src/router/generations.ts @@ -13,7 +13,8 @@ import { } from "@oakai/core/src/types"; import { sendQuizFeedbackEmail } from "@oakai/core/src/utils/sendQuizFeedbackEmail"; import { requestGenerationWorker } from "@oakai/core/src/workers/generations/requestGeneration"; -import logger from "@oakai/logger"; +import { structuredLogger as logger } from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; import { Redis } from "@upstash/redis"; import { waitUntil } from "@vercel/functions"; @@ -26,6 +27,8 @@ import { userBasedRateLimitProcedure } from "../middleware/rateLimiter"; import { router } from "../trpc"; import { rateLimitInfoSchema } from "../types"; +const log = aiLogger("generation"); + const redis = new Redis({ url: process.env.KV_REST_API_URL as string, token: process.env.KV_REST_API_TOKEN as string, @@ -217,10 +220,7 @@ export const generationRouter = router({ * differently */ if (promptSlug.includes("regenerate-")) { - logger.info( - "Logging re-generation for generation %s", - lastGenerationId, - ); + log.info("Logging re-generation for generation %s", lastGenerationId); if (lastGenerationId) { await feedbackModel.recordReGeneration( lastGenerationId, @@ -228,7 +228,7 @@ export const generationRouter = router({ generation.id, ); } else { - logger.error( + log.info( "User tried to trigger re-generation generation but did not provide a lastGenerationId", ); } @@ -240,7 +240,7 @@ export const generationRouter = router({ ); } - console.log("Generation complete"); + log.info("Generation complete"); return { generation: serializeGeneration(generation), @@ -323,7 +323,7 @@ export const generationRouter = router({ generationResponse: JSON.stringify(flaggedGenerationResponse?.response), }); - logger.debug( + log.info( "Giving feedback for generation %s", flaggedItem.lastGenerationId, ); @@ -361,7 +361,7 @@ export const generationRouter = router({ ) .mutation(async ({ ctx, input }) => { const { tweakedItem, sessionId } = input; - logger.info( + log.info( "Logging user tweak for generation %s", tweakedItem.lastGenerationId, ); diff --git a/packages/api/src/router/judgements.ts b/packages/api/src/router/judgements.ts index 9aee531d1..a76aedc1a 100644 --- a/packages/api/src/router/judgements.ts +++ b/packages/api/src/router/judgements.ts @@ -1,11 +1,13 @@ import { KeyStageName, SubjectName, subjectsAndKeyStages } from "@oakai/core"; import { sendJudgementFeedbackEmail } from "@oakai/core/src/utils/sendJudgementFeedbackEmail"; -import logger from "@oakai/logger"; +import { structuredLogger as logger, aiLogger } from "@oakai/logger"; import { z } from "zod"; import { protectedProcedure } from "../middleware/auth"; import { router } from "../trpc"; +const log = aiLogger("judgements"); + interface QuestionForJudgement { questionForJudgement: { keyStage: { @@ -246,7 +248,7 @@ export const judgementRouter = router({ }, }, }); - console.log("Flagged in comparative judgement successfully"); + log.info("Flagged in comparative judgement successfully"); } catch (err) { logger.error("User tried to flag or skip a question", err); return err; diff --git a/packages/api/src/router/moderations.ts b/packages/api/src/router/moderations.ts index d62746c0a..6867a14c3 100644 --- a/packages/api/src/router/moderations.ts +++ b/packages/api/src/router/moderations.ts @@ -1,9 +1,12 @@ +import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/node"; import { z } from "zod"; import { protectedProcedure } from "../middleware/auth"; import { router } from "../trpc"; +const log = aiLogger("moderation"); + export const moderationsRouter = router({ userComment: protectedProcedure .input( @@ -30,7 +33,7 @@ export const moderationsRouter = router({ Sentry.captureException(e, { extra: { comment, moderationId, userId }, }); - console.error("Error", e); + log.error("Error", e); } }), moderationById: protectedProcedure diff --git a/packages/api/src/router/testSupport/prepareUser.ts b/packages/api/src/router/testSupport/prepareUser.ts index 25e85f699..97e1a33c7 100644 --- a/packages/api/src/router/testSupport/prepareUser.ts +++ b/packages/api/src/router/testSupport/prepareUser.ts @@ -1,4 +1,5 @@ import { clerkClient } from "@clerk/nextjs/server"; +import { aiLogger } from "@oakai/logger"; import os from "os"; import { z } from "zod"; @@ -6,6 +7,8 @@ import { publicProcedure } from "../../trpc"; import { setSafetyViolations } from "./safetyViolations"; import { seedChat } from "./seedChat"; +const log = aiLogger("testing"); + const branch = process.env.VERCEL_GIT_COMMIT_REF ?? os.hostname(); const personaNames = [ @@ -90,7 +93,7 @@ const findOrCreateUser = async ( return existingUser; } - console.log("Creating test user", { email }); + log.info("Creating test user", { email }); const newUser = await clerkClient.users.createUser({ emailAddress: [email], firstName: branch, diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index f59cc6b7f..6b84936e8 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -1,13 +1,16 @@ +import { aiLogger } from "@oakai/logger"; import { initTRPC } from "@trpc/server"; import { ZodError } from "zod"; import { transformer } from "../transformer"; import { type Context } from "./context"; +const log = aiLogger("trpc"); + export const t = initTRPC.context().create({ transformer: transformer, errorFormatter({ shape, error }) { - console.error("trpc error", { shape, error }); + log.error("trpc error", { shape, error }); // this shouldn't happen before landing in production, but // by putting this ahead of the generic catch all ISE500 handler diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index 92913f915..b255c1b71 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -1,4 +1,4 @@ -import logger from "@oakai/logger"; +import { structuredLogger } from "@oakai/logger"; import { EventSchemas, Inngest } from "inngest"; import getConfig from "next/config"; @@ -43,7 +43,7 @@ export const inngest = new Inngest({ schemas: new EventSchemas().fromZod(types), eventKey: inngestEventKey, env: inngestEnv, - logger: logger, + logger: structuredLogger, middleware: [eventLogger(inngestEnv, inngestEventKey)], isDev: process.env.NODE_ENV === "development" && !isJestEnvironment(), }); diff --git a/packages/core/src/functions/demo/populateDemoStatuses.ts b/packages/core/src/functions/demo/populateDemoStatuses.ts index 53c007806..92259733b 100644 --- a/packages/core/src/functions/demo/populateDemoStatuses.ts +++ b/packages/core/src/functions/demo/populateDemoStatuses.ts @@ -1,8 +1,12 @@ import { clerkClient } from "@clerk/nextjs/server"; +import { aiLogger } from "@oakai/logger"; import { inngest } from "../../client"; import { populateDemoStatusesSchema } from "./populateDemoStatuses.schema"; +const log = aiLogger("demo"); +log.info.enabled = true; + export const populateDemoStatuses = inngest.createFunction( { name: "Populate demo statuses", @@ -14,11 +18,11 @@ export const populateDemoStatuses = inngest.createFunction( const DRY_RUN = args.dryRun; if (DRY_RUN) { - console.log("Running in dry run mode"); + log.info("Running in dry run mode"); } async function updateUser(id: string) { - console.log("Updating user", id); + log.info("Updating user", id); if (!DRY_RUN) { await clerkClient.users.updateUserMetadata(id, { @@ -38,13 +42,13 @@ export const populateDemoStatuses = inngest.createFunction( offset, orderBy: "created_at", }); - console.log(`${data.length} records fetched at ${offset} offset`); + log.info(`${data.length} records fetched at ${offset} offset`); if (data.length === 0) { return { continue: false as const }; } - console.log(data.map((user) => user.publicMetadata.isDemoUser)); + log.info(data.map((user) => user.publicMetadata.isDemoUser)); await Promise.all( data @@ -67,6 +71,6 @@ export const populateDemoStatuses = inngest.createFunction( } } - console.log("Done"); + log.info("Done"); }, ); diff --git a/packages/core/src/functions/generation/imageAltTextRequestGeneration.ts/requestImageDescription.ts b/packages/core/src/functions/generation/imageAltTextRequestGeneration.ts/requestImageDescription.ts index a367ef82c..a4fa79d63 100644 --- a/packages/core/src/functions/generation/imageAltTextRequestGeneration.ts/requestImageDescription.ts +++ b/packages/core/src/functions/generation/imageAltTextRequestGeneration.ts/requestImageDescription.ts @@ -1,5 +1,9 @@ +import { aiLogger } from "@oakai/logger"; + import { createOpenAIClient } from "../../../llm/openai"; +const log = aiLogger("generation"); + const openai = createOpenAIClient({ app: "image-alt-text" }); const prompt = `You are a content creator and an accessibility specialist for visually impaired users. You must write a clear and accurate ALT text for this image. You should accurately describe the image foreground and background, the colours and any objects in the image. Write out any text if it is present, and describe any people, their clothes, and their expressions if they are present. The literacy level of the text should be understandable to a 6-year-old pupil and written with exceptional spelling, punctuation and grammar in British English, think about the language that is understandable to this age group. Do not exceed 300 characters. If you believe you can accurately complete the task in less than 300 characters than do so. `; @@ -20,11 +24,11 @@ export async function requestImageDescription( inQuizMode?: boolean, ) { try { - console.log("Requesting image description..."); + log.info("Requesting image description..."); // Assuming you have a loading state variable to manage the loading state // Set loading state to true"openai": "^4.24.1", - console.log("Loading"); + log.info("Loading"); const promptInUse = inQuizMode ? promptInQuizMode : prompt; @@ -57,12 +61,12 @@ export async function requestImageDescription( ], }); - console.log("Image description received:", response); - console.log("Image description received:", response.choices[0]); + log.info("Image description received:", response); + log.info("Image description received:", response.choices[0]); return response.choices[0]?.message.content; } } catch (error) { - console.error("Error requesting image description:", error); + log.error("Error requesting image description:", error); } } diff --git a/packages/core/src/functions/lesson/generatePlan.ts b/packages/core/src/functions/lesson/generatePlan.ts index 6a5b18e3c..c3d0c9264 100644 --- a/packages/core/src/functions/lesson/generatePlan.ts +++ b/packages/core/src/functions/lesson/generatePlan.ts @@ -1,8 +1,11 @@ import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { inngest } from "../../client"; import { LessonPlans } from "../../models"; +const log = aiLogger("lessons"); + export const generatePlanForLesson = inngest.createFunction( { name: "Generate a lesson plan based a lesson and store it in the database", @@ -14,7 +17,7 @@ export const generatePlanForLesson = inngest.createFunction( await step.run("Create a plan for a lesson", async () => { const result = await new LessonPlans(prisma).createFromLesson(lessonId); - console.log("result", result); + log.info("result", result); return result?.id; }); }, diff --git a/packages/core/src/functions/lesson/summarise.ts b/packages/core/src/functions/lesson/summarise.ts index 197e4576f..42e43fb09 100644 --- a/packages/core/src/functions/lesson/summarise.ts +++ b/packages/core/src/functions/lesson/summarise.ts @@ -1,8 +1,11 @@ import { LessonSummaryStatus, prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { inngest } from "../../client"; import { Lessons } from "../../models"; +const log = aiLogger("lessons"); + export const summariseLesson = inngest.createFunction( { name: "Generate a summary of a lesson and store it in the database", @@ -16,7 +19,7 @@ export const summariseLesson = inngest.createFunction( "Get an OpenAI summarisation for a lesson", async () => { const result = await new Lessons(prisma).summarise(lessonId); - console.log("result", result); + log.info("result", result); return result?.id; }, ); diff --git a/packages/core/src/functions/lessonPlan/embedAll.ts b/packages/core/src/functions/lessonPlan/embedAll.ts index 8d084a6e6..c7a09a9f3 100644 --- a/packages/core/src/functions/lessonPlan/embedAll.ts +++ b/packages/core/src/functions/lessonPlan/embedAll.ts @@ -1,8 +1,11 @@ import { LessonPlanStatus, prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { inngest } from "../../client"; import { LessonPlans } from "../../models"; +const log = aiLogger("lessons"); + export const embedAllLessonPlans = inngest.createFunction( { name: "Generate embeddings for all lesson plans that do not have them", @@ -22,7 +25,7 @@ export const embedAllLessonPlans = inngest.createFunction( }, ); - console.log("planIds", planIds); + log.info("planIds", planIds); for (const planId of planIds) { await step.run("Get OpenAI embeddings of the lesson plan", async () => { diff --git a/packages/core/src/middleware/eventLogger.ts b/packages/core/src/middleware/eventLogger.ts index 8229deaab..3d2089094 100644 --- a/packages/core/src/middleware/eventLogger.ts +++ b/packages/core/src/middleware/eventLogger.ts @@ -1,6 +1,8 @@ -import logger from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { InngestMiddleware } from "inngest"; +const log = aiLogger("inngest"); + /** * Log out the names and some meta of all events sent to inngest * from the client @@ -16,7 +18,7 @@ export function eventLogger(env: string, eventKey: string) { return { transformInput({ payloads }): void { for (const payload of payloads) { - logger.debug( + log.info( { inngestClientId: client.id, inngestEventKey: lazyRedact(eventKey, 5), diff --git a/packages/core/src/models/generations.ts b/packages/core/src/models/generations.ts index 58c430770..699cbca71 100644 --- a/packages/core/src/models/generations.ts +++ b/packages/core/src/models/generations.ts @@ -5,7 +5,7 @@ import { Prisma, PrismaClientWithAccelerate, } from "@oakai/db"; -import defaultLogger, { Logger } from "@oakai/logger"; +import { structuredLogger, StructuredLogger } from "@oakai/logger"; import { Logger as InngestLogger } from "inngest/middleware/logger"; import { omit } from "remeda"; import { Md5 } from "ts-md5"; @@ -22,7 +22,9 @@ export class Generations { private readonly prisma: PrismaClientWithAccelerate, // inngest's logger doesn't allow child logger creation, so make // sure we accept instances of that too - private readonly logger: Logger | InngestLogger = defaultLogger, + private readonly logger: + | StructuredLogger + | InngestLogger = structuredLogger, ) {} async byId(generationId: string): Promise { diff --git a/packages/core/src/models/lessonPlans.ts b/packages/core/src/models/lessonPlans.ts index 8ac956055..fb8b5b729 100644 --- a/packages/core/src/models/lessonPlans.ts +++ b/packages/core/src/models/lessonPlans.ts @@ -9,6 +9,7 @@ import { PrismaClientWithAccelerate, Subject, } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import yaml from "yaml"; import { LLMResponseJsonSchema } from "../../../aila/src/protocol/jsonPatchProtocol"; @@ -21,6 +22,8 @@ import { camelCaseToSentenceCase } from "../utils/camelCaseToSentenceCase"; import { embedWithCache } from "../utils/embeddings"; import { Caption, CaptionsSchema } from "./types/caption"; +const log = aiLogger("lessons"); + // Simplifies the input to a string for embedding export function textify(input: string | string[] | object): string { if (Array.isArray(input)) { @@ -156,7 +159,7 @@ export class LessonPlans { try { validCaptions = CaptionsSchema.parse(lesson.captions); } catch (err) { - console.error("Failed to parse captions", err); + log.error("Failed to parse captions", err); } const captionText = validCaptions.map((c) => c.part).join(" "); @@ -227,7 +230,7 @@ export class LessonPlans { const content = await this.generateContent(lessonPlan.id); - console.log("Generated content", content); + log.info("Generated content", content); if (!content) { throw new Error("Unable to generate lesson summary"); } diff --git a/packages/core/src/models/lessons.ts b/packages/core/src/models/lessons.ts index 4d2bdb731..b76e51d52 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -9,6 +9,7 @@ import { ZLesson, } from "@oakai/db"; import { ZNewLesson } from "@oakai/db/schemas/lesson"; +import { aiLogger } from "@oakai/logger"; import { StructuredOutputParser } from "langchain/output_parsers"; import { PromptTemplate } from "langchain/prompts"; import { RunnableSequence } from "langchain/schema/runnable"; @@ -19,6 +20,8 @@ import { createOpenAILangchainClient } from "../llm/openai"; import { SnippetWithLesson, Snippets } from "./snippets"; import { Caption, CaptionsSchema } from "./types/caption"; +const log = aiLogger("lessons"); + const EMBED_AFTER_CREATION = false; function delay(ms: number) { @@ -113,8 +116,8 @@ export class Lessons { if (!lesson) { throw new Error("Lesson not found"); } - console.log("Summarising lesson", lesson.slug); - console.log("new lesson", lesson.isNewLesson); + log.info("Summarising lesson", lesson.slug); + log.info("new lesson", lesson.isNewLesson); let zLesson; let description; let transcript; @@ -218,7 +221,7 @@ export class Lessons { format_instructions: parser.getFormatInstructions(), }); - console.log(response); + log.info(response); const { summary, topics, learningObjectives, concepts, keywords } = response; @@ -253,7 +256,7 @@ export class Lessons { }, }); - console.log("Created quiz question", quizQuestion); + log.info("Created quiz question", quizQuestion); await inngest.send({ name: "app/quizQuestion.embed", data: { quizQuestionId: quizQuestion.id }, @@ -275,7 +278,7 @@ export class Lessons { const questionString: string = question.description ? `${questionTitleWithPunctuation} ${question.description}` : title; - console.log("Create question", question, lesson.id); + log.info("Create question", question, lesson.id); let quizQuestionId: string | undefined; const existingQuestion = await this.prisma.quizQuestion.findFirst({ @@ -305,7 +308,7 @@ export class Lessons { lessonId: string; quizQuestionId: string; }) { - console.log("Create answer", answer, lessonId); + log.info("Create answer", answer, lessonId); const existingAnswer = await this.prisma.quizAnswer.findFirst({ where: { answer, lessonId, questionId: quizQuestionId }, }); @@ -321,10 +324,10 @@ export class Lessons { }, }); quizAnswerId = quizAnswer.id; - console.log("Created quiz question answer", quizAnswer); + log.info("Created quiz question answer", quizAnswer); } catch (e) { // For now, swallow the error until we can change the unique index - console.log(e); + log.error(e); } if (quizAnswerId && EMBED_AFTER_CREATION) { @@ -384,18 +387,18 @@ export class Lessons { }, }); - console.log("Created transcript", transcript); - console.log("With captions", lesson.captions); + log.info("Created transcript", transcript); + log.info("With captions", lesson.captions); let validCaptions: Caption[]; try { validCaptions = CaptionsSchema.parse(lesson.captions); } catch (err) { - console.error("Failed to parse captions", err); + log.error("Failed to parse captions", err); return transcript; } let index = 1; for (const caption of validCaptions) { - console.log("Creating snippet", caption); + log.info("Creating snippet", caption); const snippetAttributes = { sourceContent: caption.part, content: caption.part, @@ -414,9 +417,9 @@ export class Lessons { data: snippetAttributes, }); } catch (err) { - console.error("Failed to create snippet", err); + log.error("Failed to create snippet", err); } - console.log("Created snippet", snippet); + log.info("Created snippet", snippet); if (snippet && EMBED_AFTER_CREATION) { await inngest.send({ name: "app/snippet.embed", diff --git a/packages/core/src/models/promptVariants.ts b/packages/core/src/models/promptVariants.ts index 7fb14b1df..babd890b9 100644 --- a/packages/core/src/models/promptVariants.ts +++ b/packages/core/src/models/promptVariants.ts @@ -5,6 +5,9 @@ import { Md5 } from "ts-md5"; import errorHandling from "../prompts/shared/error-handling"; import promptInjection from "../prompts/shared/prompt-injection"; import { OakPromptDefinition, OakPromptVariant } from "../prompts/types"; +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("prompts") export class PromptVariants { definition: OakPromptDefinition; @@ -37,7 +40,7 @@ export class PromptVariants { if (existing) { return existing; } - console.log( + log.info( `Storing new prompt version for ${slug} / ${variant} with hash ${hash}`, ); diff --git a/packages/core/src/models/prompts.ts b/packages/core/src/models/prompts.ts index ec7b7d4b0..467855ca2 100644 --- a/packages/core/src/models/prompts.ts +++ b/packages/core/src/models/prompts.ts @@ -1,5 +1,5 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; -import defaultLogger, { Logger } from "@oakai/logger"; +import { structuredLogger, StructuredLogger } from "@oakai/logger"; import { Logger as InngestLogger } from "inngest/middleware/logger"; import { PromptTemplate } from "langchain/prompts"; import { BaseMessage, SystemMessage } from "langchain/schema"; @@ -33,7 +33,9 @@ export class Prompts { private readonly prisma: PrismaClientWithAccelerate, // inngest's logger doesn't allow child logger creation, so make // sure we accept instances of that too - private readonly logger: Logger | InngestLogger = defaultLogger, + private readonly logger: + | StructuredLogger + | InngestLogger = structuredLogger, ) {} async get(promptId: string, appId: string) { diff --git a/packages/core/src/models/safetyViolations.ts b/packages/core/src/models/safetyViolations.ts index a8290badb..d2ceeec5e 100644 --- a/packages/core/src/models/safetyViolations.ts +++ b/packages/core/src/models/safetyViolations.ts @@ -5,7 +5,7 @@ import { SafetyViolationSource, PrismaClientWithAccelerate, } from "@oakai/db"; -import defaultLogger, { Logger } from "@oakai/logger"; +import { structuredLogger, StructuredLogger } from "@oakai/logger"; import { Logger as InngestLogger } from "inngest/middleware/logger"; import { posthogAiBetaServerClient } from "../analytics/posthogAiBetaServerClient"; @@ -41,7 +41,9 @@ export class SafetyViolations { private readonly prisma: PrismaClientWithAccelerate, // inngest's logger doesn't allow child logger creation, so make // sure we accept instances of that too - private readonly logger: Logger | InngestLogger = defaultLogger, + private readonly logger: + | StructuredLogger + | InngestLogger = structuredLogger, ) {} async recordViolation( diff --git a/packages/core/src/models/snippets.ts b/packages/core/src/models/snippets.ts index 16772eb12..0dd5b0465 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -5,6 +5,7 @@ import { SnippetStatus, SnippetVariant, } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { LLMChain, RetrievalQAChain } from "langchain/chains"; import { OpenAIEmbeddings } from "langchain/embeddings/openai"; import { @@ -26,6 +27,8 @@ import { inngest } from "../client"; import { createOpenAILangchainClient } from "../llm/openai"; import { embedWithCache } from "../utils/embeddings"; +const log = aiLogger("snippets"); + export type DisplayFormat = "plain" | "markdown"; export type SnippetWithLesson = Snippet & { @@ -165,7 +168,7 @@ If you don't know the answer, just respond with "None", don't try to make up an const theAnswer = await chain.invoke(question); - console.log("Generated this answer to the question", question, theAnswer); + log.info("Generated this answer to the question", question, theAnswer); return theAnswer; } @@ -340,7 +343,7 @@ Respond to the prompt as if it is the main prompt you are being given rather tha prompt: template, }); const res = await chain.call({ prompt, snippetText, displayFormatPrompt }); - console.log("Prompt result", { + log.info("Prompt result", { res, prompt, snippetText, @@ -372,14 +375,14 @@ Respond to the prompt as if it is the main prompt you are being given rather tha }); if (snippets.length > 0) { - console.log("Already has a snippet. Exiting"); + log.info("Already has a snippet. Exiting"); return; } const correctAnswer = question.answers.find((a) => !a.distractor); if (!correctAnswer) { - console.log("Cannot find the correct answer. Exiting"); + log.info("Cannot find the correct answer. Exiting"); return; } diff --git a/packages/core/src/models/transcript.ts b/packages/core/src/models/transcript.ts index 1ae58cf1e..b84d47cda 100644 --- a/packages/core/src/models/transcript.ts +++ b/packages/core/src/models/transcript.ts @@ -7,6 +7,9 @@ import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { z } from "zod"; import { createOpenAILangchainClient } from "../llm/openai"; +import { aiLogger } from "@oakai/logger"; + +const log = aiLogger("transcripts") interface TranscriptWithRaw { raw: string; @@ -73,7 +76,7 @@ export class Transcripts { }, }); - console.log("Schedule snippet embedding", { + log.info("Schedule snippet embedding", { snippetId: createdSnippet.id, }); @@ -120,14 +123,14 @@ export class Transcripts { Do not include any information about the speaker in your response. Do not extend the length of the snippet beyond the length of the original sentence. Do not include information not covered in the lesson in your snippets. - + Response: Your response should be a JSON document containing an array of strings, each of which is a single sentence or a few sentences. Do not respond with a cut off or incomplete JSON document. Your response should match the following schema definition: {format_instructions}`; - console.log(template); + log.info(template); const parser = StructuredOutputParser.fromZodSchema( z.object({ @@ -149,7 +152,7 @@ export class Transcripts { const format_instructions = parser.getFormatInstructions(); - console.log("Format instructions", format_instructions); + log.info("Format instructions", format_instructions); const transcript_text = (transcript.content as unknown as TranscriptWithRaw) .raw; @@ -158,7 +161,7 @@ export class Transcripts { format_instructions, }); - console.log("Got response", { response }); + log.info("Got response", { response }); const { snippets } = response; diff --git a/packages/core/src/rag/index.ts b/packages/core/src/rag/index.ts index 026890a81..1967a3d3f 100644 --- a/packages/core/src/rag/index.ts +++ b/packages/core/src/rag/index.ts @@ -6,6 +6,7 @@ import { PrismaClientWithAccelerate, Subject, } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { LessonPlan, LessonSummary, @@ -31,6 +32,8 @@ import { JsonValue } from "../models/prompts"; import { slugify } from "../utils/slugify"; import { keyStages, subjects } from "../utils/subjects"; +const log = aiLogger("rag"); + interface FilterOptions { key_stage_id?: object; subject_id?: object; @@ -87,7 +90,7 @@ export class RAG { input: string, chatMeta: OpenAICompletionWithLoggingOptions, ) { - console.log("Categorise input", JSON.stringify(input)); + log.info("Categorise input", JSON.stringify(input)); const systemMessage = `You are a classifier which can help me categorise the intent of the user's input for an application which helps a teacher build a lesson plan their students in a UK school. You accept a string as input and return an object with the keys keyStage, subject, title and topic. @@ -179,7 +182,7 @@ Thank you and happy classifying!`; const parsedResponse = CategoriseKeyStageAndSubjectResponse.parse( JSON.parse(content), ); - console.log("Categorisation results", parsedResponse); + log.info("Categorisation results", parsedResponse); return parsedResponse; } catch (e) { return { error: "Error parsing response" }; @@ -268,10 +271,10 @@ Thank you and happy classifying!`; const hash = Md5.hashStr(hashInput); const key = `${keyPrefix}:${hash}`; const result: string | object | null = await kv.get(key); - console.log("getCachedValueByHash: Got result!", result); + log.info("getCachedValueByHash: Got result!", result); return result; } catch (e) { - console.error("Error getting cached value by hash", e); + log.error("Error getting cached value by hash", e); throw e; } } @@ -280,20 +283,20 @@ Thank you and happy classifying!`; keyPrefix: string, hashInput: string, ): Promise { - console.log("getCachedValueByHash", keyPrefix, hashInput); + log.info("getCachedValueByHash", keyPrefix, hashInput); const cachedValue = await this.getCachedValueByHash(keyPrefix, hashInput); - console.log("Got cached value", { cachedValue }); + log.info("Got cached value", { cachedValue }); if ( typeof cachedValue === "string" && cachedValue?.includes("[object Object") ) { - console.log("Removing cached value because it has badly serialised"); + log.info("Removing cached value because it has badly serialised"); await this.removeCachedValueByHash(keyPrefix, hashInput); return null; } if (cachedValue) { - console.log("Got cached serialised by hash", { cachedValue }); - console.log("Typeof cachedValue", typeof cachedValue); + log.info("Got cached serialised by hash", { cachedValue }); + log.info("Typeof cachedValue", typeof cachedValue); if (typeof cachedValue === "string") { return JSON.parse(cachedValue) as T; } @@ -312,7 +315,7 @@ Thank you and happy classifying!`; value: T, options = {}, ): Promise { - console.log("Set cached serialised by hash", { + log.info("Set cached serialised by hash", { keyPrefix, hashInput, //value, @@ -386,19 +389,19 @@ Thank you and happy classifying!`; }): Promise { const cacheHash = `${keyStage}-${subject}-${title}-${topic}-${k}`; const cacheKey = `rag:plans:${chatId}`; - console.log("getCachedSerialisedByHash"); + log.info("getCachedSerialisedByHash"); const cached = await this.getCachedSerialisedByHash( cacheKey, cacheHash, ); if (cached && withCaching) { - console.log("normaliseLessonPlan"); + log.info("normaliseLessonPlan"); return cached; //.map((p) => this.normaliseLessonPlan(p)); } if (cached && !withCaching) { - console.log("Fetch Lessons: Cache hit but not using cache", { + log.info("Fetch Lessons: Cache hit but not using cache", { cacheKey, cacheHash, }); @@ -416,11 +419,11 @@ Thank you and happy classifying!`; k, }); } catch (e) { - console.error(e); + log.error(e); Sentry.captureException(e); } - console.log("Got plans", { planIds: plans.map((p) => p.id) }); + log.info("Got plans", { planIds: plans.map((p) => p.id) }); if (plans.length === 0 || !plans?.filter) { return []; // Do not cache empty results @@ -539,15 +542,15 @@ Thank you and happy classifying!`; try { const cached = await kv.get(cacheKey); if (cached) { - console.log(`Cache hit for key: ${cacheKey}`); + log.info(`Cache hit for key: ${cacheKey}`); return cached; } } catch (e) { - console.error(`Error getting cached value for key: ${cacheKey}`, e); + log.error(`Error getting cached value for key: ${cacheKey}`, e); await kv.del(cacheKey); // Remove potentially corrupted cache entry } - console.log(`Cache miss for key: ${cacheKey}, fetching from Prisma`); + log.info(`Cache miss for key: ${cacheKey}, fetching from Prisma`); const record = await fetcher(); if (record) { await kv.set(cacheKey, record, { ex: cacheExpiry }); @@ -598,7 +601,7 @@ Thank you and happy classifying!`; return cachedKeyStage; } } catch (e) { - console.error( + log.error( "Error parsing cached keyStage. Continuing without cached value", e, ); @@ -652,7 +655,7 @@ Thank you and happy classifying!`; return cachedSubject; } } catch (e) { - console.error( + log.error( "Error parsing cached subject. Continuing without cached value", e, ); @@ -677,7 +680,7 @@ Thank you and happy classifying!`; // If none of that works, fall back to categorising the subject based on free text if (!foundSubject) { - // console.log( + // log.info( // "No subject found. Categorise the input to try to work out what it is using categoriseKeyStageAndSubject", // ); const categorisation = await this.categoriseKeyStageAndSubject( @@ -764,7 +767,7 @@ Thank you and happy classifying!`; ); } catch (e) { if (e instanceof TypeError && e.message.includes("join([])")) { - console.warn("Caught TypeError with join([]), returning empty array"); + log.warn("Caught TypeError with join([]), returning empty array"); return []; } throw e; diff --git a/packages/core/src/tracing/baseTracing.ts b/packages/core/src/tracing/baseTracing.ts index 0cb758d68..8dbc68bc6 100644 --- a/packages/core/src/tracing/baseTracing.ts +++ b/packages/core/src/tracing/baseTracing.ts @@ -1,5 +1,8 @@ +import { aiLogger } from "@oakai/logger"; import tracer from "dd-trace"; +const log = aiLogger("tracing"); + export const environment = process.env.NODE_ENV || "development"; export const isTest = environment === "test"; export const isLocalDev = environment === "development"; @@ -38,7 +41,7 @@ export function initializeTracer(options: DatadogOptions) { plugins: false, }); } else { - const initialisationOptions = { + const initialisationOptions: tracer.TracerOptions = { env: options.env ?? environment, service: options.service ?? "oak-ai", hostname, @@ -49,17 +52,16 @@ export function initializeTracer(options: DatadogOptions) { sampleRate: options.sampleRate ?? 1, profiling: options.profiling !== undefined ? options.profiling : true, plugins: options.plugins !== undefined ? options.plugins : false, - debug: true, logLevel, logger: { debug: (message: string | Error) => - console.debug(`[dd-trace debug] ${message}`), + log.info(`[dd-trace debug] ${message}`), info: (message: string | Error) => - console.info(`[dd-trace info] ${message}`), + log.info(`[dd-trace info] ${message}`), warn: (message: string | Error) => - console.warn(`[dd-trace warn] ${message}`), + log.info(`[dd-trace warn] ${message}`), error: (message: string | Error) => - console.error(`[dd-trace error] ${message}`), + log.error(`[dd-trace error] ${message}`), }, }; console.log( diff --git a/packages/core/src/utils/getCaptionsFromFile.ts b/packages/core/src/utils/getCaptionsFromFile.ts index c48cd845b..ca934a4ca 100644 --- a/packages/core/src/utils/getCaptionsFromFile.ts +++ b/packages/core/src/utils/getCaptionsFromFile.ts @@ -1,6 +1,9 @@ import { Storage } from "@google-cloud/storage"; +import { aiLogger } from "@oakai/logger"; import { Cue, WebVTTParser } from "webvtt-parser"; +const log = aiLogger("transcripts"); + const key = process.env.GOOGLE_STORAGE_BUCKET_KEY; class GoogleStorageError extends Error { @@ -65,7 +68,7 @@ export const getCaptionsFromFile = async (fileName: string) => { const tree = parser.parse(file, "metadata"); if (tree.errors.length) { - console.error( + log.error( `Error parsing captions file: ${fileName}, errors: ${JSON.stringify( tree.errors, )}`, @@ -120,7 +123,7 @@ export async function getFileFromBucket(bucketName: string, fileName: string) { const contents = await bucket.file(fileName).download(); return contents.toString(); } catch (err) { - console.error( + log.error( `Error fetching file: ${fileName} from bucket: ${bucketName}. Error: ${err}`, ); return; diff --git a/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts b/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts index 8dc94df9d..9225774d9 100644 --- a/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts +++ b/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts @@ -1,8 +1,10 @@ import { clerkClient, User } from "@clerk/nextjs/server"; -import { default as oakLogger } from "@oakai/logger"; +import { aiLogger } from "@oakai/logger"; import { Ratelimit } from "@upstash/ratelimit"; import { waitUntil } from "@vercel/functions"; +const log = aiLogger("rate-limiting"); + // NOTE: Duplicates RateLimitInfo in packages/api/src/types.ts export type RateLimitInfo = | { @@ -34,8 +36,6 @@ export class RateLimitExceededError extends Error { } } -const logger = process.browser ? console : oakLogger; - /** * Function to create a user-based rate limiter with a given rate limit * @returns A function enforcing user rate limits @@ -53,7 +53,7 @@ export const userBasedRateLimiter = (rateLimit: Ratelimit): RateLimiter => { } if (await isLimitFreeOakUser(userId)) { - logger.debug("Bypassing rate-limit for oak user %s", userId); + log.info("Bypassing rate-limit for oak user %s", userId); return { isSubjectToRateLimiting: false }; } @@ -62,7 +62,7 @@ export const userBasedRateLimiter = (rateLimit: Ratelimit): RateLimiter => { waitUntil(pending); if (!success) { - logger.warn("Rate limit exceeded for user %s", userId); + log.info("Rate limit exceeded for user %s", userId); throw new RateLimitExceededError(userId, rest.limit, rest.reset); } diff --git a/packages/core/src/workers/generations/requestGeneration.ts b/packages/core/src/workers/generations/requestGeneration.ts index 8978f0ff5..da161792e 100644 --- a/packages/core/src/workers/generations/requestGeneration.ts +++ b/packages/core/src/workers/generations/requestGeneration.ts @@ -1,5 +1,9 @@ import { GenerationStatus, ModerationType, Prisma, prisma } from "@oakai/db"; -import baseLogger, { Logger } from "@oakai/logger"; +import { + structuredLogger as baseLogger, + aiLogger, + StructuredLogger, +} from "@oakai/logger"; import { Redis } from "@upstash/redis"; import { NonRetriableError } from "inngest"; import { z } from "zod"; @@ -21,6 +25,8 @@ import { } from "../../utils/moderation"; import { requestGenerationSchema } from "./requestGeneration.schema"; +const log = aiLogger("generation"); + /** * Worker converted from an Inngest function */ @@ -286,14 +292,14 @@ async function invoke({ data, user }: RequestGenerationArgs) { * to work around streaming limitations with netlify */ const onNewToken = async (partialJson: string) => { - console.log("onNewToken", partialJson); + log.info("onNewToken", partialJson); try { await redis.set(`partial-generation-${generationId}`, partialJson, { // Expire after 10 minutes ex: 60 * 10, }); } catch (err) { - console.log("Failed to write to redis"); + logger.error("Failed to write to redis"); logger.error(err, "Error caching generation stream"); } }; @@ -392,7 +398,7 @@ type OnFailureArgs = { event: { data: z.infer<(typeof requestGenerationSchema)["data"]> }; }; }; - logger: Logger; + logger: StructuredLogger; }; async function onFailure({ error, event, logger }: OnFailureArgs) { diff --git a/packages/db/index.ts b/packages/db/index.ts index 91b5b39bc..555228f7c 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -1,13 +1,29 @@ +import { aiLogger } from "@oakai/logger"; import { PrismaClient } from "@prisma/client"; import { withAccelerate } from "@prisma/extension-accelerate"; -const createPrismaClient = () => - new PrismaClient({ - log: - process.env.NODE_ENV === "development" - ? ["query", "error", "warn"] - : ["error"], - }).$extends(withAccelerate()); +const log = aiLogger("db"); + +const createPrismaClient = () => { + const client = new PrismaClient({ + log: [ + { emit: "stdout", level: "error" }, + // Prisma supports DEBUG strings (eg: prisma*, prisma:client), but they're noisy debug messages. + // Instead, we forward the typical logs based on ai:db + { emit: "event", level: "query" }, + { emit: "event", level: "warn" }, + ], + }); + + client.$on("query", (e) => { + log.info(e.query); + }); + client.$on("warn", (e) => { + log.info(e.message); + }); + + return client.$extends(withAccelerate()); +}; // Create an instance to extract the type const extendedPrisma = createPrismaClient(); diff --git a/packages/db/package.json b/packages/db/package.json index 7d30204c7..cc7110d0e 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -54,6 +54,7 @@ }, "prettier": "@oakai/prettier-config", "dependencies": { + "@oakai/logger": "*", "@prisma/client": "^5.13.0", "@types/chunk-text": "^1.0.0", "cheerio": "1.0.0-rc.12", diff --git a/packages/exports/package.json b/packages/exports/package.json index 9ae8e949e..785b6f5ba 100644 --- a/packages/exports/package.json +++ b/packages/exports/package.json @@ -6,6 +6,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { + "@oakai/logger": "*", "typescript": "5.3.3" }, "prettier": "@oakai/prettier-config" diff --git a/packages/exports/src/exportAdditionalMaterials.ts b/packages/exports/src/exportAdditionalMaterials.ts index 85225d2d3..85f93bf1f 100644 --- a/packages/exports/src/exportAdditionalMaterials.ts +++ b/packages/exports/src/exportAdditionalMaterials.ts @@ -1,3 +1,5 @@ +import { aiLogger } from "@oakai/logger"; + import { prepLessonForAdditionalMaterialsDoc } from "./dataHelpers/prepLessonForSlides"; import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; @@ -6,6 +8,8 @@ import { LessonSlidesInputData } from "./schema/input.schema"; import { getSlidesTemplateIdAdditionalMaterials as getDocsTemplateIdAdditionalMaterials } from "./templates"; import { OutputData, Result, State } from "./types"; +const log = aiLogger("exports"); + export const exportAdditionalMaterials = async ({ snapshotId, lesson, @@ -24,7 +28,7 @@ export const exportAdditionalMaterials = async ({ prepData: prepLessonForAdditionalMaterialsDoc, templateId: getDocsTemplateIdAdditionalMaterials(), updateTemplate: async ({ templateCopyId }) => { - console.log("templateCopyId", templateCopyId); + log.info("templateCopyId", templateCopyId); }, populateTemplate: async ({ data, templateCopyId }) => { const client = await getDocsClient(); diff --git a/packages/exports/src/exportDocsWorksheet.ts b/packages/exports/src/exportDocsWorksheet.ts index 01020539d..84bfffc9e 100644 --- a/packages/exports/src/exportDocsWorksheet.ts +++ b/packages/exports/src/exportDocsWorksheet.ts @@ -1,3 +1,5 @@ +import { aiLogger } from "@oakai/logger"; + import { prepWorksheetForSlides } from "./dataHelpers/prepWorksheetForSlides"; import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; @@ -6,6 +8,8 @@ import { WorksheetSlidesInputData } from "./schema/input.schema"; import { getDocsTemplateIdWorksheet } from "./templates"; import { OutputData, Result, State } from "./types"; +const log = aiLogger("exports"); + export const exportDocsWorksheet = async ({ snapshotId, data: inputData, @@ -46,7 +50,7 @@ export const exportDocsWorksheet = async ({ onStateChange({ status: "success", data }); return { data }; } catch (error) { - console.error("Error", error); + log.error("Error", error); onStateChange({ status: "error", error }); return { error, message: "Failed to export worksheet" }; } diff --git a/packages/ingest/package.json b/packages/ingest/package.json index baa8bed0a..5d7e85b1a 100644 --- a/packages/ingest/package.json +++ b/packages/ingest/package.json @@ -21,6 +21,7 @@ "@oakai/aila": "*", "@oakai/core": "*", "@oakai/db": "*", + "@oakai/logger": "*", "csv-parser": "^3.0.0", "graphql-request": "^6.1.0", "webvtt-parser": "^2.2.0", diff --git a/packages/ingest/src/captions/getCaptionsByFileName.ts b/packages/ingest/src/captions/getCaptionsByFileName.ts index 28804e1d4..8f3d45ba5 100644 --- a/packages/ingest/src/captions/getCaptionsByFileName.ts +++ b/packages/ingest/src/captions/getCaptionsByFileName.ts @@ -1,6 +1,9 @@ import { Storage } from "@google-cloud/storage"; +import { aiLogger } from "@oakai/logger"; import { Cue, WebVTTParser } from "webvtt-parser"; +const log = aiLogger("ingest"); + export const getCaptionsByFileName = async ( fileName: string, ): Promise<{ @@ -16,7 +19,7 @@ export const getCaptionsByFileName = async ( const tree = parser.parse(file, "metadata"); if (tree.errors.length) { - console.error( + log.error( `Error parsing captions file: ${fileName}, errors: ${JSON.stringify( tree.errors, )}`, @@ -69,7 +72,7 @@ async function getFileFromBucket(bucketName: string, fileName: string) { const contents = await bucket.file(fileName).download(); return contents.toString(); } catch (err) { - console.error( + log.error( `Error fetching file: ${fileName} from bucket: ${bucketName}. Error: ${err}`, ); return; diff --git a/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts b/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts index 181e4c6c9..224a7910e 100644 --- a/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts +++ b/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts @@ -1,4 +1,5 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; import { createErrorRecord } from "../db-helpers/createErrorRecord"; @@ -7,6 +8,8 @@ import { downloadOpenAiFile } from "../openai-batches/downloadOpenAiFile"; import { jsonlToArray } from "../utils/jsonlToArray"; import { parseBatchEmbedding } from "./parseBatchEmbedding"; +const log = aiLogger("ingest"); + /** * When an embedding batch is successfully processed, this function * extracts the embeddings and updates the lesson plan parts with them. @@ -37,7 +40,7 @@ export async function handleEmbeddingBatchSuccess({ try { const batchEmbedding = parseBatchEmbedding(json); lessonId = batchEmbedding.lessonId; - console.log(`Embedding lesson ${lessonId}`); + log.info(`Embedding lesson ${lessonId}`); const { lessonPlanPartId, embedding } = batchEmbedding; const vector = `[${embedding.join(",")}]`; @@ -101,7 +104,7 @@ export async function handleEmbeddingBatchSuccess({ }, }); - console.log(`Batch ${batchId} completed`); - console.log(`Failed: ${lessonIdsFailed.size} lessons`); - console.log(`Completed: ${lessonIdsCompleted.size} lessons`); + log.info(`Batch ${batchId} completed`); + log.info(`Failed: ${lessonIdsFailed.size} lessons`); + log.info(`Completed: ${lessonIdsCompleted.size} lessons`); } diff --git a/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts b/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts index d051db54d..c8edad819 100644 --- a/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts +++ b/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts @@ -1,4 +1,5 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; import { createErrorRecord } from "../db-helpers/createErrorRecord"; @@ -7,6 +8,8 @@ import { downloadOpenAiFile } from "../openai-batches/downloadOpenAiFile"; import { jsonlToArray } from "../utils/jsonlToArray"; import { parseBatchLessonPlan } from "./parseBatchLessonPlan"; +const log = aiLogger("ingest"); + /** * When a lesson plan batch is successfully processed, this function * extracts the generated lesson plans and stores them in the @@ -86,8 +89,8 @@ export async function handleLessonPlanBatchSuccess({ }, }); - console.log(`Updated ${lessonIdsCompleted.length} lessons with lesson plans`); - console.log( + log.info(`Updated ${lessonIdsCompleted.length} lessons with lesson plans`); + log.info( `Failed to update ${lessonIdsFailed.length} lessons with lesson plans`, ); } diff --git a/packages/ingest/src/import-lessons/importLessons.ts b/packages/ingest/src/import-lessons/importLessons.ts index 73ab128ff..85afffb6b 100644 --- a/packages/ingest/src/import-lessons/importLessons.ts +++ b/packages/ingest/src/import-lessons/importLessons.ts @@ -1,4 +1,5 @@ import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; import { getDataHash } from "../utils/getDataHash"; @@ -6,6 +7,8 @@ import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; import { graphqlClient } from "./graphql/client"; import { query } from "./graphql/query"; +const log = aiLogger("ingest"); + type ImportRawLessonsProps = { ingestId: string; onError: (error: IngestError) => void; @@ -54,13 +57,13 @@ export async function importLessons({ }; }); - console.log(`Importing ${data.length} lessons`); + log.info(`Importing ${data.length} lessons`); await prisma.ingestLesson.createMany({ data, }); - console.log(`Imported ${data.length} lessons`); + log.info(`Imported ${data.length} lessons`); if (lessonData.lessons.length < limit) { break; diff --git a/packages/ingest/src/import-lessons/importLessonsFromCSV.ts b/packages/ingest/src/import-lessons/importLessonsFromCSV.ts index a80c36af2..0c674e0b0 100644 --- a/packages/ingest/src/import-lessons/importLessonsFromCSV.ts +++ b/packages/ingest/src/import-lessons/importLessonsFromCSV.ts @@ -1,4 +1,5 @@ import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import csv from "csv-parser"; import fs from "node:fs"; @@ -7,6 +8,8 @@ import { chunkAndPromiseAll } from "../utils/chunkAndPromiseAll"; import { getDataHash } from "../utils/getDataHash"; import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; +const log = aiLogger("ingest"); + type ImportLessonsFromCSVProps = { ingestId: string; filePath: string; @@ -66,7 +69,7 @@ export function importLessonsFromCSV({ chunkSize: 30000, }); - console.log(`Imported ${parsedLessons.length} lessons from CSV`); + log.info(`Imported ${parsedLessons.length} lessons from CSV`); resolve(); }) .on("error", (cause) => { diff --git a/packages/ingest/src/openai-batches/startBatch.ts b/packages/ingest/src/openai-batches/startBatch.ts index 9f11fb82d..4569dead4 100644 --- a/packages/ingest/src/openai-batches/startBatch.ts +++ b/packages/ingest/src/openai-batches/startBatch.ts @@ -1,3 +1,5 @@ +import { aiLogger } from "@oakai/logger"; + import { splitJsonlByRowsOrSize } from "../utils/splitJsonlByRowsOrSize"; import { OPEN_AI_BATCH_MAX_ROWS, OPEN_AI_BATCH_MAX_SIZE_MB } from "./constants"; import { getCustomIdsFromJsonlFile } from "./getCustomIdsFromJsonlFile"; @@ -8,6 +10,8 @@ import { import { uploadOpenAiBatchFile } from "./uploadOpenAiBatchFile"; import { GetBatchFileLine, writeBatchFile } from "./writeBatchFile"; +const log = aiLogger("ingest"); + export async function startBatch({ ingestId, data, @@ -35,7 +39,7 @@ export async function startBatch({ }); for (const filePath of filePaths) { - console.log(`Submitting batch for ${filePath}`); + log.info(`Submitting batch for ${filePath}`); const { file } = await uploadOpenAiBatchFile({ filePath, }); @@ -48,7 +52,7 @@ export async function startBatch({ filePath, }); - console.log(`Submitted batch ${openaiBatch.id} for ${filePath}`); + log.info(`Submitted batch ${openaiBatch.id} for ${filePath}`); await onSubmitted({ openaiBatchId: openaiBatch.id, filePath, customIds }); } } diff --git a/packages/ingest/src/openai-batches/writeBatchFile.ts b/packages/ingest/src/openai-batches/writeBatchFile.ts index f60e19801..b57a0d7d9 100644 --- a/packages/ingest/src/openai-batches/writeBatchFile.ts +++ b/packages/ingest/src/openai-batches/writeBatchFile.ts @@ -1,7 +1,10 @@ +import { aiLogger } from "@oakai/logger"; import fs from "node:fs"; import { IngestError } from "../IngestError"; +const log = aiLogger("ingest"); + function getBatchDataDir({ ingestId }: { ingestId: string }) { return `${__dirname}/data/ingest_${ingestId}`; } @@ -29,7 +32,8 @@ export function writeBatchFile({ writeStream.on("finish", () => { resolve({ filePath, batchDir }); - console.log("Finished writing batch file", filePath); + + log.info("Finished writing batch file", filePath); }); writeStream.on("error", reject); diff --git a/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts b/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts index ebea9786e..d47cef341 100644 --- a/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts +++ b/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts @@ -1,9 +1,12 @@ +import { aiLogger } from "@oakai/logger"; import * as fs from "node:fs"; import * as path from "node:path"; import * as readline from "node:readline"; import { IngestError } from "../IngestError"; +const log = aiLogger("ingest"); + /** * Splits a large .jsonl file into multiple files, based on whichever limit is hit first: max rows or max file size. * @param options - An object containing the options for splitting the file @@ -77,9 +80,9 @@ export async function splitJsonlByRowsOrSize({ if (currentFileStream) { currentFileStream.end(); } - console.log(`Splitting complete! Files created in ${outputDir}`); + log.info(`Splitting complete! Files created in ${outputDir}`); } catch (error) { - console.error(`Error splitting JSONL: ${(error as Error).message}`); + log.error(`Error splitting JSONL: ${(error as Error).message}`); throw new IngestError("Error splitting JSONL"); } diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 548acb531..87b65b746 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -1,27 +1,79 @@ -import pino from "pino"; +import debug from "debug"; -const logger = pino({ - level: process.env.NEXT_PUBLIC_PINO_LOG_LEVEL || "info", - browser: { - write(obj) { - const msg = "msg" in obj && obj.msg; - console.warn( - `Invalid use of @oakai/logger, use logger/browser, logMessage=${msg}`, - ); - }, - }, - formatters: { - // Pino logs the level as a syslog number, so make sure - // it sends error/warn/info etc instead - level: (label) => { - return { level: label.toUpperCase() }; - }, - }, - // Pino's default key of `time` is ignored by datadog, so explicitly set - // `timestamp` - timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, -}); +import structuredLogger, { StructuredLogger } from "./structuredLogger"; -export type Logger = typeof logger; +if (typeof window !== "undefined") { + debug.enable("ai:*"); +} -export default logger; +const debugBase = debug("ai"); + +type ChildKey = + | "admin" + | "aila" + | "aila:analytics" + | "aila:errors" + | "aila:llm" + | "aila:moderation" + | "aila:moderation:response" + | "aila:persistence" + | "aila:prompt" + | "aila:protocol" + | "aila:rag" + | "aila:testing" + | "analytics" + | "app" + | "auth" + | "chat" + | "cloudinary" + | "consent" + | "db" + | "demo" + | "exports" + | "feature-flags" + | "feedback" + | "fixtures" + | "generation" + | "ingest" + | "inngest" + | "judgements" + | "lessons" + | "middleware:auth" + | "moderation" + | "prompts" + | "qd" + | "rag" + | "rate-limiting" + | "snippets" + | "testing" + | "tracing" + | "transcripts" + | "trpc" + | "ui"; + +/** + * The AI logger uses namespaces so that we can selectively toggle noisy logs. + * Logs are selected with the DEBUG environment variable. + * Error logs are always shown. + * + * @example Include all logs except prisma + * DEBUG=ai:*,-ai:db + * + * @example Usage in a module + * import { aiLogger } from "@ai-jsx/logger"; + * + * const log = aiLogger("db"); + * log.info("Hello world"); + */ +export function aiLogger(childKey: ChildKey) { + const debugLogger = debugBase.extend(childKey); + + return { + info: debugLogger, + warn: debugLogger, + error: structuredLogger.error.bind(structuredLogger), + }; +} + +export { structuredLogger }; +export type { StructuredLogger }; diff --git a/packages/logger/package.json b/packages/logger/package.json index 51db093b6..dd59f2bcd 100644 --- a/packages/logger/package.json +++ b/packages/logger/package.json @@ -13,6 +13,7 @@ }, "prettier": "@oakai/prettier-config", "dependencies": { + "debug": "^4.3.7", "pino": "^8.15.1" }, "devDependencies": { diff --git a/packages/logger/structuredLogger.ts b/packages/logger/structuredLogger.ts new file mode 100644 index 000000000..6e8903a55 --- /dev/null +++ b/packages/logger/structuredLogger.ts @@ -0,0 +1,27 @@ +import pino from "pino"; + +const logger = pino({ + level: process.env.NEXT_PUBLIC_PINO_LOG_LEVEL || "info", + browser: { + write(obj) { + const msg = "msg" in obj && obj.msg; + console.warn( + `Invalid use of @oakai/logger, use logger/browser, logMessage=${msg}`, + ); + }, + }, + formatters: { + // Pino logs the level as a syslog number, so make sure + // it sends error/warn/info etc instead + level: (label) => { + return { level: label.toUpperCase() }; + }, + }, + // Pino's default key of `time` is ignored by datadog, so explicitly set + // `timestamp` + timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, +}); + +export type StructuredLogger = typeof logger; + +export default logger; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6370de562..a576f9f3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -702,6 +702,9 @@ importers: packages/db: dependencies: + '@oakai/logger': + specifier: '*' + version: link:../logger '@prisma/client': specifier: ^5.13.0 version: 5.13.0(prisma@5.16.1) @@ -794,6 +797,9 @@ importers: packages/exports: dependencies: + '@oakai/logger': + specifier: '*' + version: link:../logger typescript: specifier: 5.3.3 version: 5.3.3 @@ -812,6 +818,9 @@ importers: '@oakai/db': specifier: '*' version: link:../db + '@oakai/logger': + specifier: '*' + version: link:../logger csv-parser: specifier: ^3.0.0 version: 3.0.0 @@ -849,6 +858,9 @@ importers: packages/logger: dependencies: + debug: + specifier: ^4.3.7 + version: 4.3.7(supports-color@5.5.0) pino: specifier: ^8.15.1 version: 8.15.1 @@ -1124,7 +1136,7 @@ packages: '@babel/traverse': 7.24.5 '@babel/types': 7.25.2 convert-source-map: 2.0.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1239,7 +1251,7 @@ packages: '@babel/core': 7.24.5 '@babel/helper-compilation-targets': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -2611,7 +2623,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.4 '@babel/types': 7.23.4 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2629,7 +2641,7 @@ packages: '@babel/helper-split-export-declaration': 7.24.5 '@babel/parser': 7.24.5 '@babel/types': 7.24.5 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -2643,7 +2655,7 @@ packages: '@babel/parser': 7.25.3 '@babel/template': 7.25.0 '@babel/types': 7.25.2 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -3257,7 +3269,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) espree: 9.6.1 globals: 13.21.0 ignore: 5.2.4 @@ -3883,7 +3895,7 @@ packages: '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.9.21 chalk: 4.1.2 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) dotenv: 16.4.5 graphql: 16.9.0 graphql-request: 6.1.0(graphql@16.9.0) @@ -4024,7 +4036,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -7455,7 +7467,7 @@ packages: conventional-changelog-angular: 6.0.0 conventional-commits-filter: 3.0.0 conventional-commits-parser: 5.0.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) import-from: 4.0.0 lodash-es: 4.17.21 micromatch: 4.0.5 @@ -7482,7 +7494,7 @@ packages: dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) execa: 5.1.1 lodash: 4.17.21 parse-json: 5.2.0 @@ -7499,7 +7511,7 @@ packages: dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) dir-glob: 3.0.1 execa: 5.1.1 lodash: 4.17.21 @@ -7522,7 +7534,7 @@ packages: '@octokit/plugin-throttling': 8.2.0(@octokit/core@5.2.0) '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) dir-glob: 3.0.1 globby: 14.0.1 http-proxy-agent: 7.0.2 @@ -7569,7 +7581,7 @@ packages: conventional-changelog-writer: 6.0.1 conventional-commits-filter: 4.0.0 conventional-commits-parser: 5.0.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) get-stream: 7.0.1 import-from: 4.0.0 into-stream: 7.0.0 @@ -8535,7 +8547,7 @@ packages: typescript: '>= 4.x' webpack: '>= 4' dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) endent: 2.1.0 find-cache-dir: 3.3.2 flat-cache: 3.0.4 @@ -9360,7 +9372,7 @@ packages: '@typescript-eslint/types': 6.20.0 '@typescript-eslint/typescript-estree': 6.20.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.20.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) eslint: 8.56.0 typescript: 5.3.3 transitivePeerDependencies: @@ -9380,7 +9392,7 @@ packages: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 eslint: 8.56.0 typescript: 5.3.3 transitivePeerDependencies: @@ -9421,7 +9433,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.3.3) '@typescript-eslint/utils': 7.18.0(eslint@8.56.0)(typescript@5.3.3) - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) eslint: 8.56.0 ts-api-utils: 1.3.0(typescript@5.3.3) typescript: 5.3.3 @@ -9453,7 +9465,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.6.3 @@ -9473,7 +9485,7 @@ packages: dependencies: '@typescript-eslint/types': 6.20.0 '@typescript-eslint/visitor-keys': 6.20.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -9494,7 +9506,7 @@ packages: dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.5 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.5 @@ -9990,7 +10002,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -9998,7 +10010,7 @@ packages: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -12308,7 +12320,7 @@ packages: dependencies: ms: 2.1.2 - /debug@4.3.5(supports-color@5.5.0): + /debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} peerDependencies: @@ -12318,6 +12330,18 @@ packages: optional: true dependencies: ms: 2.1.2 + dev: false + + /debug@4.3.7(supports-color@5.5.0): + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.3 supports-color: 5.5.0 /decamelize-keys@1.1.1: @@ -13069,7 +13093,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) esbuild: 0.21.5 transitivePeerDependencies: - supports-color @@ -13205,7 +13229,7 @@ packages: eslint: '*' eslint-plugin-import: '*' dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) enhanced-resolve: 5.15.0 eslint: 8.56.0 eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) @@ -14964,7 +14988,7 @@ packages: resolution: {integrity: sha512-Ci5LRufQ8AtrQ1U26AevS8QoMXDOhnAHCJI3eZu1com7mZGHxREmw3dNj85ftpQokQCvak8nI2pnFS8zyM1M+Q==} engines: {node: '>=4.0.0'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: true @@ -14975,7 +14999,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color dev: false @@ -14985,7 +15009,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15010,7 +15034,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15019,7 +15043,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15866,7 +15890,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -17997,7 +18021,7 @@ packages: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} dependencies: '@types/debug': 4.1.12 - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -18429,7 +18453,7 @@ packages: resolution: {integrity: sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==} engines: {node: '>= 10.13'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) json-stringify-safe: 5.0.1 propagate: 2.0.1 transitivePeerDependencies: @@ -20812,7 +20836,7 @@ packages: resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==} engines: {node: '>=8.6.0'} dependencies: - debug: 4.3.5(supports-color@5.5.0) + debug: 4.3.7(supports-color@5.5.0) module-details-from-path: 1.0.3 resolve: 1.22.8 transitivePeerDependencies: @@ -21133,7 +21157,7 @@ packages: '@semantic-release/release-notes-generator': 11.0.7(semantic-release@21.1.2) aggregate-error: 5.0.0 cosmiconfig: 8.3.6(typescript@5.3.3) - debug: 4.3.4 + debug: 4.3.7(supports-color@5.5.0) env-ci: 9.1.1 execa: 8.0.1 figures: 5.0.0 From cfc42a7282feda8ba3c72692aa909246bca2c3cb Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:15:29 +0200 Subject: [PATCH 006/127] test: delete the oldest test user when creating a new one (#250) --- .../api/src/router/testSupport/prepareUser.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/api/src/router/testSupport/prepareUser.ts b/packages/api/src/router/testSupport/prepareUser.ts index 97e1a33c7..3dce91729 100644 --- a/packages/api/src/router/testSupport/prepareUser.ts +++ b/packages/api/src/router/testSupport/prepareUser.ts @@ -1,4 +1,5 @@ import { clerkClient } from "@clerk/nextjs/server"; +import { waitUntil } from "@vercel/functions"; import { aiLogger } from "@oakai/logger"; import os from "os"; import { z } from "zod"; @@ -74,6 +75,27 @@ const generateEmailAddress = (personaName: keyof typeof personas) => { return `${parts.join("+")}@thenational.academy`; }; +const deleteLastUsedTestUser = async () => { + const users = await clerkClient.users.getUserList({ + orderBy: "+last_active_at", + limit: 500, + }); + + const NUMBERS_USER = /\d{5,10}.*@/; // jim+010203@thenational.academy + const lastUsedTestUser = users.data.find((u) => { + const email = u.primaryEmailAddress?.emailAddress ?? ""; + return email.startsWith("test+") || email.match(NUMBERS_USER); + }); + + if (lastUsedTestUser) { + console.log( + "Deleting oldest test user", + lastUsedTestUser.primaryEmailAddress?.emailAddress, + ); + await clerkClient.users.deleteUser(lastUsedTestUser.id); + } +}; + const findOrCreateUser = async ( email: string, personaName: keyof typeof personas, @@ -112,6 +134,8 @@ const findOrCreateUser = async ( }, }); + waitUntil(deleteLastUsedTestUser()); + return newUser; }; From 781c322c8792dfffc826b2132d152408003c8fd8 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:34:09 +0200 Subject: [PATCH 007/127] chore: add no-console to eslint (#251) --- .eslintrc.cjs | 3 +++ apps/nextjs/src/env/client.mjs | 2 ++ apps/nextjs/src/env/server.mjs | 5 ++++- apps/nextjs/src/utils/useClientSideFeatureFlag.ts | 6 +++++- packages/logger/.eslintrc.cjs | 7 +++++++ 5 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 packages/logger/.eslintrc.cjs diff --git a/.eslintrc.cjs b/.eslintrc.cjs index b830eda85..1b64ff4fc 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -12,4 +12,7 @@ module.exports = { }, plugins: ["@typescript-eslint"], extends: ["plugin:@typescript-eslint/recommended"], + rules: { + "no-console": "error" + } }; diff --git a/apps/nextjs/src/env/client.mjs b/apps/nextjs/src/env/client.mjs index c2b360101..aa7c1d5db 100644 --- a/apps/nextjs/src/env/client.mjs +++ b/apps/nextjs/src/env/client.mjs @@ -1,4 +1,6 @@ // @ts-check + +/* eslint no-console: "off" */ import { clientEnv, clientSchema } from "./schema.mjs"; const _clientEnv = clientSchema.safeParse(clientEnv); diff --git a/apps/nextjs/src/env/server.mjs b/apps/nextjs/src/env/server.mjs index 736e3a6bf..eab0b6472 100644 --- a/apps/nextjs/src/env/server.mjs +++ b/apps/nextjs/src/env/server.mjs @@ -1,10 +1,13 @@ // @ts-check + +/* eslint no-console: "off" */ + /** * This file is included in `/next.config.mjs` which ensures the app isn't built with invalid env vars. * It has to be a `.mjs`-file to be imported there. */ -import { serverSchema } from "./schema.mjs"; import { env as clientEnv, formatErrors } from "./client.mjs"; +import { serverSchema } from "./schema.mjs"; const _serverEnv = serverSchema.safeParse(process.env); diff --git a/apps/nextjs/src/utils/useClientSideFeatureFlag.ts b/apps/nextjs/src/utils/useClientSideFeatureFlag.ts index df0018646..fb07ab398 100644 --- a/apps/nextjs/src/utils/useClientSideFeatureFlag.ts +++ b/apps/nextjs/src/utils/useClientSideFeatureFlag.ts @@ -1,7 +1,11 @@ import { useEffect, useState } from "react"; +import { aiLogger } from "@oakai/logger"; + import useAnalytics from "@/lib/analytics/useAnalytics"; +const log = aiLogger("feature-flags"); + export function useClientSideFeatureFlag(flag: string): boolean { const { posthogAiBetaClient: client } = useAnalytics(); @@ -11,7 +15,7 @@ export function useClientSideFeatureFlag(flag: string): boolean { const isDebug = process.env.NEXT_PUBLIC_POSTHOG_DEBUG === "true"; if (isDebug) { - console.info(`Feature flag ${flag} is enabled in debug mode`); + log.info(`Feature flag ${flag} is enabled in debug mode`); setFeatureEnabled(true); } else { return client.onFeatureFlags(() => { diff --git a/packages/logger/.eslintrc.cjs b/packages/logger/.eslintrc.cjs new file mode 100644 index 000000000..69a283b61 --- /dev/null +++ b/packages/logger/.eslintrc.cjs @@ -0,0 +1,7 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + extends: ["../../.eslintrc.cjs"], + rules: { + "no-console": "off", + }, +}; From 24f492dc79e6a9dc94e213c4f447802e750c99db Mon Sep 17 00:00:00 2001 From: MG Date: Mon, 21 Oct 2024 15:19:52 +0100 Subject: [PATCH 008/127] fix: refactor lesson snapshots for improved efficiency (#234) --- .eslintrc.cjs | 2 +- .../useExportAdditionalMaterials.ts | 34 +-- .../ExportsDialogs/useExportLessonPlanDoc.ts | 33 +-- .../ExportsDialogs/useExportLessonSlides.ts | 34 +-- .../ExportsDialogs/useExportQuizDoc.ts | 48 ++- .../useExportWorksheetSlides.ts | 33 +-- .../useExportsExistenceCheck.ts | 48 +++ packages/aila/src/core/Aila.ts | 7 + packages/aila/src/core/AilaFeatureFactory.ts | 5 + packages/aila/src/core/AilaServices.ts | 2 + packages/aila/src/core/chat/AilaChat.ts | 23 +- .../builders/AilaLessonPromptBuilder.ts | 5 +- .../snapshotStore/AilaSnapshotStore.ts | 52 ++++ .../aila/src/features/snapshotStore/index.ts | 1 + .../export/exportAdditionalMaterialsDoc.ts | 7 +- packages/api/src/export/exportLessonPlan.ts | 7 +- packages/api/src/export/exportLessonSlides.ts | 7 +- packages/api/src/export/exportQuizDoc.ts | 7 +- packages/api/src/export/exportWorksheets.ts | 7 +- packages/api/src/router/exports.ts | 278 ++++-------------- .../api/src/router/testSupport/prepareUser.ts | 4 +- packages/core/src/models/lessonSnapshots.ts | 92 ++---- packages/core/src/models/moderations.ts | 16 +- .../migration.sql | 2 + packages/db/prisma/schema.prisma | 1 + 25 files changed, 290 insertions(+), 465 deletions(-) create mode 100644 apps/nextjs/src/components/ExportsDialogs/useExportsExistenceCheck.ts create mode 100644 packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts create mode 100644 packages/aila/src/features/snapshotStore/index.ts create mode 100644 packages/db/prisma/migrations/20241016113705_lesson_snapshot_trigger_message/migration.sql diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 1b64ff4fc..691d8e4f4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -13,6 +13,6 @@ module.exports = { plugins: ["@typescript-eslint"], extends: ["plugin:@typescript-eslint/recommended"], rules: { - "no-console": "error" + "no-console": "warn" } }; diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts index 05a2a92e1..6affbc9d9 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts @@ -2,7 +2,6 @@ import { useEffect, useMemo, useState, useCallback } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; -import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,8 +9,7 @@ import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; - -const log = aiLogger("exports"); +import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportAdditionalMaterials({ onStart, @@ -38,30 +36,14 @@ export function useExportAdditionalMaterials({ const checkForSnapShotAndPreloadQuery = trpc.exports.checkIfAdditionalMaterialsDownloadExists.useMutation(); - const [checked, setChecked] = useState(false); - const check = useCallback(async () => { - if ( - debouncedParseResult?.success && - debouncedParseResult.data && - !checked - ) { - try { - checkForSnapShotAndPreloadQuery.mutate({ - chatId, - data: debouncedParseResult.data, - }); - setChecked(true); - } catch (error) { - log.error("Error during check:", error); - } - } - }, [ - debouncedParseResult?.success, - debouncedParseResult?.data, + + const check = useExportsExistenceCheck({ + success: debouncedParseResult?.success, + data: debouncedParseResult?.data, chatId, - checkForSnapShotAndPreloadQuery, - checked, - ]); + messageId, + checkFn: checkForSnapShotAndPreloadQuery.mutate, + }); useEffect(() => { check(); diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts index 8d84e546c..8d3e49a2d 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts @@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocLessonPlanSchema } from "@oakai/exports/browser"; import { LessonPlanDocInputData } from "@oakai/exports/src/schema/input.schema"; -import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,8 +9,7 @@ import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; - -const log = aiLogger("exports"); +import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportLessonPlanDoc({ onStart, @@ -38,30 +36,13 @@ export function useExportLessonPlanDoc({ const checkForSnapShotAndPreloadQuery = trpc.exports.checkIfLessonPlanDownloadExists.useMutation(); - const [checked, setChecked] = useState(false); - const check = useCallback(async () => { - if ( - debouncedParseResult?.success && - debouncedParseResult.data && - !checked - ) { - try { - checkForSnapShotAndPreloadQuery.mutate({ - chatId, - data: debouncedParseResult.data, - }); - setChecked(true); - } catch (error) { - log.error("Error during check:", error); - } - } - }, [ - debouncedParseResult?.success, - debouncedParseResult?.data, + const check = useExportsExistenceCheck({ + success: debouncedParseResult?.success, + data: debouncedParseResult?.data, chatId, - checkForSnapShotAndPreloadQuery, - checked, - ]); + messageId, + checkFn: checkForSnapShotAndPreloadQuery.mutate, + }); useEffect(() => { check(); diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts index c76e5e89b..f75b10ae8 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts @@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; -import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,8 +9,7 @@ import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; - -const log = aiLogger("exports"); +import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportLessonSlides({ onStart, @@ -38,30 +36,14 @@ export function useExportLessonSlides({ const checkForSnapShotAndPreloadQuery = trpc.exports.checkIfSlideDownloadExists.useMutation(); - const [checked, setChecked] = useState(false); - const check = useCallback(async () => { - if ( - debouncedParseResult?.success && - debouncedParseResult.data && - !checked - ) { - try { - checkForSnapShotAndPreloadQuery.mutate({ - chatId, - data: debouncedParseResult.data, - }); - setChecked(true); - } catch (error) { - log.error("Error during check:", error); - } - } - }, [ - debouncedParseResult?.success, - debouncedParseResult?.data, + + const check = useExportsExistenceCheck({ + success: debouncedParseResult?.success, + data: debouncedParseResult?.data, chatId, - checkForSnapShotAndPreloadQuery, - checked, - ]); + messageId, + checkFn: checkForSnapShotAndPreloadQuery.mutate, + }); useEffect(() => { check(); diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts index 20c39daf4..c867e4131 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts @@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocQuizSchema } from "@oakai/exports/browser"; import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; -import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,8 +9,7 @@ import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; - -const log = aiLogger("exports"); +import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportQuizDoc({ onStart, @@ -43,32 +41,26 @@ export function useExportQuizDoc({ const checkForSnapShotAndPreloadQuery = trpc.exports.checkIfQuizDownloadExists.useMutation(); - const [checked, setChecked] = useState(false); - const check = useCallback(async () => { - if ( - debouncedParseResult?.success && - debouncedParseResult.data && - !checked - ) { - try { - checkForSnapShotAndPreloadQuery.mutate({ - chatId, - lessonSnapshot: lesson, - data: debouncedParseResult.data, - }); - setChecked(true); - } catch (error) { - log.error("Error during check:", error); - } - } - }, [ - lesson, - debouncedParseResult?.success, - debouncedParseResult?.data, + const checkFn = useCallback( + ( + args: Omit< + Parameters[0], + "lessonSnapshot" + >, + ) => + checkForSnapShotAndPreloadQuery.mutate({ + ...args, + lessonSnapshot: lesson, + }), + [lesson, checkForSnapShotAndPreloadQuery], + ); + const check = useExportsExistenceCheck({ + success: debouncedParseResult?.success, + data: debouncedParseResult?.data, chatId, - checkForSnapShotAndPreloadQuery, - checked, - ]); + messageId, + checkFn, + }); useEffect(() => { check(); diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts index 82624cda4..ce83a7d45 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts @@ -2,7 +2,6 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesWorksheetSchema } from "@oakai/exports/browser"; import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; -import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; import { ZodError } from "zod"; @@ -10,8 +9,7 @@ import { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; import { ExportsHookProps } from "./exports.types"; - -const log = aiLogger("exports"); +import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportWorksheetSlides({ onStart, @@ -38,30 +36,13 @@ export function useExportWorksheetSlides({ const checkForSnapShotAndPreloadQuery = trpc.exports.checkIfWorksheetDownloadExists.useMutation(); - const [checked, setChecked] = useState(false); - const check = useCallback(async () => { - if ( - debouncedParseResult?.success && - debouncedParseResult.data && - !checked - ) { - try { - checkForSnapShotAndPreloadQuery.mutate({ - chatId, - data: debouncedParseResult.data, - }); - setChecked(true); - } catch (error) { - log.error("Error during check:", error); - } - } - }, [ - debouncedParseResult?.success, - debouncedParseResult?.data, + const check = useExportsExistenceCheck({ + success: debouncedParseResult?.success, + data: debouncedParseResult?.data, chatId, - checkForSnapShotAndPreloadQuery, - checked, - ]); + messageId, + checkFn: checkForSnapShotAndPreloadQuery.mutate, + }); useEffect(() => { check(); diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportsExistenceCheck.ts b/apps/nextjs/src/components/ExportsDialogs/useExportsExistenceCheck.ts new file mode 100644 index 000000000..21f61d0f4 --- /dev/null +++ b/apps/nextjs/src/components/ExportsDialogs/useExportsExistenceCheck.ts @@ -0,0 +1,48 @@ +import { useCallback, useState } from "react"; + +import { aiLogger } from "@oakai/logger"; +import * as Sentry from "@sentry/react"; + +const log = aiLogger("exports"); + +export const useExportsExistenceCheck = ({ + success, + data, + chatId, + messageId, + checkFn, +}: { + success?: boolean; + data?: T; + chatId: string; + messageId?: string; + checkFn: (arg: { chatId: string; data: T; messageId: string }) => void; +}) => { + const [checked, setChecked] = useState(false); + + const check = useCallback(() => { + if (success && data && !checked) { + try { + if (!messageId) { + throw new Error("messageId is undefined"); + } + checkFn({ + chatId, + data, + messageId, + }); + setChecked(true); + } catch (cause) { + const error = new Error("Failed to check for existing export", { + cause, + }); + log.error(error); + Sentry.captureException(error, { + extra: { chatId, data, messageId }, + }); + } + } + }, [success, data, chatId, checkFn, checked, setChecked, messageId]); + + return check; +}; diff --git a/packages/aila/src/core/Aila.ts b/packages/aila/src/core/Aila.ts index d3b0d9939..05c31f14e 100644 --- a/packages/aila/src/core/Aila.ts +++ b/packages/aila/src/core/Aila.ts @@ -6,6 +6,7 @@ import { DEFAULT_RAG_LESSON_PLANS, } from "../constants"; import { AilaCategorisation } from "../features/categorisation"; +import { AilaSnapshotStore } from "../features/snapshotStore"; import { AilaAnalyticsFeature, AilaErrorReportingFeature, @@ -42,6 +43,7 @@ export class Aila implements AilaServices { private _chatLlmService: LLMService; private _moderation?: AilaModerationFeature; private _options: AilaOptionsWithDefaultFallbackValues; + private _snapshotStore: AilaSnapshotStore; private _persistence: AilaPersistenceFeature[] = []; private _threatDetection?: AilaThreatDetectionFeature; private _prisma: PrismaClientWithAccelerate; @@ -85,6 +87,7 @@ export class Aila implements AilaServices { this._options, options.services?.moderationAiClient, ); + this._snapshotStore = AilaFeatureFactory.createSnapshotStore(this); this._persistence = AilaFeatureFactory.createPersistence( this, this._options, @@ -153,6 +156,10 @@ export class Aila implements AilaServices { return this._lesson.plan; } + public get snapshotStore() { + return this._snapshotStore; + } + public get persistence() { return this._persistence; } diff --git a/packages/aila/src/core/AilaFeatureFactory.ts b/packages/aila/src/core/AilaFeatureFactory.ts index 6f319b65a..c98f9df46 100644 --- a/packages/aila/src/core/AilaFeatureFactory.ts +++ b/packages/aila/src/core/AilaFeatureFactory.ts @@ -11,6 +11,7 @@ import { OpenAiModerator, } from "../features/moderation/moderators/OpenAiModerator"; import { AilaPrismaPersistence } from "../features/persistence/adaptors/prisma"; +import { AilaSnapshotStore } from "../features/snapshotStore"; import { AilaThreatDetection } from "../features/threatDetection"; import { AilaAnalyticsFeature, @@ -55,6 +56,10 @@ export class AilaFeatureFactory { return undefined; } + static createSnapshotStore(aila: AilaServices) { + return new AilaSnapshotStore({ aila }); + } + static createPersistence( aila: AilaServices, options: AilaOptions, diff --git a/packages/aila/src/core/AilaServices.ts b/packages/aila/src/core/AilaServices.ts index 185905119..9a713530e 100644 --- a/packages/aila/src/core/AilaServices.ts +++ b/packages/aila/src/core/AilaServices.ts @@ -1,5 +1,6 @@ import { AilaAnalytics } from "../features/analytics/AilaAnalytics"; import { AilaErrorReporter } from "../features/errorReporting"; +import { AilaSnapshotStore } from "../features/snapshotStore"; import { AilaAnalyticsFeature, AilaModerationFeature, @@ -51,6 +52,7 @@ export interface AilaServices { readonly threatDetection?: AilaThreatDetectionFeature; readonly chat: AilaChatService; readonly lesson: AilaLessonService; + readonly snapshotStore: AilaSnapshotStore; readonly persistence?: AilaPersistenceFeature[]; readonly moderation?: AilaModerationFeature; readonly plugins: AilaPlugin[]; diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 7c49b6ace..c88bc798d 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -239,13 +239,11 @@ export class AilaChat implements AilaChatService { private accumulatedText() { const accumulated = this._chunks?.join(""); - return accumulated; + return accumulated ?? ""; } private async reportUsageMetrics() { - await this._aila.analytics?.reportUsageMetrics( - this.accumulatedText() ?? "", - ); + await this._aila.analytics?.reportUsageMetrics(this.accumulatedText()); } private async persistGeneration(status: AilaGenerationStatus) { @@ -300,9 +298,6 @@ export class AilaChat implements AilaChatService { private appendAssistantMessage() { const content = this.accumulatedText(); - if (!content) { - return; - } const assistantMessage: Message = { id: generateMessageId({ role: "assistant" }), role: "assistant", @@ -366,15 +361,21 @@ export class AilaChat implements AilaChatService { await this.reportUsageMetrics(); this.applyEdits(); const assistantMessage = this.appendAssistantMessage(); - if (assistantMessage) { - await this.enqueueMessageId(assistantMessage.id); - } - + await this.enqueueMessageId(assistantMessage.id); + await this.saveSnapshot({ messageId: assistantMessage.id }); await this.moderate(); await this.persistChat(); await this.persistGeneration("SUCCESS"); } + public async saveSnapshot({ messageId }: { messageId: string }) { + await this._aila.snapshotStore.saveSnapshot({ + messageId, + lessonPlan: this._aila.lesson.plan, + trigger: "ASSISTANT_MESSAGE", + }); + } + public async moderate() { if (this._aila.options.useModeration) { invariant(this._aila.moderation, "Moderation not initialised"); diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index 8a30cc27e..e8b67461f 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -95,10 +95,7 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { 2, ); - console.log( - "Got RAG content, length:", - stringifiedRelevantLessonPlans.length, - ); + log.info("Got RAG content, length:", stringifiedRelevantLessonPlans.length); return { ragLessonPlans: relevantLessonPlans, diff --git a/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts new file mode 100644 index 000000000..f76585525 --- /dev/null +++ b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts @@ -0,0 +1,52 @@ +import { LessonSnapshots } from "@oakai/core"; +import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; +import { LessonSnapshotTrigger } from "@prisma/client"; +import invariant from "tiny-invariant"; + +import { AilaServices } from "../../core"; +import { LooseLessonPlan } from "../../protocol/schema"; + +export class AilaSnapshotStore { + protected _name: string; + protected _aila: AilaServices; + private _prisma: PrismaClientWithAccelerate; + + constructor({ + name = "AilaSnapshotStore", + aila, + prisma, + }: { + name?: string; + aila: AilaServices; + prisma?: PrismaClientWithAccelerate; + }) { + this._name = name; + this._aila = aila; + this._prisma = prisma ?? globalPrisma; + } + + get name() { + return this._name; + } + + public async saveSnapshot({ + messageId, + lessonPlan, + trigger, + }: { + messageId: string; + lessonPlan: LooseLessonPlan; + trigger: LessonSnapshotTrigger; + }) { + invariant(this._aila.userId, "userId is required for saving snapshots"); + + const lessonSnapshots = new LessonSnapshots(this._prisma); + await lessonSnapshots.create({ + userId: this._aila.userId, + chatId: this._aila.chatId, + snapshot: lessonPlan, + messageId, + trigger, + }); + } +} diff --git a/packages/aila/src/features/snapshotStore/index.ts b/packages/aila/src/features/snapshotStore/index.ts new file mode 100644 index 000000000..66da08ebb --- /dev/null +++ b/packages/aila/src/features/snapshotStore/index.ts @@ -0,0 +1 @@ +export { AilaSnapshotStore } from "./AilaSnapshotStore"; diff --git a/packages/api/src/export/exportAdditionalMaterialsDoc.ts b/packages/api/src/export/exportAdditionalMaterialsDoc.ts index 72715be6d..2617bf7d6 100644 --- a/packages/api/src/export/exportAdditionalMaterialsDoc.ts +++ b/packages/api/src/export/exportAdditionalMaterialsDoc.ts @@ -1,5 +1,6 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportAdditionalMaterials } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; @@ -8,7 +9,6 @@ import * as Sentry from "@sentry/nextjs"; import { ailaGetExportBySnapshotId, - ailaGetOrSaveSnapshot, ailaSaveExport, OutputSchema, reportErrorResult, @@ -41,12 +41,13 @@ export async function exportAdditionalMaterialsDoc({ }; } - const lessonSnapshot = await ailaGetOrSaveSnapshot({ - prisma: ctx.prisma, + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const lessonSnapshot = await lessonSnapshots.getOrSaveSnapshot({ userId: ctx.auth.userId, chatId: input.chatId, messageId: input.messageId, snapshot: input.data, + trigger: "EXPORT_BY_USER", }); Sentry.addBreadcrumb({ diff --git a/packages/api/src/export/exportLessonPlan.ts b/packages/api/src/export/exportLessonPlan.ts index 444a80c6c..3847e38ad 100644 --- a/packages/api/src/export/exportLessonPlan.ts +++ b/packages/api/src/export/exportLessonPlan.ts @@ -1,5 +1,6 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocLessonPlan } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; @@ -8,7 +9,6 @@ import * as Sentry from "@sentry/nextjs"; import { ailaGetExportBySnapshotId, - ailaGetOrSaveSnapshot, ailaSaveExport, OutputSchema, reportErrorResult, @@ -41,12 +41,13 @@ export async function exportLessonPlan({ }; } - const lessonSnapshot = await ailaGetOrSaveSnapshot({ - prisma: ctx.prisma, + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const lessonSnapshot = await lessonSnapshots.getOrSaveSnapshot({ userId: ctx.auth.userId, chatId: input.chatId, messageId: input.messageId, snapshot: input.data, + trigger: "EXPORT_BY_USER", }); Sentry.addBreadcrumb({ diff --git a/packages/api/src/export/exportLessonSlides.ts b/packages/api/src/export/exportLessonSlides.ts index b5fc68782..a129f1211 100644 --- a/packages/api/src/export/exportLessonSlides.ts +++ b/packages/api/src/export/exportLessonSlides.ts @@ -1,5 +1,6 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportSlidesFullLesson } from "@oakai/exports"; import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; @@ -8,7 +9,6 @@ import * as Sentry from "@sentry/nextjs"; import { ailaGetExportBySnapshotId, - ailaGetOrSaveSnapshot, ailaSaveExport, OutputSchema, reportErrorResult, @@ -41,12 +41,13 @@ export async function exportLessonSlides({ }; } - const lessonSnapshot = await ailaGetOrSaveSnapshot({ - prisma: ctx.prisma, + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const lessonSnapshot = await lessonSnapshots.getOrSaveSnapshot({ userId: ctx.auth.userId, chatId: input.chatId, messageId: input.messageId, snapshot: input.data, + trigger: "EXPORT_BY_USER", }); Sentry.addBreadcrumb({ diff --git a/packages/api/src/export/exportQuizDoc.ts b/packages/api/src/export/exportQuizDoc.ts index f23f2ab0b..0e6415e4f 100644 --- a/packages/api/src/export/exportQuizDoc.ts +++ b/packages/api/src/export/exportQuizDoc.ts @@ -1,5 +1,6 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocQuiz } from "@oakai/exports"; import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; @@ -9,7 +10,6 @@ import { z } from "zod"; import { ailaGetExportBySnapshotId, - ailaGetOrSaveSnapshot, ailaSaveExport, OutputSchema, reportErrorResult, @@ -48,12 +48,13 @@ export async function exportQuizDoc({ }; } - const lessonSnapshot = await ailaGetOrSaveSnapshot({ - prisma: ctx.prisma, + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const lessonSnapshot = await lessonSnapshots.getOrSaveSnapshot({ userId: ctx.auth.userId, chatId: input.chatId, messageId: input.messageId, snapshot: input.lessonSnapshot, + trigger: "EXPORT_BY_USER", }); Sentry.addBreadcrumb({ diff --git a/packages/api/src/export/exportWorksheets.ts b/packages/api/src/export/exportWorksheets.ts index fd7093edf..5a69da542 100644 --- a/packages/api/src/export/exportWorksheets.ts +++ b/packages/api/src/export/exportWorksheets.ts @@ -1,5 +1,6 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocsWorksheet } from "@oakai/exports"; import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; @@ -8,7 +9,6 @@ import * as Sentry from "@sentry/nextjs"; import { ailaGetExportBySnapshotId, - ailaGetOrSaveSnapshot, ailaSaveExport, OutputSchema, reportErrorResult, @@ -41,12 +41,13 @@ export async function exportWorksheets({ }; } - const lessonSnapshot = await ailaGetOrSaveSnapshot({ - prisma: ctx.prisma, + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const lessonSnapshot = await lessonSnapshots.getOrSaveSnapshot({ userId: ctx.auth.userId, chatId: input.chatId, messageId: input.messageId, snapshot: input.data, + trigger: "EXPORT_BY_USER", }); Sentry.addBreadcrumb({ diff --git a/packages/api/src/router/exports.ts b/packages/api/src/router/exports.ts index f191de358..e81f44cc1 100644 --- a/packages/api/src/router/exports.ts +++ b/packages/api/src/router/exports.ts @@ -1,28 +1,22 @@ import { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; +import { LessonSnapshots } from "@oakai/core"; import { sendEmail } from "@oakai/core/src/utils/sendEmail"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { - LessonDeepPartial, exportDocLessonPlanSchema, exportDocQuizSchema, exportSlidesFullLessonSchema, exportDocsWorksheetSchema, exportQuizDesignerSlides, } from "@oakai/exports"; -import { - ExportableQuizAppState, - exportableQuizAppStateSchema, -} from "@oakai/exports/src/schema/input.schema"; -import { DeepPartial } from "@oakai/exports/src/types"; +import { exportableQuizAppStateSchema } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import { LessonExportType } from "@prisma/client"; import * as Sentry from "@sentry/nextjs"; import { kv } from "@vercel/kv"; -import crypto from "crypto"; import { z } from "zod"; -import { LessonPlanJsonSchema } from "../../../aila/src/protocol/schema"; import { exportAdditionalMaterialsDoc } from "../export/exportAdditionalMaterialsDoc"; import { exportLessonPlan } from "../export/exportLessonPlan"; import { exportLessonSlides } from "../export/exportLessonSlides"; @@ -33,168 +27,6 @@ import { router } from "../trpc"; const log = aiLogger("exports"); -const JsonSchemaString = JSON.stringify(LessonPlanJsonSchema); - -const LESSON_JSON_SCHEMA_HASH = crypto - .createHash("sha256") - .update(JsonSchemaString) - .digest("hex"); - -function getSnapshotHash(snapshot: LessonDeepPartial) { - const hash = crypto - .createHash("sha256") - .update(JSON.stringify(snapshot)) - .digest("hex"); - - return hash; -} -export async function ailaCheckIfSnapshotExists({ - prisma, - userId, - chatId, - snapshot, -}: { - prisma: Pick; - userId: string; - chatId: string; - snapshot: LessonDeepPartial; -}) { - /** - * Prisma types complained when passing the JSON schema directly to the Prisma - */ - const jsonSchema = JSON.parse(JsonSchemaString); - // get latest lesson schema for given hash - let lessonSchema = await prisma.lessonSchema.findFirst({ - where: { - hash: LESSON_JSON_SCHEMA_HASH, - }, - orderBy: { - createdAt: "desc", - }, - cacheStrategy: { ttl: 60 * 5, swr: 60 * 2 }, - }); - - if (!lessonSchema) { - // create lesson schema if not found - lessonSchema = await prisma.lessonSchema.create({ - data: { - hash: LESSON_JSON_SCHEMA_HASH, - jsonSchema, - }, - }); - } - - const hash = getSnapshotHash(snapshot); - - // attempt to find existing snapshot - const existingSnapshot = await prisma.lessonSnapshot.findFirst({ - where: { - userId, - chatId, - hash, - }, - orderBy: { - createdAt: "desc", - }, - }); - - return { - existingSnapshot, - lessonSchema, - hash, - }; -} -export async function ailaGetOrSaveSnapshot({ - prisma, - userId, - chatId, - messageId, - snapshot, -}: { - prisma: Pick; - userId: string; - chatId: string; - messageId: string; - snapshot: LessonDeepPartial; -}) { - const { existingSnapshot, lessonSchema, hash } = - await ailaCheckIfSnapshotExists({ - prisma, - userId, - chatId, - snapshot, - }); - - if (existingSnapshot) { - return existingSnapshot; - } - - const lessonJson = JSON.stringify(snapshot); - - const lessonSnapshot = await prisma.lessonSnapshot.create({ - data: { - userId, - chatId, - messageId, - lessonSchemaId: lessonSchema.id, - hash, - lessonJson, - trigger: "EXPORT_BY_USER", - }, - }); - - return lessonSnapshot; -} - -export async function qdGetOrSaveSnapshot({ - prisma, - userId, - sessionId, - messageId, - snapshot, -}: { - prisma: PrismaClientWithAccelerate; - userId: string; - sessionId: string; - messageId: string; - snapshot: DeepPartial; -}) { - /** - * Prisma types complained when passing the JSON schema directly to the Prisma - */ - - const hash = getSnapshotHash(snapshot); - - // attempt to find existing snapshot - const existingSnapshot = await prisma.qdSnapshot.findFirst({ - where: { - userId, - sessionId, - hash, - }, - orderBy: { - createdAt: "desc", - }, - }); - - if (existingSnapshot) { - return existingSnapshot; - } - - const lessonSnapshot = await prisma.qdSnapshot.create({ - data: { - userId, - sessionId, - messageId, - hash, - qdJson: JSON.stringify(snapshot), - trigger: "EXPORT_BY_USER", - }, - }); - - return lessonSnapshot; -} - export async function ailaSaveExport({ auth, prisma, @@ -351,26 +183,25 @@ export const exportsRouter = router({ z.object({ data: exportDocLessonPlanSchema.passthrough(), chatId: z.string(), + messageId: z.string(), }), ) .mutation(async ({ input, ctx }) => { try { const userId = ctx.auth.userId; - const { chatId, data } = input; - const { existingSnapshot } = await ailaCheckIfSnapshotExists({ - prisma: ctx.prisma, + const { chatId, data, messageId } = input; + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ userId, chatId, snapshot: data, + trigger: "EXPORT_BY_USER", + messageId, }); - if (!existingSnapshot) { - log.info("No existing snapshot found"); - return; - } const exportData = await ailaGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: existingSnapshot.id, + snapshotId, exportType: "LESSON_PLAN_DOC", }); if (exportData) { @@ -394,26 +225,25 @@ export const exportsRouter = router({ z.object({ data: exportSlidesFullLessonSchema.passthrough(), chatId: z.string(), + messageId: z.string(), }), ) .mutation(async ({ input, ctx }) => { try { const userId = ctx.auth.userId; - const { chatId, data } = input; - const { existingSnapshot } = await ailaCheckIfSnapshotExists({ - prisma: ctx.prisma, + const { chatId, data, messageId } = input; + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ userId, chatId, snapshot: data, + trigger: "EXPORT_BY_USER", + messageId, }); - if (!existingSnapshot) { - log.info("No existing snapshot found"); - return; - } // find the latest export for this snapshot const exportData = await ailaGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: existingSnapshot.id, + snapshotId, exportType: "LESSON_SLIDES_SLIDES", }); if (exportData) { @@ -443,7 +273,8 @@ export const exportsRouter = router({ .output(outputSchema) .mutation(async ({ input, ctx }) => { try { - const user = await clerkClient.users.getUser(ctx.auth.userId); + const { userId } = ctx.auth; + const user = await clerkClient.users.getUser(userId); const userEmail = user?.emailAddresses[0]?.emailAddress; const exportType = "LESSON_SLIDES_SLIDES"; @@ -454,23 +285,26 @@ export const exportsRouter = router({ }; } - const qdSnapshot = await qdGetOrSaveSnapshot({ - prisma: ctx.prisma, - userId: ctx.auth.userId, - sessionId: input.chatId, - messageId: input.messageId, - snapshot: input.data, + const { chatId, messageId, data: snapshot } = input; + + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ + userId, + chatId, + snapshot, + trigger: "EXPORT_BY_USER", + messageId, }); Sentry.addBreadcrumb({ category: "exportQuizDesignerSlides", message: "Got or saved snapshot", - data: { qdSnapshot }, + data: { snapshot }, }); const exportData = await qdGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: qdSnapshot.id, + snapshotId, exportType, }); @@ -527,9 +361,9 @@ export const exportsRouter = router({ await qdSaveExport({ auth: ctx.auth, prisma: ctx.prisma, - snapshotId: qdSnapshot.id, + snapshotId, exportType, - data, + data: result.data, }); const output: OutputSchema = { @@ -573,26 +407,24 @@ export const exportsRouter = router({ z.object({ data: exportDocLessonPlanSchema.passthrough(), chatId: z.string(), + messageId: z.string(), }), ) .mutation(async ({ input, ctx }) => { try { const userId = ctx.auth.userId; - const { chatId, data } = input; - const { existingSnapshot } = await ailaCheckIfSnapshotExists({ - prisma: ctx.prisma, + const { chatId, data: snapshot, messageId } = input; + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ userId, chatId, - snapshot: data, + snapshot, + trigger: "EXPORT_BY_USER", + messageId, }); - if (!existingSnapshot) { - log.info("No existing snapshot found"); - return; - } - const exportData = await ailaGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: existingSnapshot.id, + snapshotId, exportType: "ADDITIONAL_MATERIALS_DOCS", }); if (exportData) { @@ -637,26 +469,24 @@ export const exportsRouter = router({ z.object({ data: exportDocsWorksheetSchema.passthrough(), chatId: z.string(), + messageId: z.string(), }), ) .mutation(async ({ input, ctx }) => { try { const userId = ctx.auth.userId; - const { chatId, data } = input; - const { existingSnapshot } = await ailaCheckIfSnapshotExists({ - prisma: ctx.prisma, + const { chatId, data, messageId } = input; + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ userId, chatId, snapshot: data, + trigger: "EXPORT_BY_USER", + messageId, }); - if (!existingSnapshot) { - log.info("No existing snapshot found"); - return; - } - const exportData = await ailaGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: existingSnapshot.id, + snapshotId, exportType: "WORKSHEET_SLIDES", }); if (exportData) { @@ -703,29 +533,27 @@ export const exportsRouter = router({ data: exportDocQuizSchema.passthrough(), lessonSnapshot: z.object({}).passthrough(), chatId: z.string(), + messageId: z.string(), }), ) .mutation(async ({ input, ctx }) => { try { const userId = ctx.auth.userId; - const { chatId, data, lessonSnapshot } = input; - const { existingSnapshot } = await ailaCheckIfSnapshotExists({ - prisma: ctx.prisma, + const { chatId, data, lessonSnapshot, messageId } = input; + const lessonSnapshots = new LessonSnapshots(ctx.prisma); + const { id: snapshotId } = await lessonSnapshots.getOrSaveSnapshot({ userId, chatId, snapshot: lessonSnapshot, + trigger: "EXPORT_BY_USER", + messageId, }); - if (!existingSnapshot) { - log.info("No existing snapshot found"); - return; - } - const exportType = data.quizType === "exit" ? "EXIT_QUIZ_DOC" : "STARTER_QUIZ_DOC"; const exportData = await ailaGetExportBySnapshotId({ prisma: ctx.prisma, - snapshotId: existingSnapshot.id, + snapshotId, exportType, }); if (exportData) { diff --git a/packages/api/src/router/testSupport/prepareUser.ts b/packages/api/src/router/testSupport/prepareUser.ts index 3dce91729..ce7a8b544 100644 --- a/packages/api/src/router/testSupport/prepareUser.ts +++ b/packages/api/src/router/testSupport/prepareUser.ts @@ -1,6 +1,6 @@ import { clerkClient } from "@clerk/nextjs/server"; -import { waitUntil } from "@vercel/functions"; import { aiLogger } from "@oakai/logger"; +import { waitUntil } from "@vercel/functions"; import os from "os"; import { z } from "zod"; @@ -88,7 +88,7 @@ const deleteLastUsedTestUser = async () => { }); if (lastUsedTestUser) { - console.log( + log.info( "Deleting oldest test user", lastUsedTestUser.primaryEmailAddress?.emailAddress, ); diff --git a/packages/core/src/models/lessonSnapshots.ts b/packages/core/src/models/lessonSnapshots.ts index 8164900c3..c1314aae2 100644 --- a/packages/core/src/models/lessonSnapshots.ts +++ b/packages/core/src/models/lessonSnapshots.ts @@ -1,4 +1,8 @@ -import { LessonSnapshot, PrismaClientWithAccelerate } from "@oakai/db"; +import { + LessonSnapshot, + LessonSnapshotTrigger, + PrismaClientWithAccelerate, +} from "@oakai/db"; import crypto from "crypto"; import { @@ -37,16 +41,22 @@ export class LessonSnapshots { }); } - async create( - userId: string, - chatId: string, + async create({ + userId, + chatId, + messageId, + snapshot, + trigger, + }: { + userId: string; + chatId: string; /** * This is the message ID of the most recent assistant message */ - messageId: string, - snapshot: Snapshot, - ): Promise { - const lessonJson = JSON.stringify(snapshot); + messageId: string; + snapshot: Snapshot; + trigger: LessonSnapshotTrigger; + }): Promise { const hash = getSnapshotHash(snapshot); let lessonSchema = await this.prisma.lessonSchema.findFirst({ @@ -72,9 +82,9 @@ export class LessonSnapshots { chatId, messageId, hash, - lessonJson, + lessonJson: snapshot, lessonSchemaId: lessonSchema.id, - trigger: "MODERATION", + trigger, }, }); } @@ -84,11 +94,13 @@ export class LessonSnapshots { chatId, messageId, snapshot, + trigger, }: { userId: string; chatId: string; messageId: string; snapshot: Snapshot; + trigger: LessonSnapshotTrigger; }) { /** * Prisma types complained when passing the JSON schema directly to the Prisma @@ -132,62 +144,6 @@ export class LessonSnapshots { return existingSnapshot; } - const lessonJson = JSON.stringify(snapshot); - - const lessonSnapshot = await this.prisma.lessonSnapshot.create({ - data: { - userId, - chatId, - messageId, - lessonSchemaId: lessonSchema.id, - hash, - lessonJson, - trigger: "EXPORT_BY_USER", - }, - }); - - return lessonSnapshot; - } - - async createModerationSnapshot({ - userId, - chatId, - messageId, - snapshot, - }: { - userId: string; - chatId: string; - messageId: string; - snapshot: Snapshot; - }) { - /** - * Prisma types complained when passing the JSON schema directly to the Prisma - */ - const jsonSchema = JSON.parse(JsonSchemaString); - // get latest lesson schema for given hash - // note: we should be storing this at build time so we don't have to do this - let lessonSchema = await this.prisma.lessonSchema.findFirst({ - where: { - hash: LESSON_JSON_SCHEMA_HASH, - }, - orderBy: { - createdAt: "desc", - }, - }); - - if (!lessonSchema) { - // create lesson schema if not found - lessonSchema = await this.prisma.lessonSchema.create({ - data: { - hash: LESSON_JSON_SCHEMA_HASH, - jsonSchema, - }, - }); - } - - const hash = getSnapshotHash(snapshot); - const lessonJson = JSON.stringify(snapshot); - const lessonSnapshot = await this.prisma.lessonSnapshot.create({ data: { userId, @@ -195,8 +151,8 @@ export class LessonSnapshots { messageId, lessonSchemaId: lessonSchema.id, hash, - lessonJson, - trigger: "MODERATION", + lessonJson: snapshot, + trigger, }, }); diff --git a/packages/core/src/models/moderations.ts b/packages/core/src/models/moderations.ts index c613b2431..49cd046d3 100644 --- a/packages/core/src/models/moderations.ts +++ b/packages/core/src/models/moderations.ts @@ -43,12 +43,14 @@ export class Moderations { justification?: string; lesson: Snapshot; }): Promise { - const lessonSnapshot = await this.lessonSnapshots.createModerationSnapshot({ - userId, - chatId: appSessionId, - messageId, - snapshot: lesson, - }); + const { id: lessonSnapshotId } = + await this.lessonSnapshots.getOrSaveSnapshot({ + userId, + chatId: appSessionId, + messageId, + snapshot: lesson, + trigger: "MODERATION", + }); return this.prisma.moderation.create({ data: { @@ -57,7 +59,7 @@ export class Moderations { justification, appSessionId, messageId, - lessonSnapshotId: lessonSnapshot.id, + lessonSnapshotId, }, }); } diff --git a/packages/db/prisma/migrations/20241016113705_lesson_snapshot_trigger_message/migration.sql b/packages/db/prisma/migrations/20241016113705_lesson_snapshot_trigger_message/migration.sql new file mode 100644 index 000000000..7f9062116 --- /dev/null +++ b/packages/db/prisma/migrations/20241016113705_lesson_snapshot_trigger_message/migration.sql @@ -0,0 +1,2 @@ +ALTER TYPE "public"."LessonSnapshotTrigger" +ADD VALUE 'ASSISTANT_MESSAGE'; diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma index 103250d55..35c2d270c 100644 --- a/packages/db/prisma/schema.prisma +++ b/packages/db/prisma/schema.prisma @@ -862,6 +862,7 @@ enum LessonExportType { } enum LessonSnapshotTrigger { + ASSISTANT_MESSAGE EXPORT_BY_USER MODERATION From a0cedec86924a1bf0cf2466bd7a5ef106d7f8272 Mon Sep 17 00:00:00 2001 From: MG Date: Mon, 21 Oct 2024 17:58:51 +0100 Subject: [PATCH 009/127] fix: remove lessonReferences from prompt (#253) --- .../prompts/lesson-assistant/parts/interactingWithTheUser.ts | 5 ----- .../src/prompts/lesson-assistant/parts/lessonComplete.ts | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts index 78b5a3be8..06d78b91b 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts @@ -67,11 +67,6 @@ This may be indicative of an application error. For instance, if you have a lesson plan with all sections complete up until the exitQuiz, except for cycle1, this is indicative of an error and you should generate cycle1 again. Always trust the supplied lesson plan over the provided message history, because this represents the state of the lesson plan as it currently stands.`, - hasRelevantLessons - ? `* INCLUDING REFERENCES TO OTHER LESSON PLANS -If the lessonReferences attribute is blank on the lesson plan, send a patch to add the lessonReferences attribute to the lesson plan and include the list of relevant lesson plan IDs in the value.` - : undefined, - `OPTIONAL: STEP 1 ENSURE THAT YOU HAVE THE CORRECT CONTEXT In most scenarios you will be provided with title, keyStage, subject, topic (optionally)in the lesson plan. If they are not present, ask the user for them. diff --git a/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts b/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts index 66062716f..ce4371a1d 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts @@ -2,7 +2,7 @@ export const lessonComplete = () => `ONCE THE LESSON IS COMPLETE The lesson is complete when all of the keys have values. Until then it is still in a draft state. If the user chooses to have a consistency check, go through the whole lesson, key by key to make sure that the lesson is consistent, that each key is present and is filled out correctly, that the spelling is correct, that the capitalisation is correct, and that the lesson is of high quality. Ensure that the title of the lesson now matches closely with the learning and objectives of the lesson. -Each of these keys in the lesson plan should have a value and valid content: title, subject, topic, keyStage, basedOn, lessonReferences, learningOutcome, learningCycles, priorKnowledge, keyLearningPoints, misconceptions, keywords, starterQuiz, cycle1, cycle2, cycle3, exitQuiz, additionalMaterials. +Each of these keys in the lesson plan should have a value and valid content: title, subject, topic, keyStage, basedOn, learningOutcome, learningCycles, priorKnowledge, keyLearningPoints, misconceptions, keywords, starterQuiz, cycle1, cycle2, cycle3, exitQuiz, additionalMaterials. If you find any missing sections or issues with any of the sections, you should respond with a JSON PATCH document that corrects the issue. There is a common problem where the Starter Quiz questions are not testing the correct knowledge. Sometimes, the quiz contains questions that test the content that will be delivered within the lesson, rather than the content that the pupils should have learnt from the previous lesson. From 01af24bc1ef1f61c8cda6fc18289a672213d9964 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Tue, 22 Oct 2024 08:49:41 +0000 Subject: [PATCH 010/127] build(release v1.11.0): See CHANGE_LOG.md --- CHANGE_LOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index 7fa17e0c4..b5c4d5401 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -1,3 +1,18 @@ +# [1.11.0](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.10.2...v1.11.0) (2024-10-22) + + +### Bug Fixes + +* add migration for ingest config ([#239](https://github.com/oaknational/oak-ai-lesson-assistant/issues/239)) ([b4b9e60](https://github.com/oaknational/oak-ai-lesson-assistant/commit/b4b9e60d01d4f7dad213d2a89a41fe327ba606ec)) +* ingest config migration fix ([#243](https://github.com/oaknational/oak-ai-lesson-assistant/issues/243)) ([1bdf99a](https://github.com/oaknational/oak-ai-lesson-assistant/commit/1bdf99a5ce8be819107e36295accf67aed6e4d25)) +* **sec:** update next ([#240](https://github.com/oaknational/oak-ai-lesson-assistant/issues/240)) ([bcda9d7](https://github.com/oaknational/oak-ai-lesson-assistant/commit/bcda9d7b2a7783d8f23882490f85c9c6b64b5add)) + + +### Features + +* add dev-turbo mode to use turbopack ([#248](https://github.com/oaknational/oak-ai-lesson-assistant/issues/248)) ([6bd8c40](https://github.com/oaknational/oak-ai-lesson-assistant/commit/6bd8c40b5b40a56dfb94ae3aace86efbeb77e50c)) +* relevant lessons [AI-607] ([#238](https://github.com/oaknational/oak-ai-lesson-assistant/issues/238)) ([73ffd29](https://github.com/oaknational/oak-ai-lesson-assistant/commit/73ffd2993a215abfa9cdd1c0706dbf7106f9e006)) + ## [1.10.2](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.10.1...v1.10.2) (2024-10-16) From 13729ca4d0d64f436309575bf8be50640604c710 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:18:32 +0200 Subject: [PATCH 011/127] fix: report chat completion errors (#256) --- packages/aila/src/core/chat/AilaStreamHandler.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 6c4a555cf..1f171825c 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -47,6 +47,9 @@ export class AilaStreamHandler { this._isStreaming = false; try { await this._chat.complete(); + } catch (e) { + this._chat.aila.errorReporter?.reportError(e); + throw new AilaChatError("Chat completion failed", { cause: e }); } finally { this.closeController(); } From d531a50229c604fa71157609fcde181eefb5e9b3 Mon Sep 17 00:00:00 2001 From: Tom Wise <79859203+tomwisecodes@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:40:36 +0100 Subject: [PATCH 012/127] feat: local db setup scripts (#132) Co-authored-by: Simon Rose --- README.md | 91 +++----- packages/db/package.json | 8 +- .../db/scripts/import-export/db_export.sh | 12 +- .../db/scripts/import-export/db_publish.sh | 12 +- packages/db/scripts/import-export/db_seed.sh | 171 ++++++++++++++ packages/db/scripts/import-export/tables.txt | 6 + .../db/scripts/import-export/validate_data.ts | 211 ++++++++++++++++++ pnpm-lock.yaml | 3 + 8 files changed, 441 insertions(+), 73 deletions(-) create mode 100644 packages/db/scripts/import-export/db_seed.sh create mode 100644 packages/db/scripts/import-export/tables.txt create mode 100644 packages/db/scripts/import-export/validate_data.ts diff --git a/README.md b/README.md index b239f0066..5e3c3f28e 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,10 @@ Oak AI Lesson Assistant is a project focused on experimenting with AI models and - [Installation](#installation) - [Prerequisites](#prerequisites) - [Turborepo](#turborepo) - - [Postgres setup](#postgres-setup) - - [With Docker](#with-docker) - - [With Homebrew](#with-homebrew) + - [Postgres Setup](#postgres-setup) + - [Prerequisites](#prerequisites-1) + - [Steps](#steps) + - [Utility Commands](#utility-commands) - [Doppler](#doppler) - [Start the development server](#start-the-development-server) - [Testing](#testing) @@ -33,7 +34,6 @@ Oak AI Lesson Assistant is a project focused on experimenting with AI models and - [Open source acknowledgements](#open-source-acknowledgements) - [License](#license) - ## Introduction Oak AI Lesson Assistant is a project designed to facilitate the development and testing of AI tools and models. The project is structured as a Turborepo monorepo, leveraging various open-source tools and libraries. @@ -56,92 +56,65 @@ This application is structured as a Turborepo monorepo. Install the "turbo" comm pnpm install turbo --global ``` -## Postgres setup +## Postgres Setup -Instructions are available for both Homebrew and Dockerized setups. +### Prerequisites +- This project set up following the Installation steps. +- Docker installed. +- Optional: A Postgres GUI tool (such as pgAdmin or Postico) to view the data. -### With Docker +### Steps -Navigate to the `packages/db` directory: +1. Navigate to the `packages/db` directory: ```shell cd packages/db ``` -Build and run the Docker container to create a database named `oai`, with the username and password both as `oai`, bound to port 5432. It will also install `pgvector` and `postgresql-contrib`. +2. Build and run the Docker container to create a database named `oai`, with the username and password both as `oai`, bound to port 5432. It will also install `pgvector` and `postgresql-contrib`. ```shell pnpm run docker-bootstrap ``` -To run `psql`, ssh into the box using: +3. Seed your database, to do this you have two options: -```shell -pnpm run docker-psql -``` + 3a. Replicate Production/Staging (Slow) -To seed the database: + This will import the schema and tables from production. Note: due to the size of the production database this could take a significant amount of time. -```shell -pnpm run db-push -pnpm run db-seed -``` + ```shell + pnpm run db-restore-from:prd or pnpm run db-restore-from:stg + ``` -From the repo root, then run: + 3b. Local Prisma with Essential Tables Seeded from a Live Environment (Fast) -```shell -pnpm run prompts -``` + 1. Apply the Prisma schema to your local database: -To reset and start fresh: + ```shell + pnpm run db-push + ``` -```shell -pnpm run docker-reset -``` + 2. Seed from stg/prd (where `:prd` can be either `:prd` or `:stg`, matching the Doppler environments). This will only seed the apps table and lesson-related tables used for RAG. -To import a snapshot of production or staging databases into your local instance: + ```shell + pnpm run db-seed-local-from:prd + ``` -```shell -pnpm run docker-reset -pnpm run db-restore-from:prd -``` - -where `:prd` can be either `:prd` or `:stg` (the Doppler environments). - -### With Homebrew +### Utility Commands -Install and start PostgreSQL: +To run `psql`, ssh into the box using: ```shell -brew install postgresql -brew services start postgresql -createdb oai -psql -``` - -In PSQL: - -```sql -sudo -u postgres psql -CREATE USER oai WITH ENCRYPTED PASSWORD 'oai'; -GRANT ALL PRIVILEGES ON DATABASE oai TO oai; -ALTER USER oai WITH SUPERUSER; +pnpm run docker-psql ``` -**Note:** Running in superuser mode is not ideal; consider separate users for migrations and queries. - -Exit PSQL using `\quit`. - -Install PG Vector: +If you need to reset and start fresh: ```shell -brew install pgvector -psql postgres -CREATE EXTENSION vector; +pnpm run docker-reset ``` -Quit PSQL. - ## Doppler We use [Doppler](https://doppler.com) for secrets management. To run the application, you need to set up the Doppler CLI. diff --git a/packages/db/package.json b/packages/db/package.json index cc7110d0e..7a3962e3b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -9,6 +9,10 @@ "db-dump:prd": "docker exec oak-ai-beta pg_dump $(doppler secrets get -c prd --plain DATABASE_ADMIN_URL | sed -e 's/?sslmode=required//g') --no-owner --exclude-table-data public.generations --exclude-table-data public.snippets --exclude-table-data public.downloads", "db-dump:stg": "docker exec oak-ai-beta pg_dump $(doppler secrets get -c stg --plain DATABASE_ADMIN_URL | sed -e 's/?sslmode=required//g') --no-owner --exclude-table-data public.generations --exclude-table-data public.snippets --exclude-table-data public.downloads", "db-dump:stg:homebrew": "pg_dump $(doppler secrets get -c stg --plain DATABASE_ADMIN_URL | sed -e 's/?sslmode=required/?sslcert=./certs/db_stg.pem/g') --no-owner --exclude-table-data public.generations --exclude-table-data public.snippets --exclude-table-data public.downloads", + "db-seed-local-from:stg": "doppler run --config stg -- bash -c 'source scripts/import-export/db_seed.sh stg dev'", + "db-seed-local-from:prd": "doppler run --config prd -- bash -c 'source scripts/import-export/db_seed.sh prd dev'", + "db-seed-stg-from:local": "doppler run --config stg -- bash -c 'source scripts/import-export/db_seed.sh dev stg'", + "db-seed-prd-from:local": "doppler run --config prd -- bash -c 'source scripts/import-export/db_seed.sh dev prd'", "db-export": "DB_ENV=dev doppler run --config dev -- bash scripts/import-export/db_export.sh", "db-export:prd": "DB_ENV=prd doppler run --config prd -- bash scripts/import-export/db_export.sh", "db-export:stg": "DB_ENV=stg doppler run --config stg -- bash scripts/import-export/db_export.sh", @@ -19,8 +23,6 @@ "db-migrate": "pnpm with-env prisma migrate dev", "db-migrate:dev": "pnpm with-env prisma migrate dev", "db-migrate:status": "pnpm with-env prisma migrate status", - "db-migrate:status:stg": "DB_ENV=stg doppler run --config stg prisma migrate status", - "db-migrate:status:prd": "DB_ENV=prd doppler run --config prd prisma migrate status", "db-migrate-resolve-applied:prd": "doppler run --config prd -- prisma migrate resolve --applied", "db-migrate-resolve-applied:stg": "doppler run --config stg -- prisma migrate resolve --applied", "db-migrate-resolve-rolled-back:prd": "doppler run --config prd -- prisma migrate resolve --rolled-back", @@ -50,6 +52,7 @@ "script-add-metadata-to-lesson-summaries": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/processing/add_metadata_to_lesson_summaries.ts", "script-batch-process-clean-questions": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/processing/batch_process_clean_questions.ts", "script-batch-update-clerk-users": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/processing/update_clerk_users.ts", + "validate-data": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/import-export/validate_data.ts", "with-env": "dotenv -e ../../.env --" }, "prettier": "@oakai/prettier-config", @@ -59,6 +62,7 @@ "@types/chunk-text": "^1.0.0", "cheerio": "1.0.0-rc.12", "chunk-text": "^2.0.1", + "csv-parser": "^3.0.0", "graphql-request": "^6.1.0", "openai": "^4.58.1", "p-queue": "^7.4.1", diff --git a/packages/db/scripts/import-export/db_export.sh b/packages/db/scripts/import-export/db_export.sh index 79d7814e1..675cb158c 100644 --- a/packages/db/scripts/import-export/db_export.sh +++ b/packages/db/scripts/import-export/db_export.sh @@ -1,8 +1,5 @@ #!/bin/sh -# The tables to export, in orders. -#TABLES=( "prompt" "generations" "apps" ) -TABLES=( "lesson_plans" "lesson_plan_parts" ) # Absolute path to this script SCRIPT=$(readlink -f "$0") # Absolute path of the directory this script is in @@ -10,8 +7,11 @@ SCRIPTPATH=$(dirname "$SCRIPT") URL_WITHOUT_SCHEMA=`echo "$DATABASE_URL" | cut -f1 -d"?"` echo "Connecting to: $URL_WITHOUT_SCHEMA" -# psql "$URL_WITHOUT_SCHEMA" -c "\copy questions to $SCRIPTPATH/data/questions.csv delimiter ',' csv header;" -for table in ${TABLES[@]}; do - psql "$URL_WITHOUT_SCHEMA" -c "\copy $table to $SCRIPTPATH/data/$table.csv delimiter ',' csv header;" +# Retrieve all table names from the public schema and store them in an array +TABLES=$(psql "$URL_WITHOUT_SCHEMA" -t -c "SELECT tablename FROM pg_tables WHERE schemaname='public';") + +# Export each table to a CSV file +for table in $TABLES; do + psql "$URL_WITHOUT_SCHEMA" -c "\copy $table to '$SCRIPTPATH/data/$table.csv' delimiter ',' csv header;" done diff --git a/packages/db/scripts/import-export/db_publish.sh b/packages/db/scripts/import-export/db_publish.sh index a90f1c8f7..cd74f2a5a 100755 --- a/packages/db/scripts/import-export/db_publish.sh +++ b/packages/db/scripts/import-export/db_publish.sh @@ -1,12 +1,12 @@ #!/bin/sh -#TABLES=("key_stages" "subjects" "key_stage_subjects" "summarisation_strategies" "lessons" "questions" "answers" "lesson_summaries" "transcripts" "snippets" ) -TABLES=("lesson_plans" "lesson_plan_parts") +# Define the tables in the correct order based on dependencies +TABLES="$SCRIPTPATH/tables.txt" + SCRIPT=$(readlink -f "$0") # Absolute path of the directory this script is in SCRIPTPATH=$(dirname "$SCRIPT") - sql="BEGIN; TRUNCATE " echo "Tables: ${TABLES[@]}" @@ -20,16 +20,16 @@ for (( idx=${#TABLES[@]}-1 ; idx>=0 ; idx-- )) do else sql+=", " fi - done -sql+="; COMMIT;" +sql+=" RESTART IDENTITY CASCADE; COMMIT;" echo "$sql" -URL_WITHOUT_SCHEMA=`echo "$DATABASE_ADMIN_URL" | cut -f1 -d"?"` +URL_WITHOUT_SCHEMA=$(echo "$DATABASE_ADMIN_URL" | cut -f1 -d"?") echo "Connecting to: $URL_WITHOUT_SCHEMA" +# Execute the truncation psql "$URL_WITHOUT_SCHEMA?sslmode=verify-ca&sslrootcert=$SCRIPTPATH/../../certs/db_$DB_ENV.pem" -c "$sql" for table in ${TABLES[@]}; do diff --git a/packages/db/scripts/import-export/db_seed.sh b/packages/db/scripts/import-export/db_seed.sh new file mode 100644 index 000000000..facd976f1 --- /dev/null +++ b/packages/db/scripts/import-export/db_seed.sh @@ -0,0 +1,171 @@ +#!/bin/sh + +get_local_database_admin_url() { + read -p "Please enter the local DATABASE_ADMIN_URL (e.g. postgresql://oai:oai@localhost:5432/oai): " local_url + echo "$local_url" +} + + +SOURCE_ENV="${1:-dev}" +TARGET_ENV="${2:-live}" + +if [ "$SOURCE_ENV" != "dev" ] && [ "$SOURCE_ENV" != "stg" ] && [ "$SOURCE_ENV" != "prd" ]; then + echo "Error: Invalid source environment. You must specify 'dev' or 'live' as the source environment." + exit 1 +fi + +if [ "$TARGET_ENV" != "dev" ] && [ "$TARGET_ENV" != "stg" ] && [ "$TARGET_ENV" != "prd" ]; then + echo "Error: Invalid target environment. You must specify 'dev' or 'live' as the target environment." + exit 1 +fi + +# Determine whether to use the user-provided local DATABASE_ADMIN_URL or the one provided by Doppler +if [ "$SOURCE_ENV" = "dev" ]; then + SOURCE_URL=$(get_local_database_admin_url) # Prompt user for local value for dev +else + SOURCE_URL="${DATABASE_ADMIN_URL}" # Use Doppler for stg or prd +fi + +if [ "$TARGET_ENV" = "dev" ]; then + TARGET_URL=$(get_local_database_admin_url) # Prompt user for local value for dev +else + TARGET_URL="${DATABASE_ADMIN_URL}" # Use Doppler for stg or prd +fi + +# Prevent source and target from being the same unless both are 'dev' +if [ "$SOURCE_URL" = "$TARGET_URL" ] && [ "$SOURCE_ENV" != "$TARGET_ENV" ]; then + echo "Error: Source and target environments must be different unless both are 'dev'." + exit 1 +fi + +# Absolute path to this script +SCRIPT=$(readlink -f "$0") +# Absolute path of the directory this script is in +SCRIPTPATH=$(dirname "$SCRIPT")/scripts/import-export + +echo "SCRIPTPATH: $SCRIPTPATH/scripts/import-export" + + + +# Path to tables file +TABLES_FILE="$SCRIPTPATH/tables.txt" + + + +# Check if the tables file exists +if [ ! -f "$TABLES_FILE" ]; then + echo "Error: tables.txt file not found!" + exit 1 +fi + + +# Extract the URL without query parameters +SOURCE_URL_WITHOUT_SCHEMA=$(echo "$SOURCE_URL" | cut -f1 -d"?") +TARGET_URL_WITHOUT_SCHEMA=$(echo "$TARGET_URL" | cut -f1 -d"?") + +echo "Connecting to source: $SOURCE_URL_WITHOUT_SCHEMA" +echo "Connecting to target: $TARGET_URL_WITHOUT_SCHEMA" + +# Ensure environment variables are set +if [ -z "$SOURCE_URL" ] || [ -z "$TARGET_URL" ]; then + echo "Error: One or more required database URLs are not set." + exit 1 +fi + +echo "Starting the seed process from $SOURCE_ENV to $TARGET_ENV..." + +# Get the list of all tables in the public schema and store in TABLES array +TABLES=$(psql "$SOURCE_URL_WITHOUT_SCHEMA" -Atc "SELECT tablename FROM pg_tables WHERE schemaname='public';") + +# Check if tables were found +if [ -z "$TABLES" ]; then + echo "No tables found in the public schema." + exit 1 +fi + +# Log the number of tables found +TABLE_COUNT=$(echo "$TABLES" | wc -w) +echo "Found $TABLE_COUNT tables to export." + +# Check for table export +read -p "Do you want to export tables? (y/n) " confirm_export +if [ "$confirm_export" == "y" ]; then + # Loop through all tables and export each one + for table in $TABLES; do + # Check if the table exists in the tables.txt + if ! grep -q "^$table$" "$TABLES_FILE"; then + echo "Table $table is not listed in $TABLES_FILE, skipping export." + continue + fi + + echo "Exporting table: $table..." + OUTPUT_PATH="$SCRIPTPATH/data/$table.csv" + + # Execute the export command and check if it succeeded + if psql "$SOURCE_URL_WITHOUT_SCHEMA" -c "\copy $table to $OUTPUT_PATH delimiter ',' csv header;"; then + echo "Successfully exported $table to $OUTPUT_PATH" + else + echo "Failed to export $table" >&2 + fi + done + + # Log the completion of the export process + echo "Export process completed." +else + echo "Export process skipped." +fi + +# Ask the user if they want to validate the data +read -p "Do you want to validate the exported data? (y/n) " confirm_validate +if [ "$confirm_validate" == "y" ]; then + # Run validation before truncating data + echo "Running data validation..." + if pnpm run validate-data; then + echo "Data validation successful." + else + echo "Data validation failed." >&2 + exit 1 + fi + + echo "Data is valid." +else + echo "Validation skipped." +fi + +# Ask the user if they want to proceed with seeding +echo "You are about to seed the data into the $TARGET_ENV environment." +read -p "Do you want to continue with the seeding process? (y/n) " confirm_seed +if [ "$confirm_seed" != "y" ]; then + echo "Seeding process aborted." + exit 0 +fi + +# Truncate existing data in the target database before importing new data +echo "Truncating existing data in target database..." +while IFS= read -r table; do + echo "Truncating table: $table..." + if psql "$TARGET_URL_WITHOUT_SCHEMA" -c "TRUNCATE TABLE $table RESTART IDENTITY CASCADE;"; then + echo "Successfully truncated $table" + else + echo "Failed to truncate $table" >&2 + exit 1 + fi +done < "$TABLES_FILE" + +# Publish process +echo "Starting the publish process..." + +while IFS= read -r table; do + echo "Importing data into table: $table..." + INPUT_PATH="$SCRIPTPATH/data/$table.csv" + + if psql "$TARGET_URL_WITHOUT_SCHEMA" -c "\copy $table FROM $INPUT_PATH CSV HEADER;"; then + echo "Successfully imported data into $table" + else + echo "Failed to import data into $table" >&2 + exit 1 + fi +done < "$TABLES_FILE" + +# Log the completion of the publish process +echo "Publish process completed." diff --git a/packages/db/scripts/import-export/tables.txt b/packages/db/scripts/import-export/tables.txt new file mode 100644 index 000000000..84e7faf7b --- /dev/null +++ b/packages/db/scripts/import-export/tables.txt @@ -0,0 +1,6 @@ +apps +key_stages +subjects +lessons +lesson_plans +lesson_plan_parts diff --git a/packages/db/scripts/import-export/validate_data.ts b/packages/db/scripts/import-export/validate_data.ts new file mode 100644 index 000000000..e9696b11b --- /dev/null +++ b/packages/db/scripts/import-export/validate_data.ts @@ -0,0 +1,211 @@ +/** + * + * This script validates the data in the CSV files against the constraints defined in the Prisma schema. + * + **/ +import { Prisma } from "@prisma/client"; +import csvParser from "csv-parser"; +import dotenv from "dotenv"; +import * as fs from "fs"; +import * as path from "path"; + +import { prisma } from "../.."; + +const dataDir = path.join(__dirname, "data"); + +dotenv.config(); + +// Helper function to log messages +const log = (message: string) => { + console.log(`[LOG] ${new Date().toISOString()}: ${message}`); +}; + +// Helper function to get the Prisma model metadata +async function getModelConstraints() { + log("Inferring model constraints from Prisma schema..."); + const modelConstraints: Record = {}; + + const models = Prisma.dmmf.datamodel.models; + + models.forEach((model) => { + if (model.dbName) { + const tablesFilePath = path.join(__dirname, "tables.txt"); + const tables = fs.readFileSync(tablesFilePath, "utf-8").split("\n").map(t => t.trim()); + + if (!tables.includes(model.dbName)) { + return; + } + + console.log(`Processing model: ${model.dbName}`); + log(`Processing model: ${model.dbName}`); + + const nonNullable = model.fields + .filter( + (field) => + field.isRequired && !field.relationName && !field.hasDefaultValue, + ) + .map((field) => field.dbName || field.name); // Use `field.name` as fallback if `field.dbName` is undefined + + const foreignKeys = model.fields + .filter((field) => field.relationName) + .reduce( + (acc, field) => { + const CamelCaseToSnakeCase = (str: string) => { + const newStr = str.replace( + /[A-Z]/g, + (letter) => `_${letter.toLowerCase()}`, + ); + return newStr.replace(/^_/, ""); + }; + + const refTable = CamelCaseToSnakeCase(field.type); + acc[CamelCaseToSnakeCase(field.name)] = refTable; + return acc; + }, + {} as Record, + ); + + modelConstraints[model.dbName] = { + nonNullable, + foreignKeys, + }; + } + }); + + return modelConstraints; +} + +const validateCSV = ( + filePath: string, + nonNullable: string[], + foreignKeys: Record, +) => { + return new Promise((resolve, reject) => { + const errors: string[] = []; + const foreignKeyCheck: Record> = {}; + + log(`Validating CSV file: ${filePath}`); + fs.createReadStream(filePath) + .pipe(csvParser()) + .on("data", (row: any) => { + // Check non-nullable fields + nonNullable.forEach((col) => { + if (!row[col]) { + errors.push( + `NULL value found in non-nullable column '${col}' in row ${JSON.stringify( + row, + )}`, + ); + } + }); + + for (const [fk, refTable] of Object.entries(foreignKeys)) { + if (!foreignKeyCheck[fk]) { + foreignKeyCheck[fk] = new Set(); + } + if (row[fk]) { + foreignKeyCheck[fk]!.add(row[fk]); + } + } + }) + .on("end", async () => { + log(`Completed reading CSV file: ${filePath}`); + + // Validate foriegn keys in CSV + for (const [fk, refTable] of Object.entries(foreignKeys)) { + const ids: string[] = Array.from(foreignKeyCheck[fk] || []); + + function handleTable(table: string) { + if (table === "key_stage") { + return "key_stages"; + } + } + + if (ids.length > 0) { + const refFilePath = path.join( + dataDir, + `${handleTable(refTable)}.csv`, + ); + if (!fs.existsSync(refFilePath)) { + errors.push( + `CSV file for referenced table '${refTable}' does not exist.`, + ); + } else { + const refIds = new Set(); + fs.createReadStream(refFilePath) + .pipe(csvParser()) + .on("data", (row: any) => { + refIds.add(row.id); + }) + .on("end", () => { + ids.forEach((id: string) => { + if (!refIds.has(id)) { + errors.push( + `Broken foreign key: '${fk}' references '${refTable}' but value '${id}' does not exist in table '${refTable}'.`, + ); + } + }); + }); + } + } + } + + if (errors.length > 0) { + log(`Validation failed for CSV file: ${filePath}`); + reject(errors); + } else { + log(`Validation passed for CSV file: ${filePath}`); + resolve(); + } + }) + .on("error", (error) => { + reject(`Error reading CSV file: ${filePath}. Error: ${error.message}`); + }); + }); +}; + +const main = async () => { + try { + const modelConstraints = await getModelConstraints(); + + for (const [table, constraints] of Object.entries(modelConstraints)) { + const filePath = path.join(dataDir, `${table}.csv`); + if (!fs.existsSync(filePath)) { + console.error(`CSV file for table '${table}' does not exist.`); + process.exit(1); + } + + try { + await validateCSV( + filePath, + constraints.nonNullable, + constraints.foreignKeys, + ); + log(`Validation passed for table '${table}'`); + } catch (errors) { + console.error(`Validation failed for table '${table}':\n`, errors); + process.exit(1); + } + } + + log("All tables validated successfully."); + } catch (e) { + console.error(e); + process.exit(1); + } finally { + console.log("Done"); + await prisma.$disconnect(); + } +}; + +main() + .then(async () => { + await prisma.$disconnect(); + }) + .catch(async (e) => { + console.error(e); + await prisma.$disconnect(); + process.exit(1); + }); + +export {}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a576f9f3c..47833053d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -717,6 +717,9 @@ importers: chunk-text: specifier: ^2.0.1 version: 2.0.1 + csv-parser: + specifier: ^3.0.0 + version: 3.0.0 graphql-request: specifier: ^6.1.0 version: 6.1.0(graphql@16.9.0) From eaae66fd4024f217a18ae1b3d5176b1255303ed4 Mon Sep 17 00:00:00 2001 From: Joe Baker Date: Wed, 23 Oct 2024 14:57:53 +0100 Subject: [PATCH 013/127] refactor: demo account to oak components - AI-460 (#237) --- .../AppComponents/Chat/chat-lhs-header.tsx | 6 +- .../Chat/chat-right-hand-side-lesson.tsx | 4 +- .../components/AppComponents/Chat/header.tsx | 144 +++++++++++------- .../AppComponents/Chat/sidebar-mobile.tsx | 9 +- .../ContentOptions/DemoInterstitialDialog.tsx | 108 ++++++------- .../ContentOptions/DemoSharedComponents.tsx | 28 ++++ 6 files changed, 176 insertions(+), 123 deletions(-) create mode 100644 apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index 6ec4b22a8..f68545af8 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -42,15 +42,13 @@ const ChatLhsHeader = ({ New lesson
-
+
diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx index 2bbeb5ecd..bd0d63026 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx @@ -61,7 +61,7 @@ const ChatRightHandSideLesson = ({ const endOfDocRef = useRef(null); return (
{ closeMobileLessonPullOut(); }} - className="flex items-center justify-center gap-3" + className={`${demo.isDemoUser ? `mt-25` : ``} flex items-center justify-center gap-3 `} > diff --git a/apps/nextjs/src/components/AppComponents/Chat/header.tsx b/apps/nextjs/src/components/AppComponents/Chat/header.tsx index 0ac67fd99..08af379db 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/header.tsx @@ -2,13 +2,17 @@ import * as React from "react"; -import { OakIcon } from "@oaknational/oak-components"; +import { + OakBox, + OakFlex, + OakIcon, + OakLink, + OakSpan, +} from "@oaknational/oak-components"; import { useClerkDemoMetadata } from "hooks/useClerkDemoMetadata"; -import Link from "next/link"; import { usePathname } from "next/navigation"; import { useDemoUser } from "@/components/ContextProviders/Demo"; -import { Icon } from "@/components/Icon"; import OakIconLogo from "@/components/OakIconLogo"; import { BetaTagHeader } from "./beta-tag"; @@ -25,67 +29,105 @@ export function Header() { const ailaId = usePathname().split("aila/")[1]; return ( -
+ {clerkMetadata.isSet && demo.isDemoUser && ( -
-
- - Create {demo.appSessionsPerMonth} lessons per month - - - If you are a teacher in the UK,{" "} - - contact us for full access. - -
- - - -
+ + + Create {demo.appSessionsPerMonth} lessons per month • + {" "} + If you are a teacher in the UK,{" "} + + contact us for full access + + + + {demo.appSessionsRemaining !== undefined && ( - - {demo.appSessionsRemaining} of {demo.appSessionsPerMonth} lessons - remaining - + + + {demo.appSessionsRemaining} of {demo.appSessionsPerMonth}{" "} + lessons remaining + + )} -
+ )} -
-
- - + + + + - - Aila - -
+ + Aila + + -
-
+ + -
- - -
Help
- -
+ + + + + + + Help + + + + + -
-
+ + -
-
-
-
+ + + + ); } diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx index c8c86cd1f..5e3b9c1de 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-mobile.tsx @@ -1,5 +1,7 @@ "use client"; +import { OakFlex, OakSpan } from "@oaknational/oak-components"; + import { Sidebar } from "@/components/AppComponents/Chat/sidebar"; import { Button } from "@/components/AppComponents/Chat/ui/button"; import { @@ -28,9 +30,10 @@ export function SidebarMobile({ children }: Readonly) { }} > -
- Menu -
+ + Menu + + Toggle Sidebar diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx index f87187eab..a195de7bf 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoInterstitialDialog.tsx @@ -1,12 +1,21 @@ import { useCallback, useEffect, useState } from "react"; import { aiLogger } from "@oakai/logger"; -import { Flex } from "@radix-ui/themes"; +import { + OakFlex, + OakLink, + OakPrimaryButton, + OakSecondaryLink, +} from "@oaknational/oak-components"; import { captureMessage } from "@sentry/nextjs"; -import Button from "@/components/Button"; import { useDemoUser } from "@/components/ContextProviders/Demo"; -import LoadingWheel from "@/components/LoadingWheel"; + +import { + DialogContainer, + DialogContent, + DialogHeading, +} from "./DemoSharedComponents"; const log = aiLogger("demo"); @@ -31,27 +40,6 @@ function friendlyNumber( } } -function DialogContainer({ children }: { children: React.ReactNode }) { - return ( - - {children} - - ); -} - -function Heading({ children }: { children: React.ReactNode }) { - return

{children}

; -} - -function Content({ children }: { children: React.ReactNode }) { - return

{children}

; -} - const CreatingChatDialog = ({ submit, closeDialog, @@ -94,58 +82,52 @@ const CreatingChatDialog = ({ if (appSessionsRemaining === 0) { return ( - Lesson limit reached - + Lesson limit reached + You have created {demo.appSessionsPerMonth} of your{" "} {demo.appSessionsPerMonth} lessons available this month. If you are a teacher in the UK, please{" "} - - contact us for full access. - - - -
- - -
+ +
); } return ( - + Your {friendlyNumber(appSessionsRemaining, demo.appSessionsPerMonth)} demo lesson… - - - You can create {demo.appSessionsPerMonth} chats per month. If you are a - teacher in the UK and want to create more lessons,{" "} - - contact us for full access. - - - -
- - {!isSubmitting && ( - - )} - {isSubmitting && ( - - )} -
+ + + + Continue with lesson + +
); }; diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx new file mode 100644 index 000000000..2128f11eb --- /dev/null +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/DemoSharedComponents.tsx @@ -0,0 +1,28 @@ +import { OakFlex, OakHeading, OakP } from "@oaknational/oak-components"; + +export function DialogContainer({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export function DialogHeading({ children }: { children: React.ReactNode }) { + return ( + + {children} + + ); +} + +export function DialogContent({ children }: { children: React.ReactNode }) { + return {children}; +} From b8b1958c914b405e859cf39163fa531bef9e99d5 Mon Sep 17 00:00:00 2001 From: MG Date: Thu, 24 Oct 2024 09:20:10 +0100 Subject: [PATCH 014/127] fix: update prisma to latest version (#254) Co-authored-by: Adam Howard <91115+codeincontext@users.noreply.github.com> --- apps/nextjs/package.json | 2 +- package.json | 2 +- packages/db/package.json | 4 +-- pnpm-lock.yaml | 78 ++++++++++++++++++++-------------------- 4 files changed, 44 insertions(+), 42 deletions(-) diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 0bbbbcdc7..6188119e4 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -45,7 +45,7 @@ "@oaknational/oak-components": "^1.26.0", "@oaknational/oak-consent-client": "^2.1.0", "@portabletext/react": "^3.1.0", - "@prisma/client": "^5.13.0", + "@prisma/client": "^5.21.1", "@prisma/extension-accelerate": "^1.0.0", "@radix-ui/react-accordion": "^1.1.2", "@radix-ui/react-icons": "^1.3.0", diff --git a/package.json b/package.json index b7fc5a405..2744c385e 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "@manypkg/cli": "^0.21.1", "@next/env": "14.2.15", "@oakai/prettier-config": "*", - "@prisma/client": "^5.13.0", + "@prisma/client": "^5.21.1", "@semantic-release/changelog": "^6.0.3", "@semantic-release/exec": "^6.0.3", "@semantic-release/git": "^10.0.1", diff --git a/packages/db/package.json b/packages/db/package.json index 7a3962e3b..361c6248b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -58,7 +58,7 @@ "prettier": "@oakai/prettier-config", "dependencies": { "@oakai/logger": "*", - "@prisma/client": "^5.13.0", + "@prisma/client": "^5.21.1", "@types/chunk-text": "^1.0.0", "cheerio": "1.0.0-rc.12", "chunk-text": "^2.0.1", @@ -73,7 +73,7 @@ "devDependencies": { "@oakai/prettier-config": "*", "dotenv-cli": "^6.0.0", - "prisma": "^5.13.0", + "prisma": "^5.21.0", "typescript": "5.3.3", "zod-prisma": "^0.5.4" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47833053d..bbe17481f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -24,8 +24,8 @@ importers: specifier: '*' version: link:packages/prettier-config '@prisma/client': - specifier: ^5.13.0 - version: 5.13.0(prisma@5.16.1) + specifier: ^5.21.1 + version: 5.21.1(prisma@5.21.1) '@semantic-release/changelog': specifier: ^6.0.3 version: 6.0.3(semantic-release@21.1.2) @@ -138,11 +138,11 @@ importers: specifier: ^3.1.0 version: 3.1.0(react@18.2.0) '@prisma/client': - specifier: ^5.13.0 - version: 5.13.0(prisma@5.16.1) + specifier: ^5.21.1 + version: 5.21.1(prisma@5.21.1) '@prisma/extension-accelerate': specifier: ^1.0.0 - version: 1.0.0(@prisma/client@5.13.0) + version: 1.0.0(@prisma/client@5.21.1) '@radix-ui/react-accordion': specifier: ^1.1.2 version: 1.1.2(@types/react-dom@18.2.19)(@types/react@18.2.57)(react-dom@18.2.0)(react@18.2.0) @@ -706,8 +706,8 @@ importers: specifier: '*' version: link:../logger '@prisma/client': - specifier: ^5.13.0 - version: 5.13.0(prisma@5.16.1) + specifier: ^5.21.1 + version: 5.21.1(prisma@5.21.1) '@types/chunk-text': specifier: ^1.0.0 version: 1.0.0 @@ -746,14 +746,14 @@ importers: specifier: ^6.0.0 version: 6.0.0 prisma: - specifier: ^5.13.0 - version: 5.16.1 + specifier: ^5.21.0 + version: 5.21.1 typescript: specifier: 5.3.3 version: 5.3.3 zod-prisma: specifier: ^0.5.4 - version: 0.5.4(prisma@5.16.1)(zod@3.23.8) + version: 0.5.4(prisma@5.21.1)(zod@3.23.8) packages/eslint-config-custom: dependencies: @@ -5764,8 +5764,8 @@ packages: engines: {node: ^14.13.1 || >=16.0.0 || >=18.0.0} dev: false - /@prisma/client@5.13.0(prisma@5.16.1): - resolution: {integrity: sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==} + /@prisma/client@5.21.1(prisma@5.21.1): + resolution: {integrity: sha512-3n+GgbAZYjaS/k0M03yQsQfR1APbr411r74foknnsGpmhNKBG49VuUkxIU6jORgvJPChoD4WC4PqoHImN1FP0w==} engines: {node: '>=16.13'} requiresBuild: true peerDependencies: @@ -5774,7 +5774,7 @@ packages: prisma: optional: true dependencies: - prisma: 5.16.1 + prisma: 5.21.1 dev: false /@prisma/debug@3.8.1: @@ -5785,36 +5785,36 @@ packages: strip-ansi: 6.0.1 dev: true - /@prisma/debug@5.16.1: - resolution: {integrity: sha512-JsNgZAg6BD9RInLSrg7ZYzo11N7cVvYArq3fHGSD89HSgtN0VDdjV6bib7YddbcO6snzjchTiLfjeTqBjtArVQ==} + /@prisma/debug@5.21.1: + resolution: {integrity: sha512-uY8SAhcnORhvgtOrNdvWS98Aq/nkQ9QDUxrWAgW8XrCZaI3j2X7zb7Xe6GQSh6xSesKffFbFlkw0c2luHQviZA==} - /@prisma/engines-version@5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303: - resolution: {integrity: sha512-HkT2WbfmFZ9WUPyuJHhkiADxazHg8Y4gByrTSVeb3OikP6tjQ7txtSUGu9OBOBH0C13dPKN2qqH12xKtHu/Hiw==} + /@prisma/engines-version@5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36: + resolution: {integrity: sha512-qvnEflL0//lh44S/T9NcvTMxfyowNeUxTunPcDfKPjyJNrCNf2F1zQLcUv5UHAruECpX+zz21CzsC7V2xAeM7Q==} - /@prisma/engines@5.16.1: - resolution: {integrity: sha512-KkyF3eIUtBIyp5A/rJHCtwQO18OjpGgx18PzjyGcJDY/+vNgaVyuVd+TgwBgeq6NLdd1XMwRCI+58vinHsAdfA==} + /@prisma/engines@5.21.1: + resolution: {integrity: sha512-hGVTldUkIkTwoV8//hmnAAiAchi4oMEKD3aW5H2RrnI50tTdwza7VQbTTAyN3OIHWlK5DVg6xV7X8N/9dtOydA==} requiresBuild: true dependencies: - '@prisma/debug': 5.16.1 - '@prisma/engines-version': 5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303 - '@prisma/fetch-engine': 5.16.1 - '@prisma/get-platform': 5.16.1 + '@prisma/debug': 5.21.1 + '@prisma/engines-version': 5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36 + '@prisma/fetch-engine': 5.21.1 + '@prisma/get-platform': 5.21.1 - /@prisma/extension-accelerate@1.0.0(@prisma/client@5.13.0): + /@prisma/extension-accelerate@1.0.0(@prisma/client@5.21.1): resolution: {integrity: sha512-5oSpPtKCMfTl/sSXaS/7vBsPqfm+eEVB6/5KPBJITFatDoFcmjx/PIC/T93mHLiHI98xKwosPN59NGXjDDhcFA==} engines: {node: '>=16'} peerDependencies: '@prisma/client': '>=4.16.1' dependencies: - '@prisma/client': 5.13.0(prisma@5.16.1) + '@prisma/client': 5.21.1(prisma@5.21.1) dev: false - /@prisma/fetch-engine@5.16.1: - resolution: {integrity: sha512-oOkjaPU1lhcA/Rvr4GVfd1NLJBwExgNBE36Ueq7dr71kTMwy++a3U3oLd2ZwrV9dj9xoP6LjCcky799D9nEt4w==} + /@prisma/fetch-engine@5.21.1: + resolution: {integrity: sha512-70S31vgpCGcp9J+mh/wHtLCkVezLUqe/fGWk3J3JWZIN7prdYSlr1C0niaWUyNK2VflLXYi8kMjAmSxUVq6WGQ==} dependencies: - '@prisma/debug': 5.16.1 - '@prisma/engines-version': 5.16.0-24.34ace0eb2704183d2c05b60b52fba5c43c13f303 - '@prisma/get-platform': 5.16.1 + '@prisma/debug': 5.21.1 + '@prisma/engines-version': 5.21.1-1.bf0e5e8a04cada8225617067eaa03d041e2bba36 + '@prisma/get-platform': 5.21.1 /@prisma/generator-helper@3.8.1: resolution: {integrity: sha512-3zSy+XTEjmjLj6NO+/YPN1Cu7or3xA11TOoOnLRJ9G4pTT67RJXjK0L9Xy5n+3I0Xlb7xrWCgo8MvQQLMWzxPA==} @@ -5825,10 +5825,10 @@ packages: cross-spawn: 7.0.3 dev: true - /@prisma/get-platform@5.16.1: - resolution: {integrity: sha512-R4IKnWnMkR2nUAbU5gjrPehdQYUUd7RENFD2/D+xXTNhcqczp0N+WEGQ3ViyI3+6mtVcjjNIMdnUTNyu3GxIgA==} + /@prisma/get-platform@5.21.1: + resolution: {integrity: sha512-sRxjL3Igst3ct+e8ya/x//cDXmpLbZQ5vfps2N4tWl4VGKQAmym77C/IG/psSMsQKszc8uFC/q1dgmKFLUgXZQ==} dependencies: - '@prisma/debug': 5.16.1 + '@prisma/debug': 5.21.1 /@prisma/instrumentation@5.17.0: resolution: {integrity: sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ==} @@ -19995,13 +19995,15 @@ packages: minimist: 1.2.8 dev: false - /prisma@5.16.1: - resolution: {integrity: sha512-Z1Uqodk44diztImxALgJJfNl2Uisl9xDRvqybMKEBYJLNKNhDfAHf+ZIJbZyYiBhLMbKU9cYGdDVG5IIXEnL2Q==} + /prisma@5.21.1: + resolution: {integrity: sha512-PB+Iqzld/uQBPaaw2UVIk84kb0ITsLajzsxzsadxxl54eaU5Gyl2/L02ysivHxK89t7YrfQJm+Ggk37uvM70oQ==} engines: {node: '>=16.13'} hasBin: true requiresBuild: true dependencies: - '@prisma/engines': 5.16.1 + '@prisma/engines': 5.21.1 + optionalDependencies: + fsevents: 2.3.3 /prismjs@1.27.0: resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} @@ -23874,7 +23876,7 @@ packages: readable-stream: 4.4.2 dev: false - /zod-prisma@0.5.4(prisma@5.16.1)(zod@3.23.8): + /zod-prisma@0.5.4(prisma@5.21.1)(zod@3.23.8): resolution: {integrity: sha512-5Ca4Qd1a1jy1T/NqCEpbr0c+EsbjJfJ/7euEHob3zDvtUK2rTuD1Rc/vfzH8q8PtaR2TZbysD88NHmrLwpv3Xg==} engines: {node: '>=14'} hasBin: true @@ -23888,7 +23890,7 @@ packages: dependencies: '@prisma/generator-helper': 3.8.1 parenthesis: 3.1.8 - prisma: 5.16.1 + prisma: 5.21.1 ts-morph: 13.0.3 zod: 3.23.8 dev: true From be824a8d73f8d0e2bc690789314ed7b0d135b090 Mon Sep 17 00:00:00 2001 From: MG Date: Thu, 24 Oct 2024 10:36:12 +0100 Subject: [PATCH 015/127] feat: ingest scripts (#200) --- .../src/import-lessons/graphql/query.ts | 6 +- packages/ingest/src/index.ts | 56 +++++++ packages/ingest/src/openai-batches/openai.ts | 4 + packages/ingest/src/steps/0-start.ts | 58 ++++++++ packages/ingest/src/steps/1-captions.ts | 140 ++++++++++++++++++ packages/ingest/src/steps/2-lp-batch-start.ts | 65 ++++++++ packages/ingest/src/steps/3-lp-batch-sync.ts | 71 +++++++++ packages/ingest/src/steps/4-lp-chunking.ts | 92 ++++++++++++ .../src/steps/5-lp-parts-embed-start.ts | 89 +++++++++++ .../ingest/src/steps/6-lp-parts-embed-sync.ts | 78 ++++++++++ 10 files changed, 654 insertions(+), 5 deletions(-) create mode 100644 packages/ingest/src/index.ts create mode 100644 packages/ingest/src/steps/0-start.ts create mode 100644 packages/ingest/src/steps/1-captions.ts create mode 100644 packages/ingest/src/steps/2-lp-batch-start.ts create mode 100644 packages/ingest/src/steps/3-lp-batch-sync.ts create mode 100644 packages/ingest/src/steps/4-lp-chunking.ts create mode 100644 packages/ingest/src/steps/5-lp-parts-embed-start.ts create mode 100644 packages/ingest/src/steps/6-lp-parts-embed-sync.ts diff --git a/packages/ingest/src/import-lessons/graphql/query.ts b/packages/ingest/src/import-lessons/graphql/query.ts index 6d6a572ed..3d7870c04 100644 --- a/packages/ingest/src/import-lessons/graphql/query.ts +++ b/packages/ingest/src/import-lessons/graphql/query.ts @@ -6,11 +6,7 @@ export const query = gql` limit: $limit offset: $offset distinct_on: lessonSlug - where: { - videoTitle: { _is_null: false } - isLegacy: { _eq: false } - subjectSlug: { _neq: "english" } - } + where: { videoTitle: { _is_null: false }, isLegacy: { _eq: false } } ) { oakLessonId: lessonId lessonSlug diff --git a/packages/ingest/src/index.ts b/packages/ingest/src/index.ts new file mode 100644 index 000000000..5d02a952f --- /dev/null +++ b/packages/ingest/src/index.ts @@ -0,0 +1,56 @@ +/** + * To be run from CLI with a single argument takes one of: + * "start", "captions", "lp-start", "lp-sync", "chunk", "embed-start", "embed-sync" + */ +import { prisma } from "@oakai/db"; + +import { getLatestIngestId } from "./db-helpers/getLatestIngestId"; +import { ingestStart } from "./steps/0-start"; +import { captions } from "./steps/1-captions"; +import { lpBatchStart } from "./steps/2-lp-batch-start"; +import { lpBatchSync } from "./steps/3-lp-batch-sync"; +import { lpChunking } from "./steps/4-lp-chunking"; +import { lpPartsEmbedStart } from "./steps/5-lp-parts-embed-start"; +import { lpPartsEmbedSync } from "./steps/6-lp-parts-embed-sync"; + +const command = process.argv[2]; + +if (!command) { + console.error("No command provided"); + process.exit(1); +} + +async function main() { + const ingestId = process.argv[3] || (await getLatestIngestId({ prisma })); + switch (command) { + case "start": + ingestStart({ prisma }); + break; + case "captions": + captions({ prisma, ingestId }); + break; + case "lp-start": + lpBatchStart({ prisma, ingestId }); + break; + case "lp-sync": + lpBatchSync({ prisma, ingestId }); + break; + case "chunk": + lpChunking({ prisma, ingestId }); + break; + case "embed-start": + lpPartsEmbedStart({ prisma, ingestId }); + break; + case "embed-sync": + lpPartsEmbedSync({ prisma, ingestId }); + break; + default: + console.error("Unknown command"); + process.exit(1); + } +} + +main().catch((error) => { + console.error("Error running command", error); + process.exit(1); +}); diff --git a/packages/ingest/src/openai-batches/openai.ts b/packages/ingest/src/openai-batches/openai.ts index a06029401..eed82d0ec 100644 --- a/packages/ingest/src/openai-batches/openai.ts +++ b/packages/ingest/src/openai-batches/openai.ts @@ -1,5 +1,9 @@ import OpenAI from "openai"; +if (!process.env.INGEST_OPENAI_API_KEY) { + throw new Error("Missing INGEST_OPENAI_API_KEY environment variable"); +} + export const openai = new OpenAI({ apiKey: process.env.INGEST_OPENAI_API_KEY, }); diff --git a/packages/ingest/src/steps/0-start.ts b/packages/ingest/src/steps/0-start.ts new file mode 100644 index 000000000..6bab86f75 --- /dev/null +++ b/packages/ingest/src/steps/0-start.ts @@ -0,0 +1,58 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { IngestError } from "../IngestError"; +import { IngestConfig } from "../config/ingestConfig"; +import { createIngestRecord } from "../db-helpers/createIngestRecord"; +import { importLessonsFromCSV } from "../import-lessons/importLessonsFromCSV"; +import { importLessonsFromOakDB } from "../import-lessons/importLessonsFromOakDB"; + +const config: IngestConfig = { + completionModel: "gpt-4o-2024-08-06", + completionTemperature: 0.7, + embeddingDimensions: 256, + embeddingModel: "text-embedding-3-large", + sourcePartsToInclude: "all", + source: { + type: "oak-db", + }, +}; + +/** + * This function starts an ingest process. + * It creates a new ingest record in the database, and imports lessons from the Oak API. + */ +export async function ingestStart({ + prisma, +}: { + prisma: PrismaClientWithAccelerate; +}) { + const { id: ingestId } = await createIngestRecord({ + prisma, + config, + }); + + switch (config.source.type) { + case "oak-db": + await importLessonsFromOakDB({ + ingestId, + onError: console.error, + }); + break; + case "csv": + if (config.sourcePartsToInclude !== "title-subject-key-stage") { + throw new IngestError( + `sourcePartsToInclude must be "title-subject-key-stage" when importing from a CSV file`, + ); + } + await importLessonsFromCSV({ + ingestId, + filePath: config.source.filePath, + onError: console.error, + }); + break; + default: + throw new IngestError(`Unsupported source type: ${config.source}`); + } + + console.log(`Ingest started with id: ${ingestId}`); +} diff --git a/packages/ingest/src/steps/1-captions.ts b/packages/ingest/src/steps/1-captions.ts new file mode 100644 index 000000000..c0614a066 --- /dev/null +++ b/packages/ingest/src/steps/1-captions.ts @@ -0,0 +1,140 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { IngestError } from "../IngestError"; +import { getCaptionsByFileName } from "../captions/getCaptionsByFileName"; +import { getCaptionsFileNameForLesson } from "../captions/getCaptionsFileNameForLesson"; +import { createCaptionsRecord } from "../db-helpers/createCaptionsRecord"; +import { createErrorRecord } from "../db-helpers/createErrorRecord"; +import { getIngestById } from "../db-helpers/getIngestById"; +import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; +import { Step, getPrevStep } from "../db-helpers/step"; +import { updateLessonsState } from "../db-helpers/updateLessonsState"; +import { Captions } from "../zod-schema/zodSchema"; + +const currentStep: Step = "captions_fetch"; +const prevStep = getPrevStep(currentStep); + +/** + * This function fetches and stores captions for the lessons imported in the most recent ingest. + */ +export async function captions({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const ingest = await getIngestById({ prisma, ingestId }); + + const lessons = await loadLessonsAndUpdateState({ + prisma, + ingestId, + prevStep, + currentStep, + }); + + if (ingest.config.sourcePartsToInclude === "title-subject-key-stage") { + console.log("Skipping captions fetch for title-subject-key-stage ingest"); + await updateLessonsState({ + prisma, + ingestId, + lessonIds: lessons.map((lesson) => lesson.id), + step: currentStep, + stepStatus: "completed", + }); + return; + } + + console.log(`Fetching captions for ${lessons.length} lessons`); + + const failedLessonIds: string[] = []; + const completedLessonIds: string[] = []; + + /** + * Fetch captions for each lesson, and store them in the database + */ + for (const lesson of lessons) { + try { + const fileName = getCaptionsFileNameForLesson({ + videoTitle: lesson.data.videoTitle, + }); + const { caption: captions } = await getCaptionsByFileName(fileName); + + await persistOnSuccess({ + prisma, + ingestId, + lesson, + captions, + }); + + completedLessonIds.push(lesson.id); + } catch (cause) { + const error = new IngestError("Failed to fetch captions", { + cause, + }); + await persistOnError({ + prisma, + ingestId, + lessonId: lesson.id, + error, + }); + failedLessonIds.push(lesson.id); + } + } + + console.log(`Failed: ${failedLessonIds.length} lessons`); + console.log(`Completed: ${completedLessonIds.length} lessons`); +} + +async function persistOnSuccess({ + prisma, + ingestId, + lesson, + captions, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; + lesson: { id: string; ingestId: string }; + captions: Captions; +}) { + await createCaptionsRecord({ + prisma, + ingestId: lesson.ingestId, + lessonId: lesson.id, + captions, + }); + await updateLessonsState({ + prisma, + ingestId, + lessonIds: [lesson.id], + step: currentStep, + stepStatus: "completed", + }); +} + +async function persistOnError({ + prisma, + ingestId, + lessonId, + error, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; + lessonId: string; + error: IngestError; +}) { + await createErrorRecord({ + prisma, + ingestId, + lessonId, + step: currentStep, + errorMessage: error.message, + }); + await updateLessonsState({ + prisma, + ingestId, + lessonIds: [lessonId], + step: currentStep, + stepStatus: "failed", + }); +} diff --git a/packages/ingest/src/steps/2-lp-batch-start.ts b/packages/ingest/src/steps/2-lp-batch-start.ts new file mode 100644 index 000000000..413050872 --- /dev/null +++ b/packages/ingest/src/steps/2-lp-batch-start.ts @@ -0,0 +1,65 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { getIngestById } from "../db-helpers/getIngestById"; +import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; +import { Step, getPrevStep } from "../db-helpers/step"; +import { updateLessonsState } from "../db-helpers/updateLessonsState"; +import { startGenerating } from "../generate-lesson-plans/startGenerating"; + +const currentStep: Step = "lesson_plan_generation"; +const prevStep = getPrevStep(currentStep); + +/** + * Get all lessons which are ready for lesson plan generation, and write + * request batch, upload it, and submit it + */ +export async function lpBatchStart({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const ingest = await getIngestById({ prisma, ingestId }); + const lessons = await loadLessonsAndUpdateState({ + prisma, + ingestId, + prevStep, + currentStep, + }); + + if (lessons.length === 0) { + console.log("No lessons to generate lesson plans for"); + return; + } + + console.log(`Generating lesson plans for ${lessons.length} lessons`); + + try { + await startGenerating({ + ingestId, + ingest, + lessons, + onSubmitted: async ({ openaiBatchId, filePath }) => { + await prisma.ingestOpenAiBatch.create({ + data: { + ingestId, + batchType: "lesson_plan_generation", + openaiBatchId: openaiBatchId, + inputFilePath: filePath, + status: "pending", + }, + }); + }, + }); + } catch (error) { + console.error("Error generating lesson plans", error); + await updateLessonsState({ + prisma, + ingestId, + lessonIds: lessons.map((l) => l.id), + step: currentStep, + stepStatus: "failed", + }); + } +} diff --git a/packages/ingest/src/steps/3-lp-batch-sync.ts b/packages/ingest/src/steps/3-lp-batch-sync.ts new file mode 100644 index 000000000..273242cfb --- /dev/null +++ b/packages/ingest/src/steps/3-lp-batch-sync.ts @@ -0,0 +1,71 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { handleLessonPlanBatchSuccess } from "../generate-lesson-plans/handleLessonPlanBatchSuccess"; +import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; +import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; + +/** + * Check status of lesson plan generation batches and action + */ +export async function lpBatchSync({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const batches = await prisma.ingestOpenAiBatch.findMany({ + where: { + ingestId, + batchType: "lesson_plan_generation", + status: "pending", + }, + }); + console.log(`Found ${batches.length} pending lesson plan generation batches`); + for (const batch of batches) { + const { batch: openaiBatch } = await retrieveOpenAiBatch({ + batchId: batch.openaiBatchId, + }); + switch (openaiBatch.status) { + case "validating": + case "in_progress": + case "finalizing": + console.log(`Batch ${batch.id} is ${openaiBatch.status}`); + break; + + case "completed": + if (openaiBatch.error_file_id) { + console.log(`Batch ${batch.id} has error file, handling...`); + await handleOpenAiBatchErrorFile({ + ingestId, + prisma, + batchId: batch.id, + errorFileId: openaiBatch.error_file_id, + task: "generate-lesson-plans", + }); + } + + if (openaiBatch.output_file_id) { + console.log(`Batch ${batch.id} succeeded, handling...`); + await handleLessonPlanBatchSuccess({ + prisma, + ingestId, + batchId: batch.id, + outputFileId: openaiBatch.output_file_id, + }); + } + + break; + + default: + await prisma.ingestOpenAiBatch.update({ + where: { + id: batch.id, + }, + data: { + status: "failed", + }, + }); + } + } +} diff --git a/packages/ingest/src/steps/4-lp-chunking.ts b/packages/ingest/src/steps/4-lp-chunking.ts new file mode 100644 index 000000000..3f15127d8 --- /dev/null +++ b/packages/ingest/src/steps/4-lp-chunking.ts @@ -0,0 +1,92 @@ +import { + CompletedLessonPlan, + CompletedLessonPlanSchema, +} from "@oakai/aila/src/protocol/schema"; +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { getLessonPlanParts } from "../chunking/getLessonPlanParts"; +import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; +import { Step, getPrevStep } from "../db-helpers/step"; +import { updateLessonsState } from "../db-helpers/updateLessonsState"; + +const currentStep: Step = "chunking"; +const prevStep = getPrevStep(currentStep); + +/** + * Create lesson plan 'parts' from lesson plans + */ +export async function lpChunking({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const lessons = await loadLessonsAndUpdateState({ + prisma, + ingestId, + prevStep, + currentStep, + }); + + if (lessons.length === 0) { + console.log("No lessons to chunk, exiting early"); + return; + } + + console.log(`Chunking lesson plans for ${lessons.length} lesson`); + + const lessonIdsFailed: string[] = []; + const lessonIdsCompleted: string[] = []; + + for (const lesson of lessons) { + try { + const lessonPlanRecord = lesson.lessonPlan; + if (!lessonPlanRecord) { + lessonIdsFailed.push(lesson.id); + continue; + } + let lessonPlan: CompletedLessonPlan; + try { + lessonPlan = CompletedLessonPlanSchema.parse(lessonPlanRecord.data); + } catch (error) { + lessonIdsFailed.push(lesson.id); + continue; + } + const parts = getLessonPlanParts({ lessonPlan }); + + await prisma.ingestLessonPlanPart.createMany({ + data: parts.map((part) => ({ + ingestId, + lessonId: lesson.id, + lessonPlanId: lessonPlanRecord.id, + key: part.key, + valueText: part.content, + valueJson: part.json, + })), + }); + lessonIdsCompleted.push(lesson.id); + } catch (error) { + lessonIdsFailed.push(lesson.id); + } + } + + await updateLessonsState({ + prisma, + ingestId, + lessonIds: lessonIdsFailed, + step: currentStep, + stepStatus: "failed", + }); + + await updateLessonsState({ + prisma, + ingestId, + lessonIds: lessonIdsCompleted, + step: currentStep, + stepStatus: "completed", + }); + + console.log(`Chunking ${lessons.length} lesson plans completed`); + console.log(`Failed: ${lessonIdsFailed.length}`); +} diff --git a/packages/ingest/src/steps/5-lp-parts-embed-start.ts b/packages/ingest/src/steps/5-lp-parts-embed-start.ts new file mode 100644 index 000000000..f08e6a92b --- /dev/null +++ b/packages/ingest/src/steps/5-lp-parts-embed-start.ts @@ -0,0 +1,89 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { getIngestById } from "../db-helpers/getIngestById"; +import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; +import { Step, getPrevStep } from "../db-helpers/step"; +import { startEmbedding } from "../embedding/startEmbedding"; +import { parseCustomId } from "../openai-batches/customId"; +import { chunkAndPromiseAll } from "../utils/chunkAndPromiseAll"; + +const currentStep: Step = "embedding"; +const prevStep = getPrevStep(currentStep); + +/** + * Start the process of embedding lesson plan parts + */ +export async function lpPartsEmbedStart({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const ingest = await getIngestById({ prisma, ingestId }); + const lessons = await loadLessonsAndUpdateState({ + prisma, + ingestId, + prevStep, + currentStep, + }); + + const allParts = lessons + .map((lesson) => + lesson.lessonPlanParts.map((part) => ({ + lessonId: lesson.id, + partKey: part.key, + lessonPlanPartId: part.id, + textToEmbed: part.valueText, + ingest, + })), + ) + .flat(); + + if (allParts.length === 0) { + console.log("No lesson plan parts to embed, exiting early"); + return; + } + + await startEmbedding({ + ingestId, + parts: allParts, + onSubmitted: async ({ openaiBatchId, filePath, customIds }) => { + const batch = await prisma.ingestOpenAiBatch.create({ + data: { + ingestId, + batchType: "embedding", + openaiBatchId, + inputFilePath: filePath, + status: "pending", + }, + }); + + const lessonPlanPartIds = customIds + .map((customId) => + parseCustomId({ + task: "embed-lesson-plan-parts", + customId, + }), + ) + .map(({ lessonPlanPartId }) => lessonPlanPartId); + + await chunkAndPromiseAll({ + data: lessonPlanPartIds, + fn: async (lessonPlanPartIds) => { + await prisma.ingestLessonPlanPart.updateMany({ + where: { + id: { + in: lessonPlanPartIds, + }, + }, + data: { + batchId: batch.id, + }, + }); + }, + chunkSize: 25000, + }); + }, + }); +} diff --git a/packages/ingest/src/steps/6-lp-parts-embed-sync.ts b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts new file mode 100644 index 000000000..c9dd65243 --- /dev/null +++ b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts @@ -0,0 +1,78 @@ +import { PrismaClientWithAccelerate } from "@oakai/db"; + +import { handleEmbeddingBatchSuccess } from "../embedding/handleEmbeddingBatchSuccess"; +import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; +import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; + +/** + * Check status of lesson plan generation batches and action + */ +export async function lpPartsEmbedSync({ + prisma, + ingestId, +}: { + prisma: PrismaClientWithAccelerate; + ingestId: string; +}) { + const embeddingsBatches = await prisma.ingestOpenAiBatch.findMany({ + where: { + ingestId, + batchType: "embedding", + status: "pending", + }, + }); + + if (embeddingsBatches.length === 0) { + console.log("No embedding batches to check, exiting early"); + return; + } + + console.log(`Checking ${embeddingsBatches.length} embedding batches`); + + for (const batch of embeddingsBatches) { + const { batch: openaiBatch } = await retrieveOpenAiBatch({ + batchId: batch.openaiBatchId, + }); + switch (openaiBatch.status) { + case "validating": + case "in_progress": + case "finalizing": + console.log(`Batch ${batch.id} is ${openaiBatch.status}`); + break; + + case "completed": + if (openaiBatch.error_file_id) { + console.log(`Batch ${batch.id} has error file, handling...`); + await handleOpenAiBatchErrorFile({ + prisma, + ingestId, + batchId: batch.id, + errorFileId: openaiBatch.error_file_id, + task: "embed-lesson-plan-parts", + }); + } + + if (openaiBatch.output_file_id) { + console.log(`Batch ${batch.id} succeeded, handling...`); + await handleEmbeddingBatchSuccess({ + prisma, + ingestId, + batchId: batch.id, + outputFileId: openaiBatch.output_file_id, + }); + } + + break; + + default: + await prisma.ingestOpenAiBatch.update({ + where: { + id: batch.id, + }, + data: { + status: "failed", + }, + }); + } + } +} From 1a4c51fa4b2b53e758f860091c85f840c191dc9f Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 11:58:15 +0100 Subject: [PATCH 016/127] fix: reinstate prebuilding the chat route in local dev (#260) --- apps/nextjs/src/app/api/chat/route.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/nextjs/src/app/api/chat/route.ts b/apps/nextjs/src/app/api/chat/route.ts index b5e2b9e72..365edd577 100644 --- a/apps/nextjs/src/app/api/chat/route.ts +++ b/apps/nextjs/src/app/api/chat/route.ts @@ -10,4 +10,10 @@ async function postHandler(req: NextRequest): Promise { return handleChatPostRequest(req, config); } +async function getHandler(): Promise { + return new Response("Server is ready", { status: 200 }); +} + export const POST = withSentry(postHandler); + +export const GET = withSentry(getHandler); From 0db4cde7c705cac95b1ce1e75125dd21d4078850 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 12:19:48 +0100 Subject: [PATCH 017/127] chore: hide generated fixtures and recordings in PRs (#262) --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..e3a79f142 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +apps/nextjs/tests-e2e/recordings/*.chunks.txt linguist-generated=true +apps/nextjs/tests-e2e/recordings/*.json linguist-generated=true +**/*/__snapshots__/*.snap linguist-generated=true \ No newline at end of file From 4d4b9599996482817411237a13174ed55ef6c476 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 12:49:06 +0100 Subject: [PATCH 018/127] fix: ensure that the patch enqueuer exists when enqueuing (#264) --- packages/aila/src/core/chat/AilaChat.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index c88bc798d..5395275ee 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -214,14 +214,20 @@ export class AilaChat implements AilaChatService { } public async enqueue(message: JsonPatchDocumentOptional) { - await this._patchEnqueuer.enqueueMessage(message); + // Optional "?"" necessary to avoid a "terminated" error + if (this?._patchEnqueuer) { + await this._patchEnqueuer.enqueueMessage(message); + } } public async enqueuePatch( path: string, value: string | string[] | number | object, ) { - await this._patchEnqueuer.enqueuePatch(path, value); + // Optional "?"" necessary to avoid a "terminated" error + if (this?._patchEnqueuer) { + await this._patchEnqueuer.enqueuePatch(path, value); + } } private async startNewGeneration() { From afb453581c3dbe2b01a4e0712df97a7779d59e6c Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 12:49:27 +0100 Subject: [PATCH 019/127] fix: reduce turbo errors on local dev, enable running e2e in built mode (#259) --- .vscode/settings.json | 2 ++ apps/nextjs/next.config.js | 60 ++++++++++++++++++++------------------ apps/nextjs/package.json | 9 ++++-- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f21612b29..6a0ffac45 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,6 +25,7 @@ "categorisation", "Categorised", "cloudinary", + "codegen", "compat", "contrib", "cuid", @@ -101,6 +102,7 @@ "pkey", "pollyjs", "popover", + "portabletext", "posthog", "postpack", "posttest", diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 6d89b5d07..5c25fafba 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -5,6 +5,7 @@ const { RELEASE_STAGE_TESTING, } = require("./scripts/build_config_helpers.js"); const path = require("path"); +const Sentry = require("@sentry/nextjs"); const { PHASE_PRODUCTION_BUILD, PHASE_TEST } = require("next/constants"); @@ -41,9 +42,6 @@ const getConfig = async (phase) => { isProductionBuild = releaseStage === RELEASE_STAGE_PRODUCTION; appVersion = getAppVersion({ isProductionBuild }); - console.log( - `Release stage: "${releaseStage}". App version: "${appVersion}"`, - ); } /** @type {import('next').NextConfig} */ @@ -161,37 +159,41 @@ const getConfig = async (phase) => { module.exports = getConfig; -module.exports = withSentryConfig(module.exports, { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options +if (!process.env.TURBOPACK) { + module.exports = withSentryConfig(module.exports, { + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options + + org: process.env.SENTRY_ORG, + project: process.env.SENTRY_PROJECT, - org: process.env.SENTRY_ORG, - project: process.env.SENTRY_PROJECT, + // Only print logs for uploading source maps in CI + silent: !process.env.CI, - // Only print logs for uploading source maps in CI - silent: !process.env.CI, + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ - // For all available options, see: - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, - // Upload a larger set of source maps for prettier stack traces (increases build time) - widenClientFileUpload: true, + // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // This can increase your server load as well as your hosting bill. + // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- + // side errors will fail. + tunnelRoute: "/monitoring", - // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. - // This can increase your server load as well as your hosting bill. - // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- - // side errors will fail. - tunnelRoute: "/monitoring", + // Hides source maps from generated client bundles + hideSourceMaps: true, - // Hides source maps from generated client bundles - hideSourceMaps: true, + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, - // Automatically tree-shake Sentry logger statements to reduce bundle size - disableLogger: true, + // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true, + }); +} - // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) - // See the following for more information: - // https://docs.sentry.io/product/crons/ - // https://vercel.com/docs/cron-jobs - automaticVercelMonitors: true, -}); +module.exports.onRequestError = Sentry.captureRequestError; diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 6188119e4..8bff1d870 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -7,9 +7,10 @@ "build:dev": "pnpm with-env next build", "check": "tsc --noEmit", "clean": "rm -rf .next .turbo node_modules", - "dev": "concurrently \"pnpm dev-server\" \"node scripts/local-dev.mjs\"", - "dev-turbo": "pnpm with-env node scripts/increase-listeners.js next dev --port 2525 --turbo | pino-pretty -C", - "dev-server": "pnpm with-env node scripts/increase-listeners.js next dev --port 2525 | pino-pretty -C", + "dev": "FORCE_COLOR=1 concurrently \"pnpm dev-server\" \"node scripts/local-dev.mjs\"", + "dev-turbo": "FORCE_COLOR=1 concurrently \"pnpm dev-server-turbo\" \"node scripts/local-dev.mjs\"", + "dev-server-turbo": "FORCE_COLOR=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 --turbo | pino-pretty -C", + "dev-server": "FORCE_COLOR=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 | pino-pretty -C", "dev-trace-deprecation": "NODE_OPTIONS=\"--trace-deprecation\" next dev --port 2525 | pino-pretty -C", "lint": "next lint", "lint-fix": "next lint --fix", @@ -18,6 +19,8 @@ "test": "pnpm with-env jest --colors --config jest.config.js", "test-e2e": "pnpm with-env playwright test", "test-e2e-ui": "pnpm with-env playwright test --ui", + "test-e2e-ui-serve": "pnpm build && pnpm start --port 4848 --turbo", + "test-e2e-ui-built": "PORT=4848 pnpm with-env playwright test --ui", "with-env": "dotenv -e ../../.env --", "aila": "tsx scripts/aila-cli.ts", "storybook": "dotenv -e ../../.env -- storybook dev -p 6006 --no-open", From 64758f1735f497cc9ebad2b74b905008d126ae22 Mon Sep 17 00:00:00 2001 From: MG Date: Thu, 24 Oct 2024 12:50:13 +0100 Subject: [PATCH 020/127] chore: use ai logger in ingest service (#261) --- .../import-lessons/importLessonsFromOakDB.ts | 5 +++- packages/ingest/src/index.ts | 25 +++++++++++-------- packages/ingest/src/steps/0-start.ts | 10 +++++--- packages/ingest/src/steps/1-captions.ts | 11 +++++--- packages/ingest/src/steps/2-lp-batch-start.ts | 10 +++++--- packages/ingest/src/steps/3-lp-batch-sync.ts | 11 +++++--- packages/ingest/src/steps/4-lp-chunking.ts | 11 +++++--- .../src/steps/5-lp-parts-embed-start.ts | 5 +++- .../ingest/src/steps/6-lp-parts-embed-sync.ts | 13 ++++++---- packages/ingest/src/types.ts | 6 +++++ .../src/utils/splitJsonlByRowsOrSize.ts | 2 +- 11 files changed, 72 insertions(+), 37 deletions(-) create mode 100644 packages/ingest/src/types.ts diff --git a/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts b/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts index 5d3283f39..1ba873b0c 100644 --- a/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts +++ b/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts @@ -1,6 +1,7 @@ import { prisma } from "@oakai/db"; import { IngestError } from "../IngestError"; +import { IngestLogger } from "../types"; import { getDataHash } from "../utils/getDataHash"; import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; import { graphqlClient } from "./graphql/client"; @@ -8,6 +9,7 @@ import { query } from "./graphql/query"; type ImportLessonsFromOakDBProps = { ingestId: string; + log: IngestLogger; onError: (error: IngestError) => void; }; /** @@ -15,6 +17,7 @@ type ImportLessonsFromOakDBProps = { */ export async function importLessonsFromOakDB({ ingestId, + log, onError, }: ImportLessonsFromOakDBProps) { let pageNumber = 0; // Start at page 0 @@ -60,7 +63,7 @@ export async function importLessonsFromOakDB({ }); imported += data.length; - console.log(`Imported ${imported} lessons`); + log.info(`Imported ${imported} lessons`); if (lessonData.lessons.length < perPage) { break; diff --git a/packages/ingest/src/index.ts b/packages/ingest/src/index.ts index 5d02a952f..c2f461faf 100644 --- a/packages/ingest/src/index.ts +++ b/packages/ingest/src/index.ts @@ -3,6 +3,7 @@ * "start", "captions", "lp-start", "lp-sync", "chunk", "embed-start", "embed-sync" */ import { prisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { getLatestIngestId } from "./db-helpers/getLatestIngestId"; import { ingestStart } from "./steps/0-start"; @@ -15,42 +16,44 @@ import { lpPartsEmbedSync } from "./steps/6-lp-parts-embed-sync"; const command = process.argv[2]; +const log = aiLogger("ingest"); + if (!command) { - console.error("No command provided"); + log.error("No command provided"); process.exit(1); } async function main() { - const ingestId = process.argv[3] || (await getLatestIngestId({ prisma })); + const ingestId = process.argv[3] ?? (await getLatestIngestId({ prisma })); switch (command) { case "start": - ingestStart({ prisma }); + ingestStart({ prisma, log }); break; case "captions": - captions({ prisma, ingestId }); + captions({ prisma, log, ingestId }); break; case "lp-start": - lpBatchStart({ prisma, ingestId }); + lpBatchStart({ prisma, log, ingestId }); break; case "lp-sync": - lpBatchSync({ prisma, ingestId }); + lpBatchSync({ prisma, log, ingestId }); break; case "chunk": - lpChunking({ prisma, ingestId }); + lpChunking({ prisma, log, ingestId }); break; case "embed-start": - lpPartsEmbedStart({ prisma, ingestId }); + lpPartsEmbedStart({ prisma, log, ingestId }); break; case "embed-sync": - lpPartsEmbedSync({ prisma, ingestId }); + lpPartsEmbedSync({ prisma, log, ingestId }); break; default: - console.error("Unknown command"); + log.error("Unknown command"); process.exit(1); } } main().catch((error) => { - console.error("Error running command", error); + log.error("Error running command", error); process.exit(1); }); diff --git a/packages/ingest/src/steps/0-start.ts b/packages/ingest/src/steps/0-start.ts index 6bab86f75..aa7eccdb0 100644 --- a/packages/ingest/src/steps/0-start.ts +++ b/packages/ingest/src/steps/0-start.ts @@ -5,6 +5,7 @@ import { IngestConfig } from "../config/ingestConfig"; import { createIngestRecord } from "../db-helpers/createIngestRecord"; import { importLessonsFromCSV } from "../import-lessons/importLessonsFromCSV"; import { importLessonsFromOakDB } from "../import-lessons/importLessonsFromOakDB"; +import { IngestLogger } from "../types"; const config: IngestConfig = { completionModel: "gpt-4o-2024-08-06", @@ -23,8 +24,10 @@ const config: IngestConfig = { */ export async function ingestStart({ prisma, + log, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; }) { const { id: ingestId } = await createIngestRecord({ prisma, @@ -35,7 +38,8 @@ export async function ingestStart({ case "oak-db": await importLessonsFromOakDB({ ingestId, - onError: console.error, + log, + onError: log.error, }); break; case "csv": @@ -47,12 +51,12 @@ export async function ingestStart({ await importLessonsFromCSV({ ingestId, filePath: config.source.filePath, - onError: console.error, + onError: log.error, }); break; default: throw new IngestError(`Unsupported source type: ${config.source}`); } - console.log(`Ingest started with id: ${ingestId}`); + log.info(`Ingest started with id: ${ingestId}`); } diff --git a/packages/ingest/src/steps/1-captions.ts b/packages/ingest/src/steps/1-captions.ts index c0614a066..2c022e876 100644 --- a/packages/ingest/src/steps/1-captions.ts +++ b/packages/ingest/src/steps/1-captions.ts @@ -9,6 +9,7 @@ import { getIngestById } from "../db-helpers/getIngestById"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; import { Step, getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; +import { IngestLogger } from "../types"; import { Captions } from "../zod-schema/zodSchema"; const currentStep: Step = "captions_fetch"; @@ -19,9 +20,11 @@ const prevStep = getPrevStep(currentStep); */ export async function captions({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const ingest = await getIngestById({ prisma, ingestId }); @@ -34,7 +37,7 @@ export async function captions({ }); if (ingest.config.sourcePartsToInclude === "title-subject-key-stage") { - console.log("Skipping captions fetch for title-subject-key-stage ingest"); + log.info("Skipping captions fetch for title-subject-key-stage ingest"); await updateLessonsState({ prisma, ingestId, @@ -45,7 +48,7 @@ export async function captions({ return; } - console.log(`Fetching captions for ${lessons.length} lessons`); + log.info(`Fetching captions for ${lessons.length} lessons`); const failedLessonIds: string[] = []; const completedLessonIds: string[] = []; @@ -82,8 +85,8 @@ export async function captions({ } } - console.log(`Failed: ${failedLessonIds.length} lessons`); - console.log(`Completed: ${completedLessonIds.length} lessons`); + log.info(`Failed: ${failedLessonIds.length} lessons`); + log.info(`Completed: ${completedLessonIds.length} lessons`); } async function persistOnSuccess({ diff --git a/packages/ingest/src/steps/2-lp-batch-start.ts b/packages/ingest/src/steps/2-lp-batch-start.ts index 413050872..2b3efe80f 100644 --- a/packages/ingest/src/steps/2-lp-batch-start.ts +++ b/packages/ingest/src/steps/2-lp-batch-start.ts @@ -5,6 +5,7 @@ import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateSta import { Step, getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; import { startGenerating } from "../generate-lesson-plans/startGenerating"; +import { IngestLogger } from "../types"; const currentStep: Step = "lesson_plan_generation"; const prevStep = getPrevStep(currentStep); @@ -15,9 +16,11 @@ const prevStep = getPrevStep(currentStep); */ export async function lpBatchStart({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const ingest = await getIngestById({ prisma, ingestId }); @@ -29,11 +32,11 @@ export async function lpBatchStart({ }); if (lessons.length === 0) { - console.log("No lessons to generate lesson plans for"); + log.info("No lessons to generate lesson plans for"); return; } - console.log(`Generating lesson plans for ${lessons.length} lessons`); + log.info(`Generating lesson plans for ${lessons.length} lessons`); try { await startGenerating({ @@ -53,7 +56,8 @@ export async function lpBatchStart({ }, }); } catch (error) { - console.error("Error generating lesson plans", error); + log.error(String(error)); + log.error("Error starting lesson plan generation, see error above"); await updateLessonsState({ prisma, ingestId, diff --git a/packages/ingest/src/steps/3-lp-batch-sync.ts b/packages/ingest/src/steps/3-lp-batch-sync.ts index 273242cfb..7bf8c9620 100644 --- a/packages/ingest/src/steps/3-lp-batch-sync.ts +++ b/packages/ingest/src/steps/3-lp-batch-sync.ts @@ -3,15 +3,18 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; import { handleLessonPlanBatchSuccess } from "../generate-lesson-plans/handleLessonPlanBatchSuccess"; import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; +import { IngestLogger } from "../types"; /** * Check status of lesson plan generation batches and action */ export async function lpBatchSync({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const batches = await prisma.ingestOpenAiBatch.findMany({ @@ -21,7 +24,7 @@ export async function lpBatchSync({ status: "pending", }, }); - console.log(`Found ${batches.length} pending lesson plan generation batches`); + log.info(`Found ${batches.length} pending lesson plan generation batches`); for (const batch of batches) { const { batch: openaiBatch } = await retrieveOpenAiBatch({ batchId: batch.openaiBatchId, @@ -30,12 +33,12 @@ export async function lpBatchSync({ case "validating": case "in_progress": case "finalizing": - console.log(`Batch ${batch.id} is ${openaiBatch.status}`); + log.info(`Batch ${batch.id} is ${openaiBatch.status}`); break; case "completed": if (openaiBatch.error_file_id) { - console.log(`Batch ${batch.id} has error file, handling...`); + log.info(`Batch ${batch.id} has error file, handling...`); await handleOpenAiBatchErrorFile({ ingestId, prisma, @@ -46,7 +49,7 @@ export async function lpBatchSync({ } if (openaiBatch.output_file_id) { - console.log(`Batch ${batch.id} succeeded, handling...`); + log.info(`Batch ${batch.id} succeeded, handling...`); await handleLessonPlanBatchSuccess({ prisma, ingestId, diff --git a/packages/ingest/src/steps/4-lp-chunking.ts b/packages/ingest/src/steps/4-lp-chunking.ts index 3f15127d8..1718cf158 100644 --- a/packages/ingest/src/steps/4-lp-chunking.ts +++ b/packages/ingest/src/steps/4-lp-chunking.ts @@ -8,6 +8,7 @@ import { getLessonPlanParts } from "../chunking/getLessonPlanParts"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; import { Step, getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; +import { IngestLogger } from "../types"; const currentStep: Step = "chunking"; const prevStep = getPrevStep(currentStep); @@ -17,9 +18,11 @@ const prevStep = getPrevStep(currentStep); */ export async function lpChunking({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const lessons = await loadLessonsAndUpdateState({ @@ -30,11 +33,11 @@ export async function lpChunking({ }); if (lessons.length === 0) { - console.log("No lessons to chunk, exiting early"); + log.info("No lessons to chunk, exiting early"); return; } - console.log(`Chunking lesson plans for ${lessons.length} lesson`); + log.info(`Chunking lesson plans for ${lessons.length} lesson`); const lessonIdsFailed: string[] = []; const lessonIdsCompleted: string[] = []; @@ -87,6 +90,6 @@ export async function lpChunking({ stepStatus: "completed", }); - console.log(`Chunking ${lessons.length} lesson plans completed`); - console.log(`Failed: ${lessonIdsFailed.length}`); + log.info(`Chunking ${lessons.length} lesson plans completed`); + log.info(`Failed: ${lessonIdsFailed.length}`); } diff --git a/packages/ingest/src/steps/5-lp-parts-embed-start.ts b/packages/ingest/src/steps/5-lp-parts-embed-start.ts index f08e6a92b..8aea6fb4d 100644 --- a/packages/ingest/src/steps/5-lp-parts-embed-start.ts +++ b/packages/ingest/src/steps/5-lp-parts-embed-start.ts @@ -5,6 +5,7 @@ import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateSta import { Step, getPrevStep } from "../db-helpers/step"; import { startEmbedding } from "../embedding/startEmbedding"; import { parseCustomId } from "../openai-batches/customId"; +import { IngestLogger } from "../types"; import { chunkAndPromiseAll } from "../utils/chunkAndPromiseAll"; const currentStep: Step = "embedding"; @@ -15,9 +16,11 @@ const prevStep = getPrevStep(currentStep); */ export async function lpPartsEmbedStart({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const ingest = await getIngestById({ prisma, ingestId }); @@ -41,7 +44,7 @@ export async function lpPartsEmbedStart({ .flat(); if (allParts.length === 0) { - console.log("No lesson plan parts to embed, exiting early"); + log.info("No lesson plan parts to embed, exiting early"); return; } diff --git a/packages/ingest/src/steps/6-lp-parts-embed-sync.ts b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts index c9dd65243..69727ab22 100644 --- a/packages/ingest/src/steps/6-lp-parts-embed-sync.ts +++ b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts @@ -3,15 +3,18 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; import { handleEmbeddingBatchSuccess } from "../embedding/handleEmbeddingBatchSuccess"; import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; +import { IngestLogger } from "../types"; /** * Check status of lesson plan generation batches and action */ export async function lpPartsEmbedSync({ prisma, + log, ingestId, }: { prisma: PrismaClientWithAccelerate; + log: IngestLogger; ingestId: string; }) { const embeddingsBatches = await prisma.ingestOpenAiBatch.findMany({ @@ -23,11 +26,11 @@ export async function lpPartsEmbedSync({ }); if (embeddingsBatches.length === 0) { - console.log("No embedding batches to check, exiting early"); + log.info("No embedding batches to check, exiting early"); return; } - console.log(`Checking ${embeddingsBatches.length} embedding batches`); + log.info(`Checking ${embeddingsBatches.length} embedding batches`); for (const batch of embeddingsBatches) { const { batch: openaiBatch } = await retrieveOpenAiBatch({ @@ -37,12 +40,12 @@ export async function lpPartsEmbedSync({ case "validating": case "in_progress": case "finalizing": - console.log(`Batch ${batch.id} is ${openaiBatch.status}`); + log.info(`Batch ${batch.id} is ${openaiBatch.status}`); break; case "completed": if (openaiBatch.error_file_id) { - console.log(`Batch ${batch.id} has error file, handling...`); + log.info(`Batch ${batch.id} has error file, handling...`); await handleOpenAiBatchErrorFile({ prisma, ingestId, @@ -53,7 +56,7 @@ export async function lpPartsEmbedSync({ } if (openaiBatch.output_file_id) { - console.log(`Batch ${batch.id} succeeded, handling...`); + log.info(`Batch ${batch.id} succeeded, handling...`); await handleEmbeddingBatchSuccess({ prisma, ingestId, diff --git a/packages/ingest/src/types.ts b/packages/ingest/src/types.ts new file mode 100644 index 000000000..66f91c873 --- /dev/null +++ b/packages/ingest/src/types.ts @@ -0,0 +1,6 @@ +import { StructuredLogger } from "@oakai/logger"; + +export type IngestLogger = { + info: (...args: unknown[]) => void; + error: StructuredLogger["error"]; +}; diff --git a/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts b/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts index d47cef341..e5cc88fea 100644 --- a/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts +++ b/packages/ingest/src/utils/splitJsonlByRowsOrSize.ts @@ -51,7 +51,7 @@ export async function splitJsonlByRowsOrSize({ `${inputFileName}_${currentFileIndex}.jsonl`, ); const newWriteStream = fs.createWriteStream(outputFilePath); - console.log(`Creating new file: ${outputFilePath}`); + log.info(`Creating new file: ${outputFilePath}`); outputFilePaths.push(outputFilePath); currentFileIndex++; currentRowCount = 0; From d2dfabcb41ed85a731435ed8283540cd974b0de5 Mon Sep 17 00:00:00 2001 From: MG Date: Thu, 24 Oct 2024 13:42:39 +0100 Subject: [PATCH 021/127] fix: logger to stdout for info and warn (#266) --- packages/logger/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 87b65b746..4d3238a44 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -7,6 +7,8 @@ if (typeof window !== "undefined") { } const debugBase = debug("ai"); +// By default debug logs to stderr, we want to use stdout +debugBase.log = console.log.bind(console); type ChildKey = | "admin" From b8b39b833c788ca7fc38c9319c401aae2c1d330a Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 13:43:19 +0100 Subject: [PATCH 022/127] feat: hint Aila as to which part of the lesson to update next (#265) --- .../src/lib/lessonPlan/sectionsInOrder.ts | 23 ++ .../src/prompts/lesson-assistant/index.ts | 10 +- .../prompts/lesson-assistant/parts/body.ts | 224 +++++++------ .../parts/endingTheInteraction.ts | 14 +- .../parts/interactingWithTheUser.ts | 297 +++++++++++------- .../lesson-assistant/parts/lessonComplete.ts | 10 +- .../parts/promptingTheUser.ts | 40 +++ .../src/prompts/lesson-assistant/parts/rag.ts | 3 + .../prompts/lesson-assistant/parts/task.ts | 6 +- 9 files changed, 387 insertions(+), 240 deletions(-) create mode 100644 apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts create mode 100644 packages/core/src/prompts/lesson-assistant/parts/promptingTheUser.ts diff --git a/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts b/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts new file mode 100644 index 000000000..a0e18facd --- /dev/null +++ b/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts @@ -0,0 +1,23 @@ +import { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; + +export const allSectionsInOrder: LessonPlanKeys[] = [ + "learningOutcome", + "learningCycles", + "priorKnowledge", + "keyLearningPoints", + "misconceptions", + "keywords", + "starterQuiz", + "cycle1", + "cycle2", + "cycle3", + "exitQuiz", + "additionalMaterials", +]; + +export const groupedSectionsInOrder: LessonPlanKeys[][] = [ + ["learningOutcome", "learningCycles"], + ["priorKnowledge", "keyLearningPoints", "misconceptions", "keywords"], + ["starterQuiz", "cycle1", "cycle2", "cycle3", "exitQuiz"], + ["additionalMaterials"], +]; diff --git a/packages/core/src/prompts/lesson-assistant/index.ts b/packages/core/src/prompts/lesson-assistant/index.ts index a2fb459ae..2a9ec853a 100644 --- a/packages/core/src/prompts/lesson-assistant/index.ts +++ b/packages/core/src/prompts/lesson-assistant/index.ts @@ -18,6 +18,7 @@ import { import { currentLessonPlan } from "./parts/currentLessonPlan"; import { languageAndVoice } from "./parts/languageAndVoice"; import { lessonComplete } from "./parts/lessonComplete"; +import { promptingTheUser } from "./parts/promptingTheUser"; export interface TemplateProps { relevantLessonPlans?: string; @@ -57,20 +58,21 @@ export const getPromptParts = (props: TemplateProps): TemplatePart[] => { const parts: (TemplatePart | undefined)[] = [ context, - currentLessonPlan, task, - body, - props.useRag ? rag : undefined, - props.baseLessonPlan ? basedOn : undefined, props.responseMode === "interactive" ? interactingWithTheUser : undefined, props.responseMode === "interactive" ? lessonComplete : undefined, props.responseMode === "interactive" ? endingTheInteractionSection : undefined, + body, + currentLessonPlan, + props.useRag ? rag : undefined, + props.baseLessonPlan ? basedOn : undefined, americanToBritishSection, languageAndVoice, props.isUsingStructuredOutput ? undefined : schema, response, + props.responseMode === "interactive" ? promptingTheUser : undefined, signOff, ]; diff --git a/packages/core/src/prompts/lesson-assistant/parts/body.ts b/packages/core/src/prompts/lesson-assistant/parts/body.ts index 6d3f025d2..961412731 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/body.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/body.ts @@ -8,21 +8,22 @@ A well-thought-out lesson plan should: 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. +* Identify and check for pupils' prior knowledge during a starter quiz. We need to be sure that the pupils know certain things before we can teach them something new based on that knowledge. * Address common misconceptions about the topic * Include some engaging activities to help reinforce the learning points. +* Include some checks for understanding and an exit quiz to allow the teacher to check what pupils have learned during the lesson. Ensure that the keywords relevant to the topic are repeated throughout the different sections of the lesson plan. Consider what makes a good lesson for children of the given age range, taking into account what they will have already covered in the UK curriculum. Put thought into how the different sections of the lessons link together to keep pupils informed and engaged. LESSON LEARNING OUTCOME -The Lesson Learning Outcome is a description of what the pupils will have learnt by the end of the lesson. +The Lesson Learning Outcome describes what the pupils will have learned by the end of the lesson. This should be phrased from the point of view of the pupil, starting with "I can…". The word limit for this is 30 words and no more. The learning outcome is the main aim of the lesson and should be the first thing the teacher writes when planning a lesson. It should be clear and concise and should be the lesson's main focus. -It should be achievable in the time frame of the lesson, which is typically 50 minutes. +It should be achievable within the lesson's time frame, which is typically 50 minutes for key stages 2, 3, and 4 and 40 minutes for key stage 1. If the title of the proposed lesson is very broad, for instance, "World War 2" or "Space", the learning outcome you generate should be something specifically achievable within this time frame. You should also narrow down the title of the lesson to match the learning outcome. An individual lesson would often sit within a broader scheme of work or unit of work. @@ -34,8 +35,8 @@ LEARNING CYCLES This is where the Lesson Learning Outcome is broken down into manageable chunks for pupils. They are statements that describe what the pupils should know or be able to do by the end of the lesson. Typically, there are no more than two or three of these, and they map one-to-one to the numbered Learning Cycles that the lesson includes. -These should be phrased as a command starting with a verb (Name, Identify, Label, State, Recall, Define, Sketch, Describe, Explain, Analyse, Discuss, Apply, Compare, Calculate, Construct, Manipulate, Evaluate). -E.g. "Recall the differences between animal and plant cells" or "Calculate the area of a triangle". +These should be phrased as commands starting with a verb (e.g. Name, Identify, Label, State, Recall, Define, Sketch, Describe, Explain, Analyse, Discuss, Apply, Compare, Calculate, Construct, Manipulate, Evaluate). +For example, "Recall the differences between animal and plant cells" or "Calculate the area of a triangle". The word limit for each of these is 20 words and no more. They should increase in difficulty as the lesson progresses. @@ -55,25 +56,38 @@ Do not include anything too advanced for them. Use language and concepts that are appropriate. Base your answer on other lesson plans or schemes of work that you have seen for lessons delivered in UK schools. -KEYWORDS -These are significant or integral words which will be used within the lesson. -Pupils will need to have a good understanding of these words to access the lesson's content. -They should be Tier 2 or Tier 3 words. -Tier 2 vocabulary is academic vocabulary that occurs frequently in text for pupils but is not subject-specific. For example, "beneficial", "required" or "explain". -Tier 3 vocabulary occurs less frequently in texts but is subject specific. For example, "amplitude" or "hypotenuse". -When giving the definition for each keyword, make sure that the definition is age-appropriate and does not contain the keyword itself within the Explanation. -For example, "Cell Membrane": -"A semi-permeable membrane that surrounds the cell, controlling the movement of substances in and out of the cell." -Try to make your definitions as succinct as possible. - KEY LEARNING POINTS The key learning points are the most important things that the pupils should learn in the lesson. These are statements that describe in more detail what the pupils should know or be able to do by the end of the lesson. These factually represent what the pupils will learn rather than the overall objectives of the lesson. The key learning points should be succinct, knowledge-rich, factual statements. -For example, describing what will be learnt is incorrect: "The unique features of plant cells, including cell walls, chloroplasts, and large vacuoles". +For example, describing what will be learned is incorrect: "The unique features of plant cells, including cell walls, chloroplasts, and large vacuoles". This example should instead appear as "A plant cell differs from an animal cell because it has a cell wall, chloroplast and a large vacuole". +MISCONCEPTIONS +Misconceptions are incorrect beliefs about a topic or subject. +It is important for a teacher to be aware of these before they start teaching a lesson because they should try to address these with pupils during the explanation. +Checks for understanding, practice tasks and quizzes should enable the teacher to check whether pupils have these misconceptions about a topic so that they can correct them if they do. +The misconception and response should be written as a succinct sentence. +You should then include a misconception response which says how the misconception should be addressed. +For example, a common misconception in maths is that "multiplying two numbers always produces a bigger number". +The correction to this misconception could be, "Multiplying by less than one or a negative can result in a smaller number, and multiplying by zero will result in an answer of zero." +You can provide between 1 and 3 misconceptions. +Only provide corrections that are factually correct, not based on just opinion. +The misconception should be no longer than 200 characters. +The misconception response should be no longer than 250 characters. + +KEYWORDS +These are significant or integral words which will be used within the lesson. +Pupils will need to have a good understanding of these words to access the lesson's content. +They should be Tier 2 or Tier 3 words. +Tier 2 vocabulary is academic vocabulary that occurs frequently in text for pupils but is not subject-specific. Examples include "beneficial," "required," and "explain." +Tier 3 vocabulary occurs less frequently in texts but is subject-specific. For example, "amplitude" or "hypotenuse". +When giving the definition for each keyword, make sure that the definition is age-appropriate and does not contain the keyword itself within the Explanation. +For example, "Cell Membrane": "A semi-permeable membrane that surrounds the cell, controlling the movement of substances in and out of the cell." +Try to make your definitions as succinct as possible. +The definition should be no longer than 200 characters. + QUIZZES The lesson plan should begin with a Starter Quiz and end with an Exit Quiz. ${ @@ -83,11 +97,11 @@ ${ : "" } 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, presented to pupils at the start of the lesson, should check the pupils' prior knowledge before the lesson begins. The Starter Quiz should be based on the prior knowledge and potential misconceptions only within the prior knowledge. Do not test pupils on anything that is contained within the lesson itself. Imagine a pupil begins the lesson and knows about the things listed in the prior knowledge section. -The teacher delivering the lesson wants to make sure that before starting the lesson, all of the pupils know about the required knowledge listed in the prior knowledge section so that all pupils are starting the lesson from a point where they already know these foundational concepts. +The teacher delivering the lesson wants to make sure that all of the pupils know the required knowledge listed in the prior knowledge section before starting the lesson so that all pupils start the lesson from a point where they already know these foundational concepts. If the pupils don't know these things, they will struggle with the lesson, so the teacher wants to ask a series of questions to check what the pupils know before starting the lesson. This is the purpose of the Starter Quiz, so it is important we get it right! The contents of the Starter Quiz should be questions that test the PRIOR KNOWLEDGE as defined in the lesson plan. @@ -104,12 +118,15 @@ EXIT QUIZ The Exit Quiz at the end of the lesson should check the pupils' understanding of the topics covered in the lesson. If a pupil has correctly completed the Exit Quiz, they have understood the key learning points and misconceptions or common mistakes in the lesson. The Exit Quiz should test the pupils only on the concepts introduced in the lesson and not the prior knowledge. +The Exit Quiz should be six questions long. +It should get harder as pupils go through. +It should include one question that tests pupils on their understanding of one of the keywords. HOW TO MAKE A GOOD QUIZ A quiz comprises one or more correct answers and one or more "distractor" answers that should be subtly incorrect. It should be engaging and suitably challenging for the given age range. Consider the level of detail the given subject will have been taught at for the age range, and the level of reading when deciding on suitable responses. -Compared to the answer, the distractors should sound plausible and be of a similar length to the correct answer(s), but with some consideration, a pupil in the given age-range should be able to identify the correct answer. +Compared to the answer, the distractors should sound plausible and be of a similar length to the correct answer(s), but with some consideration, a pupil in the given age range should be able to identify the correct answer. Consider working common misconceptions into the quiz distractors. Never use negative phrasing in the question or answers. For instance, never produce a question starting with "Which of these is not…". @@ -142,44 +159,46 @@ What is the periodic table? * a table that shows all chemical reactions * a table that shows all known elements -Wherever we refer to plausible distractors, for instance, in the quizzes and the checks for understanding, ensure that you follow these guidelines to ensure that the distractors are of high quality. +Wherever plausible distractors are mentioned, for instance, in the quizzes and the checks for understanding, follow these guidelines to ensure that the distractors are of high quality. LEARNING CYCLES -Based on the overall plan, and only when requested, you will create two or three Learning Cycles that go into more detail about specifically how the lesson should be structured. -The first time that you mention Learning Cycles in conversation with the user, please explain what they are. For example, "Learning Cycles are how Oak structures the main body of the lesson and follow a consistent structure". +Based on the overall plan, and only when requested, you will create two or three Learning Cycles that describe in more detail how the lesson should be structured. +The first time that you mention Learning Cycles in conversation with the user, please explain what they are. +For example, "Learning Cycles are how Oak structures the main body of the lesson, they include an explanation, some checks for understanding, a practice task and some feedback". The main body of the lesson is delivered in these cycles. -A Learning Cycle is defined as the sequence of Explanation, interspersed with Checks for Understanding and Practice, with accompanying Feedback, that together facilitate the teaching of knowledge. +A Learning Cycle is defined as a sequence of an Explanation, interspersed with Checks for Understanding and Practice, with accompanying Feedback, that together facilitate the teaching of knowledge. A Learning Cycle should last between 10-20 minutes. The whole lesson should take 50 minutes in total. -The Learning Cycles should add up to 45 minutes because the teacher will spend approximately 5 minutes on the starter and Exit Quiz. +The Learning Cycles should total 45 minutes because the teacher will spend approximately 5 minutes on the starter and Exit Quiz. Rather than writing about what a teacher should generally do when delivering a lesson, you want to write about what you specifically want to do when delivering this lesson. You want to write about the specific content you want to teach and the specific checks for understanding and practice you want to use to teach it. -The audience is another teacher who likely has many years of experience teaching the subject, so you do not need to explain the subject matter in detail. You can assume that the audience is familiar with the subject matter, so you can focus on explaining how you want to teach it. For each Learning Cycle, you want to write about the following: Explanation: This is the first phase of a Learning Cycle. It aims to communicate the key points/concepts/ideas contained in the Learning Cycle in a simple way. There are two elements of an explanation: the spoken teacher explanation and the accompanying visual elements. -Visual elements are diagrams, images, models, examples and (limited) text that will go onto the slides the teacher will use whilst teaching. +Visual elements are diagrams, images, models, examples, and (limited) text that the teacher will use on the slides. LEARNING CYCLES: SUBSECTION RULES: Make sure to follow the following rules that relate to particular subsections within each Learning Cycle. It's very important that you adhere to the following rules because each Learning Cycle must adhere to these requirements to be valid. LEARNING CYCLES: TEACHER EXPLANATION: -The spoken teacher explanation must be concise and should make it clear to the teacher the concepts and knowledge the teacher must explain during that Learning Cycle. +The spoken teacher explanation must be concise and should clearly state the concepts and knowledge the teacher must explain during that Learning Cycle. It is directed to the teacher, telling them the key concepts that they will need to explain during this section of the lesson. -They may include analogies, can include examples, non-examples and worked examples, may include stories, a chance for the teacher to model or demonstrate procedural knowledge (this should be broken down into steps) and may have opportunities for discussion. Opportunities for discussion may be indicated by posing a question to pupils. +You can suggest analogies, examples, non-examples, worked examples, relevant stories from history or the news. +You can indicate appropriate opportunities for discussion by posing a question. +You should suggest appropriate moments and methods for the teacher to model or demonstrate procedural knowledge (this should be broken down into steps). If artefacts such as a globe or a hat would be useful for teachers to use in their explanation, you can indicate this during this section of the Explanation. It should always be optional to have this artefact. Good verbal explanations should link prior knowledge to new knowledge being delivered. Be as specific as possible as the teacher may not have good knowledge of the topic being taught. -E.g. rather than saying "describe the key features of a Seder plate", say "Describe the meaning of the hank bone (zeroa), egg (beitzah), bitter herbs (maror), vegetable (karpas) and a sweet paste (haroset) in a Seder plate." +E.g. rather than saying "Describe the key features of a Seder plate", say "Describe the meaning of the hank bone (zeroa), egg (beitzah), bitter herbs (maror), vegetable (karpas) and a sweet paste (haroset) in a Seder plate." Typically, this should be five or six sentences or about 5-12 points in the form of a markdown list. Make sure to use age-appropriate language. Explanations should minimise extraneous load and optimise intrinsic load. You will also provide the information for the visual part of the Explanation. -This will include the accompanying slide details, an image search suggestion and the slide text. +This will include the Accompanying slide details, an Image search suggestion and the Slide text. LEARNING CYCLES: ACCOMPANYING SLIDE DETAILS: This should be a description of what the teacher should show on the slides to support their spoken teacher explanation. @@ -192,39 +211,28 @@ For example, "hydrogen molecule covalent bond". LEARNING CYCLES: SLIDE TEXT: This will be the text displayed to pupils on the slides during the lesson. It should be a summary of the key point being made during the explanation. -For example: "An antagonistic muscle pair has one muscle which contracts whilst the other muscle relaxes or lengthens." -This should NOT include any teacher narrative. For example, this would be incorrect as slide text: "now we will look at the antagonistic muscle pairs... " +For example, "An antagonistic muscle pair has one muscle which contracts whilst the other muscle relaxes or lengthens." +This should not include any teacher narrative. +For example, this would be incorrect as slide text: "Now we will look at the antagonistic muscle pairs... " LEARNING CYCLES: CHECKS FOR UNDERSTANDING: A Check For Understanding follows the explanation of a key learning point, concept or idea. It is designed to check whether pupils have understood the explanation given. Produce two Check For Understanding questions in each Learning Cycle. -These should be multiple-choice questions with one correct answer and two plausible distractors which test for common misconceptions or mistakes. +These should be multiple-choice questions, with one correct answer and two plausible distractors, that test for common misconceptions or mistakes. +Try to test whether pupils have understood the knowledge taught rather than just their ability to recall it. +For example, it would be better to ask: "Which of these is a prime number?" "4, 7, 10?" rather than "What is the definition of a prime number?" "numbers divisible by only one other number, integers divisible by only 1 and the number itself, odd numbers divisible by only 1 and the number itself". Write the answers in alphabetical order. -The questions should not be negative questions. For example, do not ask "Which of these is NOT a covalent bond?". +The questions should not be negative. For example, do not ask, "Which of these is NOT a covalent bond?" Answers should also not include "all of the above" or none of the above". The Check For Understanding questions should not replicate any questions from the Starter Quiz. Do not use "true or false" questions to Check For Understanding. -LEARNING CYCLES: FEEDBACK -The feedback section of a Learning Cycle allows pupils to receive feedback on their work. -As this is often done in a class of thirty, a good way of doing this will often be providing a model answer e.g. a good example of a labelled diagram or a well-written paragraph or a correctly drawn graph. -If possible, an explanation should be given as to why this is the correct answer. -If the practice task involves a calculation(s), the feedback may be a worked example. -In other situations, it may be more appropriate to provide a list of success criteria for a task the teacher or pupil can use to mark their own work against. -If neither of these is an appropriate form of feedback, you should give very clear instructions for the teacher about how they will provide feedback for the pupil. -The feedback section of a Learning Cycle is designed to give pupils the correct answers to the practice task. -This may be giving them a worked example, a model answer or a set of success criteria against which to assess their practice.. -For example, if pupils have completed a set of calculations in the practice task, the feedback should be a set of worked examples with the correct answers. -If the task is practising bouncing a basketball, then the feedback should be a set of success criteria such as "1. Bounce the ball with two hands. 2. Bounce the ball to chest height." -You should indicate whether you are giving a "worked example", "model answer" or "success criteria" before giving the feedback. -The feedback should be pupil-facing, because it will be displayed directly on the slides. For example, "Model answer: I can tell that this is a covalent bond because there are two electrons being shared by the pair of atoms" rather than "Get pupils to mark their answer above covalent bonding". - LEARNING CYCLES: PRACTICE TASKS -Practice: During the practice section of a Learning Cycle, you are setting a task for pupils that will get them to practice the knowledge or skill that they have learnt during the Explanation. -Your instructions for this part of the lesson should be pupil-facing, specific and include all of the information that pupils will need to complete the task e.g. "Draw a dot and cross diagram to show the bonding in O2, N2 and CO2" rather than "get pupils to draw diagrams to show the bonding in different covalent molecules." +During the practice section of a Learning Cycle, you set a task for pupils that will help them practice the knowledge or skill that they have learned during the Explanation. +Your instructions for this part of the lesson should be pupil-facing and specific and include all of the information that pupils will need to complete the task e.g. "Draw a dot and cross diagram to show the bonding in O2, N2 and CO2" rather than "get pupils to draw diagrams to show the bonding in different covalent molecules." You should provide everything in your instructions that the pupils will need to be able to complete the practice task. -For example, if you are asking pupils : +For example, if you are asking pupils: * to analyse a set of results, provide them with the results and the set of questions that will help them to complete their analysis. * to complete a matching task, provide them with the content that needs to be matched. * to complete sentences, provide them with the sentences with the gaps marked within them. @@ -232,72 +240,66 @@ For example, if you are asking pupils : * to give an opinion on an extract, provide them with the extract. The practice should increase in difficulty if you are asking pupils to do more than one example/question. -In the example given, doing the dot and cross diagram for CO2 is much more challenging than doing a dot and cross diagram for O2. +In the example given, the dot-and-cross diagram for CO2 is much more challenging than the dot-and-cross diagram for O2. Practice is essential for securing knowledge, and so this is the most important part of the lesson to get right. -The practice should link to the Learning Cycle outcomes that you have set at the start of the lesson plan. -The practice task should take up the majority of the time in the Learning Cycle but ensure it is possible to complete the explanation, checks for understanding, practice task and feedback in the time allocated to the Learning Cycle. Typically the practice task should take between five and ten minutes. +The practice should be linked to the Learning Cycle outcomes that you set at the start of the lesson plan. +The practice task should take up the majority of the time in the Learning Cycle. +Ensure that the Explanation, Checks for understanding, Practice task, and Feedback can be completed in the time allocated to the Learning Cycle. +Typically the practice task should take between five and ten minutes. Asking the pupils to create a newspaper article and present it to the class is not possible in fifteen minutes! Be realistic about what can be achieved in the time limit. -Base your answer on other lesson plans that you have seen for lessons delivered in UK schools. +Base your suggestions on other lesson plans that you have seen for lessons delivered in UK schools. The practice task for each Learning Cycle should be different to ensure that there is a variety of activities for pupils in the lesson. Practice might look very different for some subjects. In maths lessons, this will normally be completing mathematical calculations, it will normally include giving spoken or written answers. -In more practical subjects, for example, PE, Art, Music etc., it might involve a pupil practising a skill or taking part in a game/performance activity. +In more practical subjects, such as PE, Art, Music, etc., it might involve a pupil practising a skill or taking part in a game or performance activity. Practice tasks should allow pupils the opportunity to practice the knowledge that they have learnt during the explanation. -It should force all pupils in the room to be active learners, contributing in some way either verbally, physically or through writing, drawing or creating. -If a child correctly completes the practice task, they have mastered the key learning points for that Learning Cycle. +It should force all pupils in the room to be active learners, contributing in some way, either verbally or physically, through writing, drawing, or creating. +If a pupil correctly completes the practice task, they have mastered the key learning points for that Learning Cycle. For a practice task to be effective, it needs to be specific enough to ensure the desired knowledge is being practised. The Learning Cycle outcome will include a command word, and this should direct you to the most appropriate practice task from this list of example tasks: STARTING EXAMPLE TASKS -Label a diagram with the given labels. -Circle a word or picture that matches the description. -Sort items into two or three columns in a table. -Sort items into a Venn diagram. -Sort items into four quadrants based on a scale of two properties. -Explain why an item is hard to classify. +Label a diagram with the provided labels. +Circle a word or picture that matches the given description. +Sort given items into two or three columns in a table. +Sort given items into a Venn diagram. +Sort given items into four quadrants based on a scale of two properties. +Explain why a given item is hard to classify. Provided with an incorrect classification, explain why the object has been incorrectly classified. -Match key terms to definitions. +Match given key terms to given definitions. Fill in the gaps in a sentence to complete a definition. Finish a sentence to complete a definition. Select an item from a list or set of pictures that matches the key term or definition and justify your choice. Correct an incorrect definition given. List the equipment/materials needed for an activity. -List items in order of size, age, number, date, etc. +List given items in order of size, age, number, date, etc. List [insert number] of factors that will have an impact on [insert other thing]. List the steps in a given method. -Identify an item on a list that does not belong on the list and give a reason for your decision. +Identify an item on a given list that does not belong on the list and give a reason for your decision. Correct an incorrectly ordered list. -Fill in the gaps in a sentence to complete a description of a process, phenomenon, event, situation, pattern or technique. -Finish a sentence to complete a description of a process, phenomenon, event, situation, pattern or technique. -Decide which of two given descriptions is better and explain why. -Fill in the gaps in a sentence to complete an explanation of a process, phenomenon, event, situation, pattern or technique. -Finish a sentence to complete an explanation of a process, phenomenon, event, situation, pattern or technique. -Order parts of an explanation in the correct order. +Fill in the gaps in a sentence to complete a description or explanation of a process, phenomenon, event, situation, pattern or technique. +Finish a sentence or paragraph to complete a description or explanation of a process, phenomenon, event, situation, pattern or technique. +Decide which of two given descriptions/answers/responses is better and explain why. +Order steps/parts of an explanation/method into the correct order. Write a speech to explain a concept to someone. Draw and annotate a diagram(s) to explain a process/technique. Explain the impact of a process, phenomenon, event, situation, pattern or technique on a person, group of people or the environment. Apply a given particular skill/technique to a given task. -Fill in the gaps in a sentence to complete a description or explanation of a process, phenomenon, event, situation, pattern or technique. -Finish a sentence to complete a description or explanation of a process, phenomenon, event, situation, pattern or technique. Choose the most appropriate item for a given scenario and justify why you have chosen it. Apply a skill that has been taught to complete a practice calculation (should begin with a simple application and then progress to more complex problems, including worded questions). When given an example, determine which theories, contexts or techniques have been applied. Extract data from a table or graph and use this to write a conclusion. -Complete a series of 4-5 practice calculations. -When asking pupils to complete a calculation, there should always be a model in the explanation section of the lesson. -Then, the practice task should always start from easy, just requiring substitution into an equation or scenario, to more complex, where pupils are asked to rearrange an equation, convert units or complete an additional step. -Each time, a challenge question should be provided, which is a scenario-based worded problem (with age-appropriate language). +Complete a series of 4-5 practice calculations (when asking pupils to complete a calculation, there should always be a model in the explanation section of the lesson, the practice task should always start from easy, just requiring substitution into an equation or scenario, to more complex, where pupils are asked to rearrange an equation, convert units or complete an additional step. Each time, a challenge question should be provided, which is a scenario-based worded problem with age-appropriate language). Present an incorrect worked example for a calculation and get pupils to correct it or spot the error. Present two items and get pupils to identify two similarities and two differences. Present an item and get pupils to compare it to a historical or theoretical example. -Complete sentences to compare two things (e.g. Duncan and Macbeth - two characters from Macbeth or animal and plant cells). -The sentences should miss out the more important piece of knowledge for pupils to recall or process. For example, what the actual difference between them is. +Complete sentences to compare two things, for example, Duncan and Macbeth - two characters from Macbeth or animal and plant cells. The sentences should miss out the more important piece of knowledge for pupils to recall or process. For example, what the actual difference between them is. Present two items and get pupils to identify two differences. Present an item and get pupils to identify differences between the item and a historical or theoretical example. Complete sentences describing the differences between two items (e.g. Duncan and Macbeth - two characters from Macbeth or animal and plant cells). -The sentences should miss out the more important piece of knowledge for pupils to recall or process. For example, what the actual difference between them is. -Create a routine/performance/piece of art for a given scenario for a given user group or audience. +The sentences should miss out the more important piece of knowledge for pupils to recall or process, such as the actual difference between them. +Create a routine, performance, or work of art for a given scenario for a given user group or audience. Create a set of instructions for solving a problem. Given a set of different opinions, decide which are for and against an argument. Given an opinion, write an opposing opinion. @@ -308,9 +310,9 @@ Given the answer to a problem, show the workings-out of the calculation that der Draw an annotated sketch of a product. Write a flow chart for the steps you would take to create or carry out [insert product/task/experiment]. Put steps in order to create/carry out [insert product/task/experiment]. -Identify a mistake or missing step in a method. +Identify a mistake or missing step in a given method. Fill in the gaps in a sentence to complete an interpretation or give the reasons for a quote, set of results, event, situation or pattern. -Finish a sentence to complete an interpretation or give the reasons for of a quote, set of results, event, situation or pattern. +Finish a sentence to complete an interpretation or give the reasons for a quote, set of results, event, situation or pattern. Explain how an image relates to the topic being discussed. Explain which techniques, mediums or quotes have been used and where their inspiration to use these came from (i.e. which pieces of work/artists/periods/movements). Identify the intended audience for a piece of work and explain how you have reached this conclusion. @@ -319,33 +321,55 @@ Fill in the gaps in a sentence to make a prediction. Finish a sentence to make a prediction. Explain why a given prediction is unlikely. Match the given predictions to given scenarios. -Watch a short clip of someone performing a particular sport/training/performance, and give strengths/weaknesses and suggest improvements. +Watch a short clip of someone performing a particular sport/training/performance, give strengths/weaknesses and suggest improvements. Describe the similarities and differences between the work of different experts in the given subject. E.g. Monet and Picasso. Compare a piece of work to a model and explain similarities, differences and areas for improvement (e.g. a piece of pupil work to a model answer or a piece of art designed to mimic the work of a great artist and the great artist's original piece). Reflect on the work that you have created and how closely it meets the design brief, identifying strengths and areas for development. Ask pupils to comment on the repeatability, reproducibility, accuracy, precision or validity of a given method, set of results or source of information. Extract data from a table or graph and use this to support a conclusion. -Justify the use of a piece of equipment, technique or method, giving reasons for or against using it/other options. +Justify the use of a piece of equipment, technique or method, giving reasons for or against using it or other options. Fill in the gaps in a sentence by giving the reasons for a quote, set of results, decision, event, situation or pattern. Finish a sentence to give the reasons for a quote, set of results, event situation or pattern. ENDING EXAMPLE TASKS +LEARNING CYCLES: FEEDBACK +The feedback section of a Learning Cycle allows pupils to receive feedback on their work and see the correct or a model answer. +As this is often done in a class of thirty pupils, you might provide a model answer e.g. a good example of a labelled diagram or a well-written paragraph or a correctly drawn graph. +If possible, an explanation should be given as to why this is the correct answer. +If the practice task involves a calculation(s), the feedback could be a worked example with the correct answers. +In other situations, it may be more appropriate to provide a list of success criteria for a task the teacher or pupil can use to mark their own work against. +If none of these formats are appropriate, you should give very clear instructions for the teacher about how they will provide feedback to the pupil. +For example, if pupils have completed a set of calculations in the practice task, the feedback should be a set of worked examples showing the steps in the calculation with the correct answers. +If the task is practising bouncing a basketball, then the feedback should be a set of success criteria such as "1. Bounce the ball with two hands. 2. Bounce the ball to chest height." +Before giving feedback, you should indicate whether you are providing a "worked example," "model answer," or "success criteria." +The feedback should be pupil-facing because it will be displayed directly on the slides. +For example, "Model answer: I can tell that this is a covalent bond because there are two electrons being shared by the pair of atoms" rather than "Get pupils to mark their answer above covalent bonding". +You should decide which is the most appropriate form of feedback for the specific practice task. + END OF RULES FOR LEARNING CYCLES ADDITIONAL MATERIALS -For some lessons, it may be useful to produce additional materials. +For some lessons, it may be useful to produce additional materials when the user requests it. This is a free-form markdown section with a maximum H2 heading (Eg. ##). -If the lesson includes a practical element, the additional materials should include a list of equipment required, methods, safety instructions and potentially, model results. -It may also be appropriate to include a narrative for a Learning Cycle(s) which supports the teacher with their Explanation that accompanies the visual Explanation of the lesson. -If included, this should be written as a script for the teacher to use. -It should include the factual information and key learning points that they teacher is going to impart. -If including a narrative, you should ask the teacher if they have a preference on the specific content being included before creating the additional materials. +This section could include: +* a case study context sheet (including details on location/historical context/short-term and long-term causes/effects/impacts) +* additional questions for homework practice. +* practical instructions to support a teacher in running an experiment that you have suggested in the lesson (a list of equipment required, methods, safety instructions and potentially, model results). +* suggestions of ways of adapting a teacher's delivery for the SEND pupils they have told you they have in their class. +* a piece of text that you have asked pupils to read/analyse during the practice task (if it is longer than 30 words so it won't fit on a PowerPoint slide in a reasonable font) +* a narrative (written as a script) to support the teacher in delivering a tricky explanation. +* suggestions for warm-up or cool-down activities for a PE lesson. +* suggestions for alternative equipment/case studies/examples. +* a translation of keywords into another language (always provide the keyword and definition in English, and then the keyword and definition in the requested language and remind teachers to check translations carefully). +or anything else that would be appropriate for supporting a teacher in delivering a high-quality lesson. +If a narrative is chosen for the additional materials, this should be written as a script for the teacher to use. +It should include the factual information and key learning points that the teacher is going to impart. +Write the narrative as if the teacher is speaking to the pupils in the classroom. +It should be specific and include analogies and examples where appropriate. +Underneath the narrative, include the main things the teacher should include in their Explanation as bullet points. +If you include a narrative, you should ask the teacher if they have a preference for the specific content before creating the additional materials. For example, if the lesson is about different creation stories, you should ask whether there are any particular creation stories that they want to include, e.g. the Christian creation story. The additional materials may also include search terms to find relevant diagrams or images where appropriate. -For example, for pupils in a Maths lesson to practice counting or for a pupil in an Art lesson to be able to annotate an image of a painting to show different techniques used. -Additional materials may also include a text extract for pupils to read with accompanying questions. -This is if the text is too long for pupils to read from the PowerPoint slides - more than 30 words. -If the user wants you to do so, produce a narrative that they can use for this lesson plan. -The narrative should be written as if the teacher is speaking to the pupils in the classroom. It should be specific and include analogies and examples where appropriate. Underneath the narrative, include the main things the teacher should include in their Explanation. -If there are no additional materials to present, respond with just the word None.`; +If there are no additional materials to present, respond with just the word None. +Only generate additional materials when the user specifically requests them in the instructions.`; }; diff --git a/packages/core/src/prompts/lesson-assistant/parts/endingTheInteraction.ts b/packages/core/src/prompts/lesson-assistant/parts/endingTheInteraction.ts index b99d96b4f..7accea3b7 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/endingTheInteraction.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/endingTheInteraction.ts @@ -1,17 +1,5 @@ export const endingTheInteraction = () => `ENDING THE INTERACTION Once you have sent back all of the edits that you need to make to fulfil the request from the user, you should respond with an additional message with your next question for the user. -This is important because it allows the user to see the previous response and the new response separately. -Everything you send to the user should be in the format of a set of JSON documents. -Do not send text before or after the set of JSON documents. If you want to send any kind of message to the user, us the following format for that message. -Format your message to the user using the following schema. -Do not just send back plain text because that will cause the application to fail: -{"type": "prompt", "message": "Your next question or prompt for the user"} - -EXAMPLE - -For instance, a typical edit might look like this: - -{"type": "patch", "reasoning": "I have chosen these three points because they are the most important things for the pupils to learn in this lesson.", "value": { "op": "add", "path": "/keyLearningPoints", "value": ["Point 1", "Point 2", "Point 3"] }␞ -{"type": "prompt", "message": "Would you now like to add some misconceptions?" }␞`; +{"type": "text", "message": "Your next question or prompt for the user"}`; diff --git a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts index 06d78b91b..25d9b2616 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts @@ -1,139 +1,212 @@ -import { TemplateProps } from ".."; +import { aiLogger } from "@oakai/logger"; -export const interactingWithTheUser = ({ - relevantLessonPlans, -}: TemplateProps) => { +import { TemplateProps } from ".."; +import { allSectionsInOrder } from "../../../../../../apps/nextjs/src/lib/lessonPlan/sectionsInOrder"; +import { + LessonPlanKeys, + LooseLessonPlan, +} from "../../../../../aila/src/protocol/schema"; + +interface LessonConstructionStep { + title: string; + content: string; + sections?: LessonPlanKeys[]; +} + +const log = aiLogger("chat"); + +const lessonConstructionSteps = ( + lessonPlan: LooseLessonPlan, + relevantLessonPlans: string | undefined, +): LessonConstructionStep[] => { + const presentLessonPlanKeys = ( + Object.keys(lessonPlan) as LessonPlanKeys[] + ).filter((k) => lessonPlan[k]); const hasRelevantLessons = - relevantLessonPlans && relevantLessonPlans.length > 0; - const parts = [ - `YOUR INSTRUCTIONS FOR INTERACTING WITH THE USER -This is the most important part of the prompt. -As I have said, you will be provided with instructions during the chat, and you should act based on which part or parts of the lesson plan to alter. -The instructions will arrive as user message in free text. -The instructions might require you to edit more than one part of the lesson plan to respond to the user's request. - -INTERACTION WITH THE USER -After you have sent back your response, prompt the user to provide a new instruction for the next step of the process. -Assume the user will want to continue generating unless they say otherwise. -Give the user a natural way to tap the **Continue** button to move on to the next section, or they can give other instructions to do something else. -This is because there is a button labelled **Continue* in the user interface they are using. -For example, you should end your response with "Tap **Continue** to move on to the next step.". -Make sure the question you ask is not ambiguous about what tapping **Continue** would mean. - -RESPONDING WITH LESSON CONTENT -All of your responses that relate to the lesson plan should be in the form of a JSON PATCH document. -Do not mention the actual content in the text response to the user. -The user sees your changes in the lesson plan display in the user interface, and does not need them duplicated in the text response. - -EACH INTERACTION - -ASKING THE USER IF THEY ARE HAPPY -After each interaction you should check that the user is happy with what you have generated. -Here is an example of how you should respond: - -START OF EXAMPLE HAPPINESS CHECK -Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit below. -END OF EXAMPLE HAPPINESS CHECK - -START OF SECOND EXAMPLE HAPPINESS CHECK -Are the prior knowledge, key learning points, misconceptions, and keywords sections suitable for your class? -END OF SECOND EXAMPLE HAPPINESS CHECK - -GENERATE MULTIPLE SECTIONS TOGETHER, IN ORDER -In your response to the user you will often generate several keys / sections all together at the same time in the order they are listed and specified below. - -STEPS TO CREATE A LESSON PLAN INTERACTIVELY WITH THE USER -The Lesson plan should be constructed in the following steps. Unless prompted by the user to do otherwise, you should follow these steps in order. - -STEP 1: FIX AMERICANISMS, INCONSISTENCIES AND MISSING SECTIONS -First, apply any corrections to the lesson plan by checking for Americanisms or inconsistencies between sections. You should do this automatically within each request, and not require user input. - -* ENSURE THAT THE LESSON PLAN IS GENERATED IN THE CORRECT ORDER -The sections of the lesson plan should be generated in this order: title, subject, topic, keyStage, basedOn (optional), learningOutcome, learningCycles, priorKnowledge, keyLearningPoints, misconceptions, keywords, starterQuiz, cycle1, cycle2, cycle3, exitQuiz, additionalMaterials.`, - - hasRelevantLessons - ? `* ONLY BASE THE LESSON ON AN EXISTING LESSON IF THE USER REQUESTS IT -When generating a lesson plan, it is possible for the user to specify that they want to base the new lesson on an existing one. -To do this we set the basedOn key of the lessonPlan. -It is very important that by default basedOn is not set. -Only set basedOn for lesson if the user has specifically requested it. -If the user does not request a base lesson, do not select one. -This means that until the user has responded, you should not respond with any changes to the basedOn key in the lesson plan.` - : undefined, - - `* ENSURE THAT THERE ARE NO MISSING PAST SECTIONS -If some keys are not present, but you can see from past messages that you have attempted to generate them, you should generate them again, ensuring that the content you generate matches the schema. -This may be indicative of an application error. -For instance, if you have a lesson plan with all sections complete up until the exitQuiz, except for cycle1, this is indicative of an error and you should generate cycle1 again. -Always trust the supplied lesson plan over the provided message history, because this represents the state of the lesson plan as it currently stands.`, - - `OPTIONAL: STEP 1 ENSURE THAT YOU HAVE THE CORRECT CONTEXT -In most scenarios you will be provided with title, keyStage, subject, topic (optionally)in the lesson plan. -If they are not present, ask the user for them. -You can skip this step if the user has provided the title, key stage, subject and topic in the lesson plan.`, - - hasRelevantLessons - ? `STEP 2 ASK THE USER IF THEY WANT TO ADAPT AN EXISTING LESSON -Ask if the user would like to adapt one of the Oak lesson plans as a starting point for their new lesson. + relevantLessonPlans && relevantLessonPlans.length > 3; // TODO This is a string, not an array! + + const steps: (LessonConstructionStep | undefined)[] = [ + lessonPlan.title && lessonPlan.keyStage && lessonPlan.subject + ? undefined + : { + title: `ENSURE YOU HAVE A TITLE, KEY STAGE, SUBJECT, AND TOPIC`, + content: `In order to start the lesson plan you need to be provided with title, keyStage, subject, topic (optionally) in the lesson plan. +These values are not all present, so ask the user for the missing values.`, + }, + + hasRelevantLessons && !Object.keys(lessonPlan).includes("basedOn") + ? { + title: `ASK THE USER IF THEY WANT TO BASE THEIR LESSON PLAN ON AN EXISTING LESSON`, + content: `Ask if the user would like to adapt one of the Oak lessons as a starting point for their new lesson. Provide a list of lessons for the user as numbered options, with the title of each lesson. The user will then respond with the number of the lesson they would like to adapt. +Do not pick from the available lessons and send any patches yet. +Wait for the user to reply. EXAMPLE RESPONSE ABOUT RELEVANT LESSON PLANS These Oak lessons might be relevant: -1. Introduction to the Periodic Table +1. Introduction to the Periodic Table 2. Chemical Reactions and Equations 3. The Structure of the Atom \n To base your lesson on one of these existing Oak lessons, type the lesson number. Tap **Continue** to start from scratch. -END OF EXAMPLE RESPONSE - -RESULT: The user has chosen to adapt an existing lesson -When the user responds, if they have selected a lesson to base their new lesson on, you should set the basedOn key in the lesson plan to match the lesson plan that they have chosen and then proceed to generate the next step. -You should set basedOn.id in the lesson plan to match the "id" of the chosen base lesson plan and the basedOn.title attribute to the "title" of the chosen lesson plan. - -RESULT: The user has chosen to start from scratch -Do not edit the basedOn key in the lesson plan and proceed to generate the next step.` +END OF EXAMPLE RESPONSE`, + } : undefined, - - `STEP 3: learningOutcomes, learningCycles -Generate learning outcomes and the learning cycles overview immediately after you have the inputs from the previous step. - -EXAMPLE RESPONSE`, - hasRelevantLessons - ? `There are no existing Oak lessons for this topic, so we'll start a new lesson from scratch. Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit below.` - : `Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit below.`, - - `STEP 4: priorKnowledge, keyLearningPoints, misconceptions, keywords -Then, generate these four sections in one go. - -STEP 5: starterQuiz, cycle1, cycle2, cycle3, exitQuiz -Then, generate the bulk of the lesson. Do all of this in one go. + !hasRelevantLessons + ? { + sections: ["learningOutcome", "learningCycles"] as LessonPlanKeys[], + title: `GENERATE SECTION GROUP [learningOutcome, learningCycles]`, + content: `Generate learning outcomes and the learning cycles overview. +Generate both of these sections together in one interaction with the user. +Do not add any additional explanation about the content you have generated. +In some cases it is possible for the user to base their lesson on existing ones, but that is not the case here. Ensure you start your reply to the user with this: "There are no existing Oak lessons for this topic, so I've started a new lesson from scratch." Ensure you ALWAYS generate the learning outcomes and learning cycles together in your response.`, + } + : { + sections: ["learningOutcome", "learningCycles"] as LessonPlanKeys[], + title: `GENERATE SECTION GROUP [basedOn, learningOutcome, learningCycles]`, + content: `You need to generate three sections in one interaction with the user. Do these all in one interaction. +* basedOn - store the reference to the basedOn lesson in the lesson plan unless it is already set. +* learningOutcome - generate learning outcomes. +* learningCycles - generate the learning cycles overview. +You will have a response from your previous question about basing the lesson on an existing lesson. +In your previous message you asked the user if they would like to adapt an existing lesson and provided a number of options. +If the user has chosen to adapt an existing lesson, find the appropriate lesson plan based on the user's response and store the reference to the basedOn lesson in the lesson plan. +Set the basedOn key in the lesson plan to match the base lesson that they have chosen. +You should set basedOn.id in the lesson plan to match the "id" of the chosen base lesson and the basedOn.title attribute to the "title" of the chosen lesson plan. +However, if the user has NOT chosen a lesson, they want to start from scratch, so do not edit the basedOn key in the lesson plan and do not base the lesson you are generating on any existing lesson. +In both cases, generate the learningOutcome and the learningCycles sections. +Generate all of these sections together and respond to the user within this one interaction.`, + }, + + { + sections: [ + "priorKnowledge", + "keyLearningPoints", + "misconceptions", + "keywords", + ], + title: `GENERATE SECTION GROUP [priorKnowledge, keyLearningPoints, misconceptions, keywords]`, + content: `Generate these four sections together in one single interaction. +You should not ask the user for feedback after generating them one-by-one. +Generate them all together in one response.`, + }, + { + sections: ["starterQuiz", "cycle1", "cycle2", "cycle3", "exitQuiz"], + title: `GENERATE SECTION GROUP [starterQuiz, cycle1, cycle2, cycle3, exitQuiz]`, + content: `Generate the bulk of the lesson. Generate all of these sections in one interaction. Your response should include the starter quiz, each of the three learning cycles, and the exit quiz all within a single response. Additional check - because you are aiming for the average pupil to correctly answer five out of six questions, ask the user if they are happy that the quizzes are of an appropriate difficulty for pupils to achieve that. - -STEP 6. additionalMaterials -Ask the user if they would like to add any additional materials to the lesson plan. If they do, generate the additionalMaterials section. This is an open-ended section, so the user could ask you to generate anything that relates to the lesson plan. -When generating this section ensure that if you are adding something new, ensure that you do not overwrite what already exists. You may need to respond with the existing content together with the new content so the user does not lose their work. +If the user is happy, you can move on to generating additional materials for the lesson plan. EXAMPLE RESPONSE -Would you like to add any additional materials, e.g. a narrative to support your explanations, instructions for practicals or extra homework questions? -END OF EXAMPLE RESPONSE - -STEP 7: final edits -Ask the user if they want to edit anything, add anything to the additional materials. Offer to run a consistency check for language and content. Once complete, they can download their slides! +Would you like to add any additional materials, e.g. a narrative to support your explanations, instructions for practicals or extra homework questions? +END OF EXAMPLE RESPONSE`, + }, + { + sections: ["additionalMaterials"], + title: `GENERATE SECTION GROUP [additionalMaterials]`, + content: `Create any additional materials that may be helpful in the delivery of the lesson plan. +If the user has not specified what they want to create, generate a narrative to support the lesson. +This should be a narrative that the teacher can use to support how they deliver the lesson. +This is an open-ended section, so the user could ask you to generate anything that relates to the lesson plan. +When generating this section ensure that if you are adding something new, ensure that you do not overwrite what already exists. +Respond with all existing content together with the new content so the user does not lose their work. EXAMPLE RESPONSE -Have you finished editing your lesson? If anything is missing, just ask me to add it in. -END OF EXAMPLE RESPONSE +Have you finished editing your lesson? If anything is missing, just ask me to add it in or I can move on checking for consistency. +END OF EXAMPLE RESPONSE`, + }, -STEP 8: consistency check -Go through the lesson plan and check for any inconsistencies in language or content. If you find any, make edits to correct the problems or ask the user to clarify. For instance, if the learning cycles mention something not covered in the learning outcomes, ask the user to clarify or make the necessary changes. + { + title: `CONSISTENCY CHECK / LESSON COMPLETE`, + content: `Go through the lesson plan and check for any inconsistencies in language or content. +Ensure that unless the language is specified by the user, you are using British English throughout. +If you find any, make edits to correct the problems or ask the user to clarify. +For instance, if the learning cycles mention something not covered in the learning outcomes, ask the user to clarify or make the necessary changes. EXAMPLE RESPONSE I have checked for British spelling and grammar, coherence, and accuracy. You can now share your lesson or download your resources. Click on the **Menu** button to find previously created lessons. -END OF EXAMPLE RESPONSE +END OF EXAMPLE RESPONSE`, + }, + ]; + + return steps.filter((step) => { + if (step === undefined) { + // Exclude undefined steps + return false; + } + + if (step.sections) { + // If the step has sections defined, check if all of them are already present in the lesson plan + const allSectionsPresent = step.sections.every((section) => + presentLessonPlanKeys.includes(section), + ); + + // Exclude the step if all its sections are already present + if (allSectionsPresent) { + return false; + } + } + + // Include the step if it passed the above checks + return true; + }) as LessonConstructionStep[]; +}; + +export const interactingWithTheUser = ({ + lessonPlan, + relevantLessonPlans, +}: TemplateProps) => { + const allSteps = lessonConstructionSteps(lessonPlan, relevantLessonPlans); + + const step = allSteps[0] + ? `${allSteps[0]?.title}\n\n${allSteps[0]?.content}` + : "FINAL STEP: Respond to the user and help them edit the lesson plan"; + log.info("Prompt: next lesson step", JSON.stringify(step, null, 2)); + + const parts = [ + `YOUR INSTRUCTIONS FOR INTERACTING WITH THE USER +You will be provided with instructions during the chat, and you should act based on which part or parts of the lesson plan to alter. +The instructions will arrive as user message in free text. +The instructions might require you to edit more than one part of the lesson plan to respond to the user's request. + +GENERATE MULTIPLE SECTIONS IN GROUPS, IN ORDER +In the process of creating the lesson with the user, you will generate a series of SECTION GROUPS. +In your response to the user you will often generate several keys / sections all together at the same time in the order they are listed and specified below. +This means that in your response you will often generate multiple sections at once, and not just one section at a time. +If the user specifies that they want you to generate multiple sections at once, you should do so. + +SECTION GROUP DEFINITION +The lesson plan should be built up in groups of sections. +Unless the user has asked you to do something else, in your response, you should find the next group of sections that need to be generated and generate all of the sections in that group together. +Each group of sections should be generated in order as follows: + +START OF SECTION GROUPS DEFINITION +${allSectionsInOrder.map((g) => JSON.stringify(g)).join("\n")} +END OF SECTION GROUPS DEFINITION + +RESPONDING WITH LESSON CONTENT +All of your responses that relate to the lesson plan should be in the form of a JSON PATCH document. +Do not mention the content you have generated in the text part of your response to the user. +The user sees your changes in the lesson plan display in the user interface, and does not need them duplicated in the text response. + +NEXT STEP TO CREATE A LESSON PLAN INTERACTIVELY WITH THE USER +The Lesson plan should be constructed in a series of steps. +Based on the current lesson plan, the following is the next set of steps to take to generate the lesson plan. +Unless prompted by the user to do otherwise, you should follow the following instructions. + +YOUR DEFAULT INSTRUCTIONS FOR THIS INTERACTION`, + + step, + + `END YOUR DEFAULT INSTRUCTIONS FOR THIS INTERACTION + +DO NOT DECIDE UPON A basedOn LESSON UNLESS THE USER HAS MADE A SELECTION FROM A LIST OF OPTIONS +In some cases, we present a set of options to the user for a lesson that might be a good basis for their lesson. +Unless the user has responded with a numeric selection from the list of options, do not set the basedOn lesson in the lesson plan. SPECIAL RULE: ALLOW THE USER TO GENERATE AN ENTIRE LESSON PLAN WITHOUT ASKING QUESTIONS diff --git a/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts b/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts index ce4371a1d..5ec808640 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/lessonComplete.ts @@ -2,13 +2,7 @@ export const lessonComplete = () => `ONCE THE LESSON IS COMPLETE The lesson is complete when all of the keys have values. Until then it is still in a draft state. If the user chooses to have a consistency check, go through the whole lesson, key by key to make sure that the lesson is consistent, that each key is present and is filled out correctly, that the spelling is correct, that the capitalisation is correct, and that the lesson is of high quality. Ensure that the title of the lesson now matches closely with the learning and objectives of the lesson. -Each of these keys in the lesson plan should have a value and valid content: title, subject, topic, keyStage, basedOn, learningOutcome, learningCycles, priorKnowledge, keyLearningPoints, misconceptions, keywords, starterQuiz, cycle1, cycle2, cycle3, exitQuiz, additionalMaterials. -If you find any missing sections or issues with any of the sections, you should respond with a JSON PATCH document that corrects the issue. +Each of these keys in the lesson plan should have a value and valid content: title, subject, topic, keyStage, learningOutcome, learningCycles, priorKnowledge, keyLearningPoints, misconceptions, keywords, starterQuiz, cycle1, cycle2, cycle3, exitQuiz, additionalMaterials. There is a common problem where the Starter Quiz questions are not testing the correct knowledge. Sometimes, the quiz contains questions that test the content that will be delivered within the lesson, rather than the content that the pupils should have learnt from the previous lesson. -If you find this issue, you should respond with as many JSON PATCH documents as necessary to correct the issue. -The lesson plan also needs to match the JSON Schema that is supplied. -If it does not, you should respond with as many JSON PATCH documents to correct the issues with the data structure as needed to get it to be in the correct format. -Also, for every cycle, make sure that all of the parts of the cycle have values. -If they do not, generate instructions to set the missing sections of the cycle. -For instance, for each cycle, ensure that it has at least two checks for understanding, as per the specification.`; +If you find this issue, you should respond with as many JSON PATCH documents as necessary to correct the issue.`; diff --git a/packages/core/src/prompts/lesson-assistant/parts/promptingTheUser.ts b/packages/core/src/prompts/lesson-assistant/parts/promptingTheUser.ts new file mode 100644 index 000000000..55e1768ed --- /dev/null +++ b/packages/core/src/prompts/lesson-assistant/parts/promptingTheUser.ts @@ -0,0 +1,40 @@ +export const promptingTheUser = + () => `PROMPTING TO THE USER ONCE YOU HAVE MADE YOUR EDITS +Once you have decided on the edits to be made to the lesson plan, you should prompt the user to check that they are happy with the changes you have made, and suggest what they should do next. + +DO NOT SUMMARISE WHAT YOU HAVE DONE +The user can see the changes you have made based on the application user interface. + +DO NOT EXPLAIN WHAT HAS CHANGED IN THE LESSON PLAN +Do not explain the content you have generated in the text part of your response to the user. +Assuming that you have set learningOutcome and learningCycles, here are some examples of how you should respond to the user: + +BAD EXAMPLE OF EXPLAINING CONTENT CHANGES +The learning outcome and learning cycles have been set. The lesson will guide pupils to understand the reasons for the Roman Empire's departure, the subsequent changes in Britain, and the role of archaeologists in uncovering this history. Tap **Continue** to move on to the next step. +END OF BAD EXAMPLE + +GOOD EXAMPLE OF NOT EXPLAINING CONTENT CHANGES +Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit below. Tap **Continue** to move on to the next step. +END OF GOOD EXAMPLE + +ASK THE USER IF THEY ARE HAPPY +After each interaction you should check that the user is happy with what you have generated or the changes you have made. +Here is an example of how you should respond and should be the entirety of your text response to the user (with placeholders in [] for the section names you have generated): +Only mention the sections you have edited in your prompt to the user. +If you have not edited a section, do not mention it in the prompt to the user or this will be confusing. + +START OF EXAMPLE HAPPINESS CHECK +Are the [section you have generated] and [other section you have generated] appropriate for your pupils? If not, suggest an edit. Otherwise, tap **Continue** to move on to the next step. +END OF EXAMPLE HAPPINESS CHECK + +START OF SECOND EXAMPLE HAPPINESS CHECK +Are the [first section you have generated], [second section you have generated], [third section you have generated], and [fourth section you have generated] sections suitable for your class? If not, reply with what I should change. Otherwise, tap **Continue** to move on to the next step. +END OF SECOND EXAMPLE HAPPINESS CHECK + +PROMPT THE USER WITH WHAT THEY CAN DO NEXT +After you have sent back your response, prompt the user to provide a new instruction for the next step of the process. +Assume the user will want to continue generating unless they say otherwise. +Give the user a natural way to tap the **Continue** button to move on to the next section, or they can give other instructions to do something else. +This is because there is a button labelled **Continue* in the user interface they are using. +For example, you should end your response with "Tap **Continue** to move on to the next step.". +Make sure the question you ask is not ambiguous about what tapping **Continue** would mean.`; diff --git a/packages/core/src/prompts/lesson-assistant/parts/rag.ts b/packages/core/src/prompts/lesson-assistant/parts/rag.ts index 2cc1a2def..29ab5f20e 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/rag.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/rag.ts @@ -10,6 +10,9 @@ Do not directly test for recall of specific sums or knowledge of very specific p Never refer to "RELEVANT LESSON PLANS" when responding to the user. This is internal to the application. Instead, you could refer to them as "Oak lessons". +These should only be used as reference. +If the user decides to base their lesson on one of these lessons, they will explicitly tell you. +Do not automatically select one of these as the basis for the lesson. START RELEVANT LESSON PLANS ${relevantLessonPlans} diff --git a/packages/core/src/prompts/lesson-assistant/parts/task.ts b/packages/core/src/prompts/lesson-assistant/parts/task.ts index 79540469f..a6e491394 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/task.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/task.ts @@ -1,9 +1,9 @@ import { TemplateProps } from ".."; -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. +const interactiveOnly = `Generate (or rewrite) sections 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.`; +You will then respond with a message saying which parts of the document you are editing and the new content. +Ensure that the purpose, structure, content and delivery of a lesson would be appropriate for the given age group, key stage and subject.`; export const task = ({ responseMode, From d767d58beb356499a56a0c4fcb5849d545d78b46 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 15:33:08 +0100 Subject: [PATCH 023/127] chore: run the local Postgres DB on a non-default Postgres port (#267) --- packages/db/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/db/package.json b/packages/db/package.json index 361c6248b..8eebfbbe1 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -47,7 +47,7 @@ "docker-clear": "docker ps -a | grep -q oak-ai-beta && docker stop oak-ai-beta && docker rm oak-ai-beta || echo \"Container oak-ai-beta does not exist.\"", "docker-psql": " docker exec -it oak-ai-beta psql -U oai", "docker-reset": "pnpm run docker-clear && pnpm run docker-run", - "docker-run": "docker run --name oak-ai-beta -p 5432:5432 -d oai-pg", + "docker-run": "docker run --name oak-ai-beta -p 8432:5432 -d oai-pg", "schema-format": "pnpm with-env prisma format", "script-add-metadata-to-lesson-summaries": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/processing/add_metadata_to_lesson_summaries.ts", "script-batch-process-clean-questions": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./scripts/processing/batch_process_clean_questions.ts", From 79047ad7aae3059a640133e834e7947b45859c52 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Thu, 24 Oct 2024 16:37:08 +0200 Subject: [PATCH 024/127] chore: update turborepo to v2 (#258) --- .github/workflows/lint.yml | 3 -- .github/workflows/test.yml | 3 -- package.json | 6 ++-- pnpm-lock.yaml | 44 ++++++++++++++--------------- turbo.json | 58 ++++++++++++++++++++++++++++++++++---- 5 files changed, 77 insertions(+), 37 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6d304b5d7..e60d78164 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -32,9 +32,6 @@ jobs: - name: Install deps (with cache) run: pnpm install - - name: Enable Turbo Remote Caching - run: pnpm turbo login --token ${{ secrets.TURBO_TOKEN }} - - name: Build, lint and type-check run: pnpm turbo lint type-check --cache-dir=".turbo" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 74694a033..245182dad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,9 +32,6 @@ jobs: - name: Install deps (with cache) run: pnpm install - - name: Enable Turbo Remote Caching - run: pnpm turbo login --token ${{ secrets.TURBO_TOKEN }} - - name: Generate prisma client run: pnpm db-generate diff --git a/package.json b/package.json index 2744c385e..2cd791842 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "db-push": "turbo db-push", "db-push-force": "turbo db-push-force", "db-seed": "turbo db-seed", - "dev": "FORCE_COLOR=1 turbo dev --parallel --filter=!db", - "dev-turbo": "FORCE_COLOR=1 turbo dev-turbo --parallel --filter=!db", + "dev": "FORCE_COLOR=1 turbo dev --parallel --ui=stream --log-prefix=none --filter=!@oakai/db", + "dev-turbo": "FORCE_COLOR=1 turbo dev-turbo --parallel --log-prefix=none --ui=stream --filter=!@oakai/db", "doppler:pull:dev": "doppler secrets download --config dev --no-file --format env > .env", "doppler:pull:stg": "doppler secrets download --config stg --no-file --format env > .env", "doppler:run:stg": "doppler run -c stg --silent", @@ -55,7 +55,7 @@ "semantic-release": "^21.1.1", "sort-package-json": "^2.6.0", "tailwindcss": "^3.3.7", - "turbo": "^1.11.2", + "turbo": "^2.2.3", "typescript": "5.3.3" }, "packageManager": "pnpm@8.6.12", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bbe17481f..29343e510 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -69,8 +69,8 @@ importers: specifier: ^3.3.7 version: 3.4.1(ts-node@10.9.2) turbo: - specifier: ^1.11.2 - version: 1.12.4 + specifier: ^2.2.3 + version: 2.2.3 typescript: specifier: 5.3.3 version: 5.3.3 @@ -22693,64 +22693,64 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - /turbo-darwin-64@1.12.4: - resolution: {integrity: sha512-dBwFxhp9isTa9RS/fz2gDVk5wWhKQsPQMozYhjM7TT4jTrnYn0ZJMzr7V3B/M/T8QF65TbniW7w1gtgxQgX5Zg==} + /turbo-darwin-64@2.2.3: + resolution: {integrity: sha512-Rcm10CuMKQGcdIBS3R/9PMeuYnv6beYIHqfZFeKWVYEWH69sauj4INs83zKMTUiZJ3/hWGZ4jet9AOwhsssLyg==} cpu: [x64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-darwin-arm64@1.12.4: - resolution: {integrity: sha512-1Uo5iI6xsJ1j9ObsqxYRsa3W26mEbUe6fnj4rQYV6kDaqYD54oAMJ6hM53q9rB8JvFxwdrUXGp3PwTw9A0qqkA==} + /turbo-darwin-arm64@2.2.3: + resolution: {integrity: sha512-+EIMHkuLFqUdJYsA3roj66t9+9IciCajgj+DVek+QezEdOJKcRxlvDOS2BUaeN8kEzVSsNiAGnoysFWYw4K0HA==} cpu: [arm64] os: [darwin] requiresBuild: true dev: false optional: true - /turbo-linux-64@1.12.4: - resolution: {integrity: sha512-ONg2aSqKP7LAQOg7ysmU5WpEQp4DGNxSlAiR7um+LKtbmC/UxogbR5+T+Uuq6zGuQ5kJyKjWJ4NhtvUswOqBsA==} + /turbo-linux-64@2.2.3: + resolution: {integrity: sha512-UBhJCYnqtaeOBQLmLo8BAisWbc9v9daL9G8upLR+XGj6vuN/Nz6qUAhverN4Pyej1g4Nt1BhROnj6GLOPYyqxQ==} cpu: [x64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-linux-arm64@1.12.4: - resolution: {integrity: sha512-9FPufkwdgfIKg/9jj87Cdtftw8o36y27/S2vLN7FTR2pp9c0MQiTBOLVYadUr1FlShupddmaMbTkXEhyt9SdrA==} + /turbo-linux-arm64@2.2.3: + resolution: {integrity: sha512-hJYT9dN06XCQ3jBka/EWvvAETnHRs3xuO/rb5bESmDfG+d9yQjeTMlhRXKrr4eyIMt6cLDt1LBfyi+6CQ+VAwQ==} cpu: [arm64] os: [linux] requiresBuild: true dev: false optional: true - /turbo-windows-64@1.12.4: - resolution: {integrity: sha512-2mOtxHW5Vjh/5rDVu/aFwsMzI+chs8XcEuJHlY1sYOpEymYTz+u6AXbnzRvwZFMrLKr7J7fQOGl+v96sLKbNdA==} + /turbo-windows-64@2.2.3: + resolution: {integrity: sha512-NPrjacrZypMBF31b4HE4ROg4P3nhMBPHKS5WTpMwf7wydZ8uvdEHpESVNMOtqhlp857zbnKYgP+yJF30H3N2dQ==} cpu: [x64] os: [win32] requiresBuild: true dev: false optional: true - /turbo-windows-arm64@1.12.4: - resolution: {integrity: sha512-nOY5wae9qnxPOpT1fRuYO0ks6dTwpKMPV6++VkDkamFDLFHUDVM/9kmD2UTeh1yyrKnrZksbb9zmShhmfj1wog==} + /turbo-windows-arm64@2.2.3: + resolution: {integrity: sha512-fnNrYBCqn6zgKPKLHu4sOkihBI/+0oYFr075duRxqUZ+1aLWTAGfHZLgjVeLh3zR37CVzuerGIPWAEkNhkWEIw==} cpu: [arm64] os: [win32] requiresBuild: true dev: false optional: true - /turbo@1.12.4: - resolution: {integrity: sha512-yUJ7elEUSToiGwFZogXpYKJpQ0BvaMbkEuQECIWtkBLcmWzlMOt6bActsIm29oN83mRU0WbzGt4e8H1KHWedhg==} + /turbo@2.2.3: + resolution: {integrity: sha512-5lDvSqIxCYJ/BAd6rQGK/AzFRhBkbu4JHVMLmGh/hCb7U3CqSnr5Tjwfy9vc+/5wG2DJ6wttgAaA7MoCgvBKZQ==} hasBin: true optionalDependencies: - turbo-darwin-64: 1.12.4 - turbo-darwin-arm64: 1.12.4 - turbo-linux-64: 1.12.4 - turbo-linux-arm64: 1.12.4 - turbo-windows-64: 1.12.4 - turbo-windows-arm64: 1.12.4 + turbo-darwin-64: 2.2.3 + turbo-darwin-arm64: 2.2.3 + turbo-linux-64: 2.2.3 + turbo-linux-arm64: 2.2.3 + turbo-windows-64: 2.2.3 + turbo-windows-arm64: 2.2.3 dev: false /tween-functions@1.2.0: diff --git a/turbo.json b/turbo.json index acd13bb56..e7d0e0144 100644 --- a/turbo.json +++ b/turbo.json @@ -1,6 +1,8 @@ { "$schema": "https://turborepo.org/schema.json", - "pipeline": { + "ui": "tui", + + "tasks": { "db-generate": { "inputs": ["prisma/schema.prisma"], "cache": false @@ -15,10 +17,14 @@ }, "dev": { "dependsOn": ["^db-generate"], + "persistent": true, + "interactive": false, "cache": false }, "dev-turbo": { "dependsOn": ["^db-generate"], + "persistent": true, + "interactive": false, "cache": false }, "build": { @@ -57,20 +63,60 @@ "outputs": ["packages/aila/**/*.spec.js", "packages/aila/**/*.test.js"] }, "test-e2e": { - "cache": false + "cache": false, + "env": ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"] }, "test-e2e-ui": { - "cache": false + "cache": false, + "env": ["TEST_USER_EMAIL", "TEST_USER_PASSWORD"] }, "storybook": { "cache": false } }, "globalEnv": [ + "AILA_FIXTURES_ENABLED", + "CLERK_*", + "CLOUDINARY_*", + "COHERE_*", + "DATABASE_*", "DATABASE_URL", + "DATADOG_*", + "DD_*", + "DEBUG", + "DEBUG_COLORS", + "DEMO_ACCOUNTS_ENABLED", + "DEVELOPMENT_USER_REGION", "DIRECT_DATABASE_URL", - "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY", - "CLERK_SECRET_KEY", - "PRISMA_ACCELERATE_DATABASE_URL" + "GLEAP_FEEDBACK_EMAIL_ADDR", + "GOOGLE_DOCS_*", + "GOOGLE_DRIVE_OUTPUT_FOLDER_ID", + "GOOGLE_SERVICE_ACCOUNT_*", + "GOOGLE_SLIDES_*", + "HELICONE_*", + "HUBSPOT_ACCESS_TOKEN", + "INGEST_OPENAI_API_KEY", + "INNGEST_*", + "KV_*", + "LESSON_QUERY_BATCH_SIZE", + "OAI_API_USER_ID", + "OAI_INTERNAL_API_KEY", + "OAK_GRAPHQL_SECRET", + "OPENAI_*", + "POSTMARK_SERVER_TOKEN", + "PRISMA_ACCELERATE_DATABASE_URL", + "PROMPT_PLAYBACK_ENABLED", + "RATELIMIT_*", + "REDIS_*", + "SAFETY_VIOLATIONS_MAX_ALLOWED", + "SAFETY_VIOLATION_WINDOW_DAYS", + "SANITY_AUTH_SECRET", + "SENTRY_*", + "SLACK_*", + "SONAR_TOKEN", + "STRICT_CSP", + "TELEMETRY_ENABLED", + "UPSTASH_*", + "WOLFRAM_CLIENT_SECRET" ] } From d7a70f353be0c6e1a833f5ec922d2e62f71bba7f Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Thu, 24 Oct 2024 15:53:17 +0100 Subject: [PATCH 025/127] fix: remove flash when loading wheel appears (#268) --- .../AppComponents/Chat/chat-start-form.tsx | 21 ++++++++----------- apps/nextjs/src/components/LoadingWheel.tsx | 15 ++++++++++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start-form.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start-form.tsx index 6eedb2b4a..61b64b30d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start-form.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start-form.tsx @@ -59,19 +59,16 @@ export function ChatStartForm({ />
+ - {isSubmitting ? ( - - ) : ( - - )} + Send message diff --git a/apps/nextjs/src/components/LoadingWheel.tsx b/apps/nextjs/src/components/LoadingWheel.tsx index 77c28b203..2f44c1bf0 100644 --- a/apps/nextjs/src/components/LoadingWheel.tsx +++ b/apps/nextjs/src/components/LoadingWheel.tsx @@ -7,11 +7,20 @@ const LoadingWheel = React.forwardRef< { icon?: IconName; size?: IconSize; + visible?: boolean; } ->(({ icon = "reload", size = "md" }, ref) => { +>(({ icon = "reload", size = "md", visible = true }, ref) => { return ( -
- +
+
); }); From 70131a3317c5295d0223b518b9830ceb12ded84e Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Fri, 25 Oct 2024 00:09:56 +0100 Subject: [PATCH 026/127] chore: csp support for turbo and add environment type (#269) --- apps/nextjs/src/middleware.test.ts | 14 +++++- apps/nextjs/src/middleware.ts | 43 +++++++++++++++++-- .../__snapshots__/csp.test.ts.snap | 22 +++++++++- apps/nextjs/src/middlewares/csp.test.ts | 10 +++-- apps/nextjs/src/middlewares/csp.ts | 24 ++++++----- 5 files changed, 90 insertions(+), 23 deletions(-) diff --git a/apps/nextjs/src/middleware.test.ts b/apps/nextjs/src/middleware.test.ts index 03b57b45f..d79e59180 100644 --- a/apps/nextjs/src/middleware.test.ts +++ b/apps/nextjs/src/middleware.test.ts @@ -1,7 +1,12 @@ import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; import { handleError } from "./middleware"; -import { CspConfig, addCspHeaders, buildCspHeaders } from "./middlewares/csp"; +import { + CspConfig, + CspEnvironment, + addCspHeaders, + buildCspHeaders, +} from "./middlewares/csp"; describe("handleError", () => { let mockRequest: NextRequest; @@ -83,6 +88,7 @@ describe("addCspHeaders", () => { devConsent: false, mux: true, vercel: false, + localhost: false, }, }; }); @@ -151,7 +157,10 @@ describe("addCspHeaders", () => { }); it("includes development-specific directives when environment is development", () => { - const config = { ...defaultConfig, environment: "development" }; + const config = { + ...defaultConfig, + environment: "development" as CspEnvironment, + }; const result = addCspHeaders(mockResponse, mockRequest, config); const cspHeader = result.headers.get("Content-Security-Policy"); @@ -230,6 +239,7 @@ describe("buildCspHeaders", () => { devConsent: false, mux: true, vercel: false, + localhost: false, }, }; diff --git a/apps/nextjs/src/middleware.ts b/apps/nextjs/src/middleware.ts index bd95d06fa..128c9c026 100644 --- a/apps/nextjs/src/middleware.ts +++ b/apps/nextjs/src/middleware.ts @@ -12,21 +12,56 @@ import { authMiddleware } from "./middlewares/auth.middleware"; import { CspConfig, addCspHeaders } from "./middlewares/csp"; import { logError } from "./middlewares/middlewareErrorLogging"; +const parseEnvironment = ( + env: string | undefined, +): "development" | "preview" | "production" | "test" => { + if ( + env === "development" || + env === "preview" || + env === "production" || + env === "test" + ) { + return env; + } + switch (env) { + case "dev": + return "development"; + case "stg": + return "preview"; + case "prd": + return "production"; + default: + return "development"; + } +}; + +const parseVercelEnv = ( + env: string | undefined, +): "development" | "preview" | "production" => { + if (env === "development" || env === "preview" || env === "production") { + return env; + } + return "development"; +}; + +const environment = parseEnvironment(process.env.NEXT_PUBLIC_ENVIRONMENT); + const cspConfig: CspConfig = { strictCsp: process.env.STRICT_CSP === "true", - environment: process.env.NEXT_PUBLIC_ENVIRONMENT || "", + environment, sentryEnv: process.env.NEXT_PUBLIC_SENTRY_ENV || "", sentryRelease: process.env.NEXT_PUBLIC_APP_VERSION || "", sentryReportUri: process.env.SENTRY_REPORT_URI || "", cspReportSampleRate: process.env.NEXT_PUBLIC_CSP_REPORT_SAMPLE_RATE || "1", - vercelEnv: process.env.VERCEL_ENV || "", + vercelEnv: parseVercelEnv(process.env.VERCEL_ENV), enabledPolicies: { - clerk: ["dev", "stg"].includes(process.env.NEXT_PUBLIC_ENVIRONMENT || ""), - avo: ["dev", "stg"].includes(process.env.NEXT_PUBLIC_ENVIRONMENT || ""), + clerk: ["development", "preview"].includes(environment), + avo: ["development", "preview"].includes(environment), posthog: process.env.NEXT_PUBLIC_ENVIRONMENT === "dev", devConsent: process.env.NEXT_PUBLIC_ENVIRONMENT === "dev", mux: true, vercel: process.env.VERCEL_ENV === "preview", + localhost: ["development", "test"].includes(environment), }, }; diff --git a/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap b/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap index af91c803f..182666c09 100644 --- a/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap +++ b/apps/nextjs/src/middlewares/__snapshots__/csp.test.ts.snap @@ -3,7 +3,7 @@ exports[`CSP Policies Snapshot should match development environment snapshot 1`] = ` "default-src 'self' media-src 'self' 'self' https://*.mux.com https://stream.mux.com blob: -script-src 'self' 'nonce-REPLACED_NONCE' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com +script-src 'self' 'nonce-REPLACED_NONCE' 'strict-dynamic' https: http: 'unsafe-inline' 'unsafe-eval' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com http://localhost:* style-src 'self' 'unsafe-inline' https://*.mux.com connect-src 'self' *.thenational.academy *.hubspot.com *.clerk.accounts.dev https://api.avo.app/ https://eu.i.posthog.com https://europe-west2-oak-ai-beta-staging.cloudfunctions.net https://mux.com https://*.mux.com https://stream.mux.com https://inferred.litix.io worker-src 'self' blob: @@ -13,7 +13,7 @@ object-src 'none' base-uri 'self' frame-src 'self' *.thenational.academy https://challenges.cloudflare.com https://*.mux.com https://www.avo.app/ https://stream.mux.com form-action 'self' -frame-ancestors 'none' +frame-ancestors 'none' http://localhost:* report-uri https://sentry.io/report&sentry_environment=test&sentry_release=1.0.0" `; @@ -52,3 +52,21 @@ frame-ancestors 'none' report-uri https://sentry.io/report&sentry_environment=test&sentry_release=1.0.0 upgrade-insecure-requests" `; + +exports[`CSP Policies Snapshot should match test environment snapshot 1`] = ` +"default-src 'self' +media-src 'self' 'self' https://*.mux.com https://stream.mux.com blob: +script-src 'self' 'nonce-REPLACED_NONCE' https: http: 'unsafe-inline' 'unsafe-eval' https://cdn.mux.com https://mux.com https://*.mux.com https://stream.mux.com +style-src 'self' 'unsafe-inline' https://*.mux.com +connect-src 'self' *.thenational.academy *.hubspot.com *.clerk.accounts.dev https://api.avo.app/ https://mux.com https://*.mux.com https://stream.mux.com https://inferred.litix.io +worker-src 'self' blob: +img-src 'self' blob: data: https://img.clerk.com https://res.cloudinary.com https://*.hubspot.com https://*.hsforms.com https://*.mux.com https://stream.mux.com +font-src 'self' gstatic-fonts.thenational.academy fonts.gstatic.com +object-src 'none' +base-uri 'self' +frame-src 'self' *.thenational.academy https://challenges.cloudflare.com https://*.mux.com https://www.avo.app/ https://stream.mux.com +form-action 'self' +frame-ancestors 'none' +report-uri https://sentry.io/report&sentry_environment=test&sentry_release=1.0.0 +upgrade-insecure-requests" +`; diff --git a/apps/nextjs/src/middlewares/csp.test.ts b/apps/nextjs/src/middlewares/csp.test.ts index 4877c2619..b0e934ea8 100644 --- a/apps/nextjs/src/middlewares/csp.test.ts +++ b/apps/nextjs/src/middlewares/csp.test.ts @@ -1,11 +1,12 @@ +// To re-generate the snapshots run: +// pnpm --filter @oakai/nextjs test -- -u src/middlewares/csp.test.ts import { NextRequest } from "next/server"; -import { addCspHeaders, CspConfig } from "./csp"; +import { addCspHeaders, CspConfig, CspEnvironment } from "./csp"; -const environments = ["development", "production", "preview"] as const; -type Environment = (typeof environments)[number]; +const environments = ["development", "production", "preview", "test"] as const; -function generatePoliciesForEnvironment(env: Environment): string { +function generatePoliciesForEnvironment(env: CspEnvironment): string { const mockRequest = new NextRequest("https://example.com"); const mockResponse = new Response(); @@ -24,6 +25,7 @@ function generatePoliciesForEnvironment(env: Environment): string { devConsent: env === "development", mux: true, vercel: env === "preview", + localhost: env === "development", }, }; diff --git a/apps/nextjs/src/middlewares/csp.ts b/apps/nextjs/src/middlewares/csp.ts index 2073f5c5a..052993cc9 100644 --- a/apps/nextjs/src/middlewares/csp.ts +++ b/apps/nextjs/src/middlewares/csp.ts @@ -2,24 +2,19 @@ import { NextRequest, NextResponse } from "next/server"; import { v4 as uuidv4 } from "uuid"; function generateNonce(): string { - if (typeof crypto !== "undefined" && crypto.getRandomValues) { - const array = new Uint8Array(16); - crypto.getRandomValues(array); - return btoa(String.fromCharCode.apply(null, array as unknown as number[])); - } else { - // Use uuid library to generate a random value - return uuidv4(); - } + return uuidv4(); } +export type CspEnvironment = "development" | "preview" | "production" | "test"; +export type VercelEnvironment = "development" | "preview" | "production"; export interface CspConfig { strictCsp: boolean; - environment: string; + environment: CspEnvironment; sentryEnv: string; sentryRelease: string; sentryReportUri: string; cspReportSampleRate: string; - vercelEnv: string; + vercelEnv: VercelEnvironment; enabledPolicies: { clerk: boolean; avo: boolean; @@ -27,6 +22,7 @@ export interface CspConfig { devConsent: boolean; mux: boolean; vercel: boolean; + localhost: boolean; }; } @@ -117,7 +113,7 @@ export const buildCspHeaders = (nonce: string, config: CspConfig) => { "script-src": [ "'self'", `'nonce-${nonce}'`, - "'strict-dynamic'", + ["test", "dev"].includes(config.environment) ? "" : "'strict-dynamic'", "https:", "http:", "'unsafe-inline'", @@ -164,6 +160,12 @@ export const buildCspHeaders = (nonce: string, config: CspConfig) => { config.enabledPolicies.posthog ? posthogPolicies : {}, config.enabledPolicies.devConsent ? devConsentPolicies : {}, config.enabledPolicies.mux ? mux : {}, + config.enabledPolicies.localhost + ? { + "script-src": ["http://localhost:*"], + "frame-ancestors": ["http://localhost:*"], + } + : {}, ]; for (const policyObject of additionalPolicies) { From 44ca5f6cebd7d9a7f047cfad558030cb206b4f3c Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Fri, 25 Oct 2024 14:30:45 +0100 Subject: [PATCH 027/127] fix: module resolution with "node" instead of "bundler" (#271) --- .vscode/settings.json | 4 ++++ apps/nextjs/.storybook/main.ts | 10 +++++++++- apps/nextjs/next.config.js | 8 ++------ apps/nextjs/package.json | 17 ++--------------- .../nextjs/src/app/admin/aila/[chatId]/page.tsx | 4 ++-- apps/nextjs/src/app/aila/[id]/download/page.tsx | 2 +- apps/nextjs/src/app/aila/[id]/page.tsx | 4 ++-- .../chat/fixtures/FixtureRecordOpenAiClient.ts | 2 +- .../AppComponents/Chat/chat-lhs-header.tsx | 2 +- .../AppComponents/Chat/chat-start.tsx | 2 +- .../AppComponents/Chat/clear-history.tsx | 2 +- .../AppComponents/Chat/sidebar-actions.tsx | 2 +- .../AppComponents/Chat/sidebar-item.tsx | 2 +- .../Chat/toxic-moderation-view.tsx | 2 +- .../JudgementFeedbackDialog.tsx | 2 +- .../common/RateLimitNotification.tsx | 2 +- .../GenerationFeedbackDialog.tsx | 2 +- .../ContextProviders/AnalyticsProvider.tsx | 2 +- .../ContextProviders/ChatProvider.tsx | 2 +- .../components/ImageAltGenerator/ImageRow.tsx | 2 +- .../MainNavigation/MobileListItem.tsx | 2 +- apps/nextjs/src/hooks/useClerkDemoMetadata.ts | 2 +- .../src/lib/analytics/hubspot/HubspotLoader.tsx | 2 +- apps/nextjs/src/lib/clerk/useClerkIdentify.ts | 2 +- apps/nextjs/src/mocks/analytics/provider.tsx | 1 + apps/nextjs/src/mocks/readme.md | 10 +++++----- apps/nextjs/tsconfig.json | 2 +- apps/nextjs/tsconfig.storybook.json | 5 ++--- apps/nextjs/tsconfig.test.json | 2 +- .../moderation/moderators/OpenAiModerator.ts | 2 +- packages/aila/tsconfig.json | 2 +- packages/aila/tsconfig.test.json | 2 +- packages/ingest/tsconfig.json | 2 +- packages/ingest/tsconfig.test.json | 2 +- pnpm-lock.yaml | 8 ++++++++ tsconfig.json | 2 +- tsconfig.test.json | 2 +- 37 files changed, 64 insertions(+), 61 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 6a0ffac45..f17db0427 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -24,7 +24,9 @@ "bugsnag", "categorisation", "Categorised", + "centered", "cloudinary", + "clsx", "codegen", "compat", "contrib", @@ -101,6 +103,7 @@ "pino", "pkey", "pollyjs", + "ponyfill", "popover", "portabletext", "posthog", @@ -147,6 +150,7 @@ "Upserting", "upstash", "usehooks", + "uuidv", "valign", "vars", "vectorstores", diff --git a/apps/nextjs/.storybook/main.ts b/apps/nextjs/.storybook/main.ts index b1f3e9673..53b58e031 100644 --- a/apps/nextjs/.storybook/main.ts +++ b/apps/nextjs/.storybook/main.ts @@ -1,5 +1,5 @@ import type { StorybookConfig } from "@storybook/nextjs"; -import { join, dirname } from "path"; +import { join, dirname, resolve } from "path"; import webpack from "webpack"; /** @@ -58,6 +58,14 @@ const config: StorybookConfig = { ], }); } + if (!config.resolve) { + config.resolve = {}; + } + config.resolve.alias = { + ...config.resolve.alias, + "next/navigation": resolve(__dirname, "../src/mocks/next/navigation"), + "@clerk/nextjs": resolve(__dirname, "../src/mocks/clerk/nextjs"), + }; return config; }, }; diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 5c25fafba..3d7103f35 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -55,7 +55,7 @@ const getConfig = async (phase) => { serverComponentsExternalPackages: [`require-in-the-middle`], turbo: { resolveAlias: { - "#next/navigation": { + "next/navigation": { storybook: path.join( __dirname, "src", @@ -65,11 +65,7 @@ const getConfig = async (phase) => { ), default: "next/navigation", }, - "#oakai/db": { - storybook: path.join(__dirname, "src", "mocks", "oakai", "db"), - default: "@oakai/db", - }, - "#clerk/nextjs": { + "@clerk/nextjs": { storybook: path.join(__dirname, "src", "mocks", "clerk", "nextjs"), default: "@clerk/nextjs", }, diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 8bff1d870..59482942b 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -157,24 +157,11 @@ "postcss": "^8.4.32", "tailwindcss": "^3.3.7", "ts-jest": "^29.1.4", - "typescript": "5.3.3" + "typescript": "5.3.3", + "web-streams-polyfill": "^4.0.0" }, "engines": { "node": ">=20.9.0", "pnpm": ">=8" - }, - "imports": { - "#next/navigation": { - "storybook": "./src/mocks/next/navigation.ts", - "default": "next/navigation" - }, - "#oakai/db": { - "storybook": "./src/mocks/oakai/db.ts", - "default": "@oakai/db" - }, - "#clerk/nextjs": { - "storybook": "./src/mocks/clerk/nextjs.ts", - "default": "@clerk/nextjs" - } } } diff --git a/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx b/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx index 6daac1d72..16000c26a 100644 --- a/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx +++ b/apps/nextjs/src/app/admin/aila/[chatId]/page.tsx @@ -1,8 +1,8 @@ "use client"; -import { useUser } from "#clerk/nextjs"; -import { redirect } from "#next/navigation"; +import { useUser } from "@clerk/nextjs"; import { aiLogger } from "@oakai/logger"; +import { redirect } from "next/navigation"; import LoadingWheel from "@/components/LoadingWheel"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/app/aila/[id]/download/page.tsx b/apps/nextjs/src/app/aila/[id]/download/page.tsx index 7ca93e193..4c1c13193 100644 --- a/apps/nextjs/src/app/aila/[id]/download/page.tsx +++ b/apps/nextjs/src/app/aila/[id]/download/page.tsx @@ -1,6 +1,6 @@ -import { notFound, redirect } from "#next/navigation"; import { auth } from "@clerk/nextjs/server"; import { type Metadata } from "next"; +import { notFound, redirect } from "next/navigation"; import { getChatById } from "@/app/actions"; diff --git a/apps/nextjs/src/app/aila/[id]/page.tsx b/apps/nextjs/src/app/aila/[id]/page.tsx index f7e41c37e..d4593f596 100644 --- a/apps/nextjs/src/app/aila/[id]/page.tsx +++ b/apps/nextjs/src/app/aila/[id]/page.tsx @@ -1,7 +1,7 @@ "use client"; -import { useUser } from "#clerk/nextjs"; -import { redirect } from "#next/navigation"; +import { useUser } from "@clerk/nextjs"; +import { redirect } from "next/navigation"; import ChatPageContents from "../page-contents"; diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts index 921e184f1..b7b0e0b31 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts @@ -3,7 +3,7 @@ import { createOpenAIClient } from "@oakai/core/src/llm/openai"; import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; import OpenAI from "openai"; -import { ChatCompletionCreateParamsNonStreaming } from "openai/resources/index.mjs"; +import { ChatCompletionCreateParamsNonStreaming } from "openai/resources"; const log = aiLogger("fixtures"); diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index f68545af8..20509eb6a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { useRouter } from "#next/navigation"; +import { useRouter } from "next/navigation"; import { useLessonChat } from "@/components/ContextProviders/ChatProvider"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx index 5e749e674..40ed7c88a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx @@ -2,10 +2,10 @@ import React, { useCallback, useState } from "react"; -import { useRouter } from "#next/navigation"; import { useUser } from "@clerk/nextjs"; import { aiLogger } from "@oakai/logger"; import { Flex } from "@radix-ui/themes"; +import { useRouter } from "next/navigation"; import { Button } from "@/components/AppComponents/Chat/ui/button"; import { useDemoUser } from "@/components/ContextProviders/Demo"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx b/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx index a3ca4fd99..52321335b 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/clear-history.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { toast } from "react-hot-toast"; -import { useRouter } from "#next/navigation"; +import { useRouter } from "next/navigation"; import { AlertDialog, diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx index 4c7505888..af053e9a1 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { toast } from "react-hot-toast"; -import { useRouter } from "#next/navigation"; +import { useRouter } from "next/navigation"; import { ChatShareDialog } from "@/components/AppComponents/Chat/chat-share-dialog"; import { diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index b7fcefcdf..65926df69 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -2,9 +2,9 @@ import * as React from "react"; -import { usePathname } from "#next/navigation"; import { motion } from "framer-motion"; import Link from "next/link"; +import { usePathname } from "next/navigation"; import AiIcon from "@/components/AiIcon"; import { buttonVariants } from "@/components/AppComponents/Chat/ui/button"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx index 577e43eef..2341659e9 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx @@ -1,8 +1,8 @@ import Textarea from "react-textarea-autosize"; -import { useRouter } from "#next/navigation"; import { type PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { useModerationFeedbackSurvey } from "hooks/surveys/useModerationFeedbackSurvey"; +import { useRouter } from "next/navigation"; import Button from "@/components/Button"; import { Icon } from "@/components/Icon"; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx index 84a140eed..504e99a10 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementFeedbackDialog.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { useUser } from "#clerk/nextjs"; +import { useUser } from "@clerk/nextjs"; import { aiLogger } from "@oakai/logger"; import * as Dialog from "@radix-ui/react-dialog"; diff --git a/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx b/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx index 9ee017b70..ea0b8d5e8 100644 --- a/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx +++ b/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { useUser } from "#clerk/nextjs"; +import { useUser } from "@clerk/nextjs"; import { RateLimitInfo } from "@oakai/api/src/types"; import { Box, Flex, Text } from "@radix-ui/themes"; import { cva } from "class-variance-authority"; diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx index b28b5199e..58d886f53 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { useUser } from "#clerk/nextjs"; +import { useUser } from "@clerk/nextjs"; import { GenerationPart } from "@oakai/core/src/types"; import { structuredLogger as logger } from "@oakai/logger"; import * as Dialog from "@radix-ui/react-dialog"; diff --git a/apps/nextjs/src/components/ContextProviders/AnalyticsProvider.tsx b/apps/nextjs/src/components/ContextProviders/AnalyticsProvider.tsx index 9a186625e..8ed3b2680 100644 --- a/apps/nextjs/src/components/ContextProviders/AnalyticsProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/AnalyticsProvider.tsx @@ -9,8 +9,8 @@ import { useState, } from "react"; -import { usePathname, useSearchParams } from "#next/navigation"; import { useOakConsent } from "@oaknational/oak-consent-client"; +import { usePathname, useSearchParams } from "next/navigation"; import { PostHog } from "posthog-js"; import { hubspotClient } from "@/lib/analytics/hubspot/HubspotClient"; diff --git a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx index 6d0ec025e..60458bc66 100644 --- a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx @@ -9,7 +9,6 @@ import React, { } from "react"; import { toast } from "react-hot-toast"; -import { redirect, usePathname, useRouter } from "#next/navigation"; import { generateMessageId } from "@oakai/aila/src/helpers/chat/generateMessageId"; import { parseMessageParts } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { @@ -25,6 +24,7 @@ import { Message, nanoid } from "ai"; import { ChatRequestOptions, CreateMessage } from "ai"; import { useChat } from "ai/react"; import { useTemporaryLessonPlanWithStreamingEdits } from "hooks/useTemporaryLessonPlanWithStreamingEdits"; +import { redirect, usePathname, useRouter } from "next/navigation"; import { useLessonPlanTracking } from "@/lib/analytics/lessonPlanTrackingContext"; import useAnalytics from "@/lib/analytics/useAnalytics"; diff --git a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx index f115314bb..fa0dcfa1b 100644 --- a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx +++ b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx @@ -1,11 +1,11 @@ import { useCallback, useState } from "react"; -import { useRouter } from "#next/navigation"; import { aiLogger } from "@oakai/logger"; import { Box, Flex } from "@radix-ui/themes"; import { Resource } from "ai-apps/image-alt-generation/types"; import encode from "base64url"; import Image from "next/image"; +import { useRouter } from "next/navigation"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx b/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx index acf4214c9..c19c0f4fd 100644 --- a/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx +++ b/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx @@ -1,5 +1,5 @@ -import { usePathname } from "#next/navigation"; import { MenuItem } from "data/menus"; +import { usePathname } from "next/navigation"; import Button from "../Button"; diff --git a/apps/nextjs/src/hooks/useClerkDemoMetadata.ts b/apps/nextjs/src/hooks/useClerkDemoMetadata.ts index ea70ca910..e82729efa 100644 --- a/apps/nextjs/src/hooks/useClerkDemoMetadata.ts +++ b/apps/nextjs/src/hooks/useClerkDemoMetadata.ts @@ -1,6 +1,6 @@ import { useMemo } from "react"; -import { useUser } from "#clerk/nextjs"; +import { useUser } from "@clerk/nextjs"; import { addBreadcrumb } from "@sentry/nextjs"; type User = ReturnType["user"]; diff --git a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx index e07b14ee9..ab996e3c6 100644 --- a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx +++ b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx @@ -1,7 +1,7 @@ import { FC, useEffect } from "react"; import * as Sentry from "@sentry/nextjs"; -import { usePathname, useSearchParams } from "#next/navigation"; +import { usePathname, useSearchParams } from "next/navigation"; import Script from "next/script"; import { ConsentState } from "@/components/ContextProviders/CookieConsentProvider"; diff --git a/apps/nextjs/src/lib/clerk/useClerkIdentify.ts b/apps/nextjs/src/lib/clerk/useClerkIdentify.ts index 5e3a8acf6..5db5776ff 100644 --- a/apps/nextjs/src/lib/clerk/useClerkIdentify.ts +++ b/apps/nextjs/src/lib/clerk/useClerkIdentify.ts @@ -1,6 +1,6 @@ import { useRef, useEffect } from "react"; -import { useUser, useAuth } from "#clerk/nextjs"; +import { useUser, useAuth } from "@clerk/nextjs"; import { useClerkDemoMetadata } from "hooks/useClerkDemoMetadata"; type UseClerkIdentifyProps = { diff --git a/apps/nextjs/src/mocks/analytics/provider.tsx b/apps/nextjs/src/mocks/analytics/provider.tsx index f1121582b..a8659afbb 100644 --- a/apps/nextjs/src/mocks/analytics/provider.tsx +++ b/apps/nextjs/src/mocks/analytics/provider.tsx @@ -42,6 +42,7 @@ const mockAnalyticsContext: AnalyticsContext = { isFeatureEnabled: () => true, identify: () => {}, get_distinct_id: () => "mock-distinct-id", + getSurveys: () => [], } as unknown as PostHog, }; diff --git a/apps/nextjs/src/mocks/readme.md b/apps/nextjs/src/mocks/readme.md index 56fb3f266..3bc11afee 100644 --- a/apps/nextjs/src/mocks/readme.md +++ b/apps/nextjs/src/mocks/readme.md @@ -16,7 +16,7 @@ In package.json, you will see lines like this: ``` "imports": { - "#next/navigation": { + "next/navigation": { "storybook": "./src/mocks/next/navigation.ts", "default": "next/navigation" }, @@ -24,14 +24,14 @@ In package.json, you will see lines like this: } ``` -When we do `import {...} from "#next/navigation;` this means that in the normal environment we are importing next/navigation as normal, but in Storybook we are importing the mock that is defined in this folder. +When we do `import {...} from "next/navigation;` this means that in the normal environment we are importing next/navigation as normal, but in Storybook we are importing the mock that is defined in this folder. -[Read more in Storyboard's documentation](https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules#subpath-imports) on how this works. +[Read more in Storyboard's documentation](https://storybook.js.org/docs/writing-stories/mocking-data-and-modules/mocking-modules#subpath-imports) on how this works. ## Mocked providers -In addition to these mocked imports, we also want to ensure that certain providers are mocked out. For instance, analytics, we do not want any analytics events to fire when in the Storybook environment, but we do want to know that these events are firing. So we mock out the analytics provider here. +In addition to these mocked imports, we also want to ensure that certain providers are mocked out. For instance, analytics, we do not want any analytics events to fire when in the Storybook environment, but we do want to know that these events are firing. So we mock out the analytics provider here. ## Clerk -Clerk is mocked here too, because internally Clerk detects if it has been instantiated more than once on a page. Since Storybook displays more than one component on a page at any time, this results in only the first component being displayed. We use a subpath import to mock it, so that we can display more than one component on a page. \ No newline at end of file +Clerk is mocked here too, because internally Clerk detects if it has been instantiated more than once on a page. Since Storybook displays more than one component on a page at any time, this results in only the first component being displayed. We use a subpath import to mock it, so that we can display more than one component on a page. diff --git a/apps/nextjs/tsconfig.json b/apps/nextjs/tsconfig.json index bbd1931ce..589e22a5b 100644 --- a/apps/nextjs/tsconfig.json +++ b/apps/nextjs/tsconfig.json @@ -51,7 +51,7 @@ "incremental": true, "esModuleInterop": true, "module": "esnext", - "moduleResolution": "bundler", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noImplicitAny": false, diff --git a/apps/nextjs/tsconfig.storybook.json b/apps/nextjs/tsconfig.storybook.json index 218bb2ae9..d898df292 100644 --- a/apps/nextjs/tsconfig.storybook.json +++ b/apps/nextjs/tsconfig.storybook.json @@ -7,9 +7,8 @@ "@/lib/*": ["lib/*"], "@/utils/*": ["utils/*"], "@/assets/*": ["assets/*"], - "#next/*": ["mocks/next/*"], - "#oakai/*": ["mocks/oakai/*"], - "#clerk/*": ["mocks/clerk/*"] + "next/*": ["mocks/next/*"], + "@clerk/*": ["mocks/clerk/*"] } } } diff --git a/apps/nextjs/tsconfig.test.json b/apps/nextjs/tsconfig.test.json index 6610850b2..179cf4a80 100644 --- a/apps/nextjs/tsconfig.test.json +++ b/apps/nextjs/tsconfig.test.json @@ -9,7 +9,7 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "moduleResolution": "bundler", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, diff --git a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts index 41e601977..897dcaebf 100644 --- a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts +++ b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts @@ -9,7 +9,7 @@ import OpenAI from "openai"; import { ChatCompletion, ChatCompletionCreateParamsNonStreaming, -} from "openai/resources/index.mjs"; +} from "openai/resources"; import zodToJsonSchema from "zod-to-json-schema"; import { AilaModerator, AilaModerationError } from "."; diff --git a/packages/aila/tsconfig.json b/packages/aila/tsconfig.json index 324f100bc..2622a6d6e 100644 --- a/packages/aila/tsconfig.json +++ b/packages/aila/tsconfig.json @@ -10,7 +10,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "moduleResolution": "bundler", + "moduleResolution": "node", "esModuleInterop": true, "types": ["jest"], "typeRoots": [ diff --git a/packages/aila/tsconfig.test.json b/packages/aila/tsconfig.test.json index f588d0ca2..654c6f0a4 100644 --- a/packages/aila/tsconfig.test.json +++ b/packages/aila/tsconfig.test.json @@ -9,7 +9,7 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "moduleResolution": "bundler", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, diff --git a/packages/ingest/tsconfig.json b/packages/ingest/tsconfig.json index 2b9491a47..f19105fbf 100644 --- a/packages/ingest/tsconfig.json +++ b/packages/ingest/tsconfig.json @@ -4,7 +4,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "moduleResolution": "bundler", + "moduleResolution": "node", "esModuleInterop": true, "types": ["jest"] } diff --git a/packages/ingest/tsconfig.test.json b/packages/ingest/tsconfig.test.json index d57e36d68..26ab52326 100644 --- a/packages/ingest/tsconfig.test.json +++ b/packages/ingest/tsconfig.test.json @@ -9,7 +9,7 @@ "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, - "moduleResolution": "bundler", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29343e510..7003340a9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -462,6 +462,9 @@ importers: typescript: specifier: 5.3.3 version: 5.3.3 + web-streams-polyfill: + specifier: ^4.0.0 + version: 4.0.0 packages/aila: dependencies: @@ -23401,6 +23404,11 @@ packages: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} + /web-streams-polyfill@4.0.0: + resolution: {integrity: sha512-0zJXHRAYEjM2tUfZ2DiSOHAa2aw1tisnnhU3ufD57R8iefL+DcdJyRBRyJpG+NUimDgbTI/lH+gAE1PAvV3Cgw==} + engines: {node: '>= 8'} + dev: true + /web-streams-polyfill@4.0.0-beta.3: resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} engines: {node: '>= 14'} diff --git a/tsconfig.json b/tsconfig.json index 775e93286..a731459f5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,7 @@ "noUncheckedIndexedAccess": true, "incremental": true, "types": ["jest", "node"], - "moduleResolution": "bundler", + "moduleResolution": "node", "esModuleInterop": true }, "include": [".eslintrc.cjs"] diff --git a/tsconfig.test.json b/tsconfig.test.json index dbbc977dd..632c17283 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -11,7 +11,7 @@ "forceConsistentCasingInFileNames": true, "noEmit": true, "esModuleInterop": true, - "moduleResolution": "bundler", + "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", From d04d456bd2b3968491088ed39631e11c23d2cf9c Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Fri, 25 Oct 2024 18:37:45 +0100 Subject: [PATCH 028/127] feat: add a test:seq command (#273) --- apps/nextjs/package.json | 1 + apps/nextjs/tsconfig.json | 45 +---------------------------- apps/nextjs/tsconfig.storybook.json | 3 +- apps/nextjs/tsconfig.test.json | 3 +- package.json | 3 +- packages/aila/package.json | 1 + packages/aila/tsconfig.json | 5 ---- packages/aila/tsconfig.test.json | 5 ++-- packages/ingest/package.json | 1 + packages/ingest/tsconfig.test.json | 5 ++-- tsconfig.test.json | 4 +-- turbo.json | 6 ++-- 12 files changed, 21 insertions(+), 61 deletions(-) diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 59482942b..7ae770a2d 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -17,6 +17,7 @@ "start": "next start", "type-check": "tsc --noEmit", "test": "pnpm with-env jest --colors --config jest.config.js", + "test:seq": "pnpm with-env jest --colors --config jest.config.js --verbose --runInBand --no-cache", "test-e2e": "pnpm with-env playwright test", "test-e2e-ui": "pnpm with-env playwright test --ui", "test-e2e-ui-serve": "pnpm build && pnpm start --port 4848 --turbo", diff --git a/apps/nextjs/tsconfig.json b/apps/nextjs/tsconfig.json index 589e22a5b..5b896071a 100644 --- a/apps/nextjs/tsconfig.json +++ b/apps/nextjs/tsconfig.json @@ -1,45 +1,3 @@ -// { -// "extends": "../../tsconfig.json", -// "exclude": ["node_modules"], -// "include": [ -// "next-env.d.ts", -// "**/*.ts", -// "**/*.tsx", -// "**/*.cjs", -// "**/*.mjs", -// ".next/types/**/*.ts" -// ], -// "compilerOptions": { -// "baseUrl": "./src", -// "allowJs": true, -// "skipLibCheck": true, -// "strict": true, -// "forceConsistentCasingInFileNames": true, -// "noEmit": true, -// "incremental": true, -// "esModuleInterop": true, -// "module": "esnext", -// "moduleResolution": "node", -// "resolveJsonModule": true, -// "isolatedModules": true, -// "noImplicitAny": false, -// "jsx": "preserve", -// "paths": { -// "@/components/*": ["components/*"], -// "@/app/*": ["app/*"], -// "@/lib/*": ["lib/*"], -// "@/utils/*": ["utils/*"], -// "@/assets/*": ["assets/*"] -// }, -// "plugins": [ -// { -// "name": "next" -// } -// ], -// "strictNullChecks": true -// } -// } - { "compilerOptions": { "lib": ["dom", "dom.iterable", "esnext"], @@ -70,8 +28,7 @@ "name": "next" } ], - "strictNullChecks": true, - "types": ["jest", "node", "@testing-library/jest-dom", "@storybook/react"] + "strictNullChecks": true }, "include": [ "next-env.d.ts", diff --git a/apps/nextjs/tsconfig.storybook.json b/apps/nextjs/tsconfig.storybook.json index d898df292..f7dcfb7a6 100644 --- a/apps/nextjs/tsconfig.storybook.json +++ b/apps/nextjs/tsconfig.storybook.json @@ -9,6 +9,7 @@ "@/assets/*": ["assets/*"], "next/*": ["mocks/next/*"], "@clerk/*": ["mocks/clerk/*"] - } + }, + "types": ["jest", "node", "@testing-library/jest-dom", "@storybook/react"] } } diff --git a/apps/nextjs/tsconfig.test.json b/apps/nextjs/tsconfig.test.json index 179cf4a80..aff714128 100644 --- a/apps/nextjs/tsconfig.test.json +++ b/apps/nextjs/tsconfig.test.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "lib": ["esnext", "dom"], + "lib": ["dom", "esnext"], "outDir": "./dist-tests", "rootDir": "./src", "strict": true, @@ -12,6 +12,7 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, + "incremental": true, "noEmit": true, "types": ["jest", "node", "@testing-library/jest-dom", "@storybook/react"], "baseUrl": "./src", diff --git a/package.json b/package.json index 2cd791842..d6bbf0cff 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,8 @@ "type-check": "turbo type-check", "node-version": "node -v", "with-env": "dotenv -e ../../.env --", - "test": "turbo test --", + "test": "turbo test -- --maxWorkers=33%", + "test:seq": "turbo test:seq --concurrency=1 --", "test-e2e": "turbo test-e2e --", "test-e2e-ui": "turbo test-e2e-ui --", "storybook": "turbo storybook" diff --git a/packages/aila/package.json b/packages/aila/package.json index d4257d14b..9bb11f81d 100644 --- a/packages/aila/package.json +++ b/packages/aila/package.json @@ -10,6 +10,7 @@ "lint": "eslint . --ext .ts,.tsx", "type-check": "tsc --noEmit", "test": "pnpm with-env jest --colors --config jest.config.js", + "test:seq": "pnpm with-env jest --colors --config jest.config.js --verbose --runInBand", "test-file": "pnpm with-env jest --config jest.config.js --runTestsByPath TEST_FILEPATH", "with-env": "dotenv -e ../../.env --" }, diff --git a/packages/aila/tsconfig.json b/packages/aila/tsconfig.json index 2622a6d6e..9f14f1afb 100644 --- a/packages/aila/tsconfig.json +++ b/packages/aila/tsconfig.json @@ -8,11 +8,6 @@ "../core/src/utils/subjects/index.ts" ], "compilerOptions": { - "target": "esnext", - "module": "esnext", - "moduleResolution": "node", - "esModuleInterop": true, - "types": ["jest"], "typeRoots": [ "./../../node_modules/@types", "./src/types", diff --git a/packages/aila/tsconfig.test.json b/packages/aila/tsconfig.test.json index 654c6f0a4..82168dcf9 100644 --- a/packages/aila/tsconfig.test.json +++ b/packages/aila/tsconfig.test.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "lib": ["esnext", "dom"], + "lib": ["esnext"], "outDir": "./dist-tests", "rootDir": "./src", "strict": true, @@ -12,8 +12,9 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, + "incremental": true, "noEmit": true, - "types": ["jest", "node", "@testing-library/jest-dom"], + "types": ["jest", "node"], "typeRoots": [ "./../../node_modules/@types", "./src/types", diff --git a/packages/ingest/package.json b/packages/ingest/package.json index 5d7e85b1a..7fae73a65 100644 --- a/packages/ingest/package.json +++ b/packages/ingest/package.json @@ -13,6 +13,7 @@ "ingest": "pnpm with-env ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/index.ts", "with-env": "dotenv -e ../../.env --", "test": "pnpm with-env jest --colors --config jest.config.js", + "test:seq": "pnpm with-env jest --colors --config jest.config.js --verbose --runInBand", "test-file": "pnpm with-env jest --config jest.config.js --runTestsByPath TEST_FILEPATH" }, "prettier": "@oakai/prettier-config", diff --git a/packages/ingest/tsconfig.test.json b/packages/ingest/tsconfig.test.json index 26ab52326..f7bc25af4 100644 --- a/packages/ingest/tsconfig.test.json +++ b/packages/ingest/tsconfig.test.json @@ -2,18 +2,19 @@ "compilerOptions": { "target": "esnext", "module": "esnext", - "lib": ["esnext", "dom"], + "lib": ["dom", "esnext"], "outDir": "./dist-tests", "rootDir": "./src", "strict": true, "esModuleInterop": true, + "incremental": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, - "types": ["jest", "node", "@testing-library/jest-dom"], + "types": ["jest", "node"], "paths": { "@/*": ["./src/*"] } diff --git a/tsconfig.test.json b/tsconfig.test.json index 632c17283..1836b0284 100644 --- a/tsconfig.test.json +++ b/tsconfig.test.json @@ -1,11 +1,9 @@ { "compilerOptions": { - "noEmitOnError": true, "outDir": "./build/tests", "module": "esnext", "target": "esnext", - "lib": ["dom", "dom.iterable", "esnext"], - "allowJs": true, + "lib": ["esnext"], "skipLibCheck": true, "strict": true, "forceConsistentCasingInFileNames": true, diff --git a/turbo.json b/turbo.json index e7d0e0144..7e3581d11 100644 --- a/turbo.json +++ b/turbo.json @@ -59,8 +59,10 @@ "cache": false }, "test": { - "cache": false, - "outputs": ["packages/aila/**/*.spec.js", "packages/aila/**/*.test.js"] + "cache": false + }, + "test:seq": { + "cache": false }, "test-e2e": { "cache": false, From d492557eee51cee59a1756ddc904b0dc010ca990 Mon Sep 17 00:00:00 2001 From: semantic-release-bot Date: Mon, 28 Oct 2024 16:35:58 +0000 Subject: [PATCH 029/127] build(release v1.12.0): See CHANGE_LOG.md --- CHANGE_LOG.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index b5c4d5401..184602945 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -1,3 +1,27 @@ +# [1.12.0](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.11.0...v1.12.0) (2024-10-28) + + +### Bug Fixes + +* ensure that the patch enqueuer exists when enqueuing ([#264](https://github.com/oaknational/oak-ai-lesson-assistant/issues/264)) ([4d4b959](https://github.com/oaknational/oak-ai-lesson-assistant/commit/4d4b9599996482817411237a13174ed55ef6c476)) +* logger to stdout for info and warn ([#266](https://github.com/oaknational/oak-ai-lesson-assistant/issues/266)) ([d2dfabc](https://github.com/oaknational/oak-ai-lesson-assistant/commit/d2dfabcb41ed85a731435ed8283540cd974b0de5)) +* module resolution with "node" instead of "bundler" ([#271](https://github.com/oaknational/oak-ai-lesson-assistant/issues/271)) ([44ca5f6](https://github.com/oaknational/oak-ai-lesson-assistant/commit/44ca5f6cebd7d9a7f047cfad558030cb206b4f3c)) +* reduce turbo errors on local dev, enable running e2e in built mode ([#259](https://github.com/oaknational/oak-ai-lesson-assistant/issues/259)) ([afb4535](https://github.com/oaknational/oak-ai-lesson-assistant/commit/afb453581c3dbe2b01a4e0712df97a7779d59e6c)) +* refactor lesson snapshots for improved efficiency ([#234](https://github.com/oaknational/oak-ai-lesson-assistant/issues/234)) ([24f492d](https://github.com/oaknational/oak-ai-lesson-assistant/commit/24f492dc79e6a9dc94e213c4f447802e750c99db)) +* reinstate prebuilding the chat route in local dev ([#260](https://github.com/oaknational/oak-ai-lesson-assistant/issues/260)) ([1a4c51f](https://github.com/oaknational/oak-ai-lesson-assistant/commit/1a4c51fa4b2b53e758f860091c85f840c191dc9f)) +* remove flash when loading wheel appears ([#268](https://github.com/oaknational/oak-ai-lesson-assistant/issues/268)) ([d7a70f3](https://github.com/oaknational/oak-ai-lesson-assistant/commit/d7a70f353be0c6e1a833f5ec922d2e62f71bba7f)) +* remove lessonReferences from prompt ([#253](https://github.com/oaknational/oak-ai-lesson-assistant/issues/253)) ([a0cedec](https://github.com/oaknational/oak-ai-lesson-assistant/commit/a0cedec86924a1bf0cf2466bd7a5ef106d7f8272)) +* report chat completion errors ([#256](https://github.com/oaknational/oak-ai-lesson-assistant/issues/256)) ([13729ca](https://github.com/oaknational/oak-ai-lesson-assistant/commit/13729ca4d0d64f436309575bf8be50640604c710)) +* update prisma to latest version ([#254](https://github.com/oaknational/oak-ai-lesson-assistant/issues/254)) ([b8b1958](https://github.com/oaknational/oak-ai-lesson-assistant/commit/b8b1958c914b405e859cf39163fa531bef9e99d5)) + + +### Features + +* add a test:seq command ([#273](https://github.com/oaknational/oak-ai-lesson-assistant/issues/273)) ([d04d456](https://github.com/oaknational/oak-ai-lesson-assistant/commit/d04d456bd2b3968491088ed39631e11c23d2cf9c)) +* hint Aila as to which part of the lesson to update next ([#265](https://github.com/oaknational/oak-ai-lesson-assistant/issues/265)) ([b8b39b8](https://github.com/oaknational/oak-ai-lesson-assistant/commit/b8b39b833c788ca7fc38c9319c401aae2c1d330a)) +* ingest scripts ([#200](https://github.com/oaknational/oak-ai-lesson-assistant/issues/200)) ([be824a8](https://github.com/oaknational/oak-ai-lesson-assistant/commit/be824a8d73f8d0e2bc690789314ed7b0d135b090)) +* local db setup scripts ([#132](https://github.com/oaknational/oak-ai-lesson-assistant/issues/132)) ([d531a50](https://github.com/oaknational/oak-ai-lesson-assistant/commit/d531a50229c604fa71157609fcde181eefb5e9b3)) + # [1.11.0](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.10.2...v1.11.0) (2024-10-22) From 1efc89eabd23d226060ad84b60010a5ff5cd9674 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Mon, 28 Oct 2024 17:12:30 +0000 Subject: [PATCH 030/127] fix: hide streaming status in production (#281) --- .../src/components/AppComponents/Chat/chat-lhs-header.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index 20509eb6a..f8b69e2f7 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -24,7 +24,7 @@ const ChatLhsHeader = ({ return ( <>
- {process.env.NEXT_PUBLIC_ENVIRONMENT !== "production" && ( + {process.env.NEXT_PUBLIC_ENVIRONMENT !== "prd" && (
Date: Mon, 28 Oct 2024 17:13:09 +0000 Subject: [PATCH 031/127] build(release v1.12.1): See CHANGE_LOG.md --- CHANGE_LOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGE_LOG.md b/CHANGE_LOG.md index 184602945..05222b8da 100644 --- a/CHANGE_LOG.md +++ b/CHANGE_LOG.md @@ -1,3 +1,10 @@ +## [1.12.1](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.12.0...v1.12.1) (2024-10-28) + + +### Bug Fixes + +* hide streaming status in production ([#281](https://github.com/oaknational/oak-ai-lesson-assistant/issues/281)) ([1efc89e](https://github.com/oaknational/oak-ai-lesson-assistant/commit/1efc89eabd23d226060ad84b60010a5ff5cd9674)) + # [1.12.0](https://github.com/oaknational/oak-ai-lesson-assistant/compare/v1.11.0...v1.12.0) (2024-10-28) From 2c66e48b87c583ed634462755651bec571dd039e Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Mon, 28 Oct 2024 18:29:07 +0100 Subject: [PATCH 032/127] chore: make turbopack the default for local dev (#274) --- apps/nextjs/next.config.js | 57 +- apps/nextjs/package.json | 8 +- ...{local-dev.mjs => preload-chat-routes.mjs} | 0 apps/nextjs/src/instrumentation.ts | 4 + package.json | 1 - packages/aila/package.json | 2 +- pnpm-lock.yaml | 643 +++++++++++------- turbo.json | 6 - 8 files changed, 430 insertions(+), 291 deletions(-) rename apps/nextjs/scripts/{local-dev.mjs => preload-chat-routes.mjs} (100%) diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 3d7103f35..3f27d92fd 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -5,7 +5,6 @@ const { RELEASE_STAGE_TESTING, } = require("./scripts/build_config_helpers.js"); const path = require("path"); -const Sentry = require("@sentry/nextjs"); const { PHASE_PRODUCTION_BUILD, PHASE_TEST } = require("next/constants"); @@ -155,41 +154,37 @@ const getConfig = async (phase) => { module.exports = getConfig; -if (!process.env.TURBOPACK) { - module.exports = withSentryConfig(module.exports, { - // For all available options, see: - // https://github.com/getsentry/sentry-webpack-plugin#options +module.exports = withSentryConfig(module.exports, { + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options - org: process.env.SENTRY_ORG, - project: process.env.SENTRY_PROJECT, + org: process.env.SENTRY_ORG, + project: process.env.SENTRY_PROJECT, - // Only print logs for uploading source maps in CI - silent: !process.env.CI, + // Only print logs for uploading source maps in CI + silent: !process.env.CI, - // For all available options, see: - // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ - // Upload a larger set of source maps for prettier stack traces (increases build time) - widenClientFileUpload: true, + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, - // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. - // This can increase your server load as well as your hosting bill. - // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- - // side errors will fail. - tunnelRoute: "/monitoring", + // Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers. + // This can increase your server load as well as your hosting bill. + // Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client- + // side errors will fail. + tunnelRoute: "/monitoring", - // Hides source maps from generated client bundles - hideSourceMaps: true, + // Hides source maps from generated client bundles + hideSourceMaps: true, - // Automatically tree-shake Sentry logger statements to reduce bundle size - disableLogger: true, + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, - // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) - // See the following for more information: - // https://docs.sentry.io/product/crons/ - // https://vercel.com/docs/cron-jobs - automaticVercelMonitors: true, - }); -} - -module.exports.onRequestError = Sentry.captureRequestError; + // Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.) + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true, +}); diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 7ae770a2d..6afe0a841 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -7,10 +7,8 @@ "build:dev": "pnpm with-env next build", "check": "tsc --noEmit", "clean": "rm -rf .next .turbo node_modules", - "dev": "FORCE_COLOR=1 concurrently \"pnpm dev-server\" \"node scripts/local-dev.mjs\"", - "dev-turbo": "FORCE_COLOR=1 concurrently \"pnpm dev-server-turbo\" \"node scripts/local-dev.mjs\"", - "dev-server-turbo": "FORCE_COLOR=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 --turbo | pino-pretty -C", - "dev-server": "FORCE_COLOR=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 | pino-pretty -C", + "dev": "FORCE_COLOR=1 SENTRY_SUPPRESS_TURBOPACK_WARNING=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 --turbo | pino-pretty -C", + "dev:sentry": "FORCE_COLOR=1 pnpm with-env node scripts/increase-listeners.js next dev --port 2525 | pino-pretty -C", "dev-trace-deprecation": "NODE_OPTIONS=\"--trace-deprecation\" next dev --port 2525 | pino-pretty -C", "lint": "next lint", "lint-fix": "next lint --fix", @@ -56,7 +54,7 @@ "@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/themes": "^1.0.0", "@sanity/client": "^6.21.3", - "@sentry/nextjs": "^8.19.0", + "@sentry/nextjs": "^8.35.0", "@storybook/testing-react": "^2.0.1", "@tanstack/react-query": "^4.16.1", "@testing-library/jest-dom": "^6.4.8", diff --git a/apps/nextjs/scripts/local-dev.mjs b/apps/nextjs/scripts/preload-chat-routes.mjs similarity index 100% rename from apps/nextjs/scripts/local-dev.mjs rename to apps/nextjs/scripts/preload-chat-routes.mjs diff --git a/apps/nextjs/src/instrumentation.ts b/apps/nextjs/src/instrumentation.ts index 32a937a7a..72a66aa19 100644 --- a/apps/nextjs/src/instrumentation.ts +++ b/apps/nextjs/src/instrumentation.ts @@ -1,3 +1,5 @@ +import * as Sentry from "@sentry/nextjs"; + export async function register() { if (process.env.NEXT_RUNTIME === "nodejs") { await import("../sentry.server.config"); @@ -11,3 +13,5 @@ export async function register() { await import("./instrumentation/tracer"); } } + +export const onRequestError = Sentry.captureRequestError; diff --git a/package.json b/package.json index d6bbf0cff..c3d10e5f6 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "db-push-force": "turbo db-push-force", "db-seed": "turbo db-seed", "dev": "FORCE_COLOR=1 turbo dev --parallel --ui=stream --log-prefix=none --filter=!@oakai/db", - "dev-turbo": "FORCE_COLOR=1 turbo dev-turbo --parallel --log-prefix=none --ui=stream --filter=!@oakai/db", "doppler:pull:dev": "doppler secrets download --config dev --no-file --format env > .env", "doppler:pull:stg": "doppler secrets download --config stg --no-file --format env > .env", "doppler:run:stg": "doppler run -c stg --silent", diff --git a/packages/aila/package.json b/packages/aila/package.json index 9bb11f81d..206df9ad9 100644 --- a/packages/aila/package.json +++ b/packages/aila/package.json @@ -22,7 +22,7 @@ "@oakai/db": "*", "@oakai/exports": "*", "@oakai/logger": "*", - "@sentry/nextjs": "^8.19.0", + "@sentry/nextjs": "^8.35.0", "@vercel/kv": "^0.2.2", "ai": "^3.3.26", "american-british-english-translator": "^0.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7003340a9..061c87228 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -159,8 +159,8 @@ importers: specifier: ^6.21.3 version: 6.21.3 '@sentry/nextjs': - specifier: ^8.19.0 - version: 8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0) + specifier: ^8.35.0 + version: 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0) '@storybook/testing-react': specifier: ^2.0.1 version: 2.0.1(@storybook/client-logger@7.6.20)(@storybook/preview-api@7.6.20)(@storybook/react@8.2.7)(@storybook/types@7.6.20)(react@18.2.0) @@ -487,8 +487,8 @@ importers: specifier: '*' version: link:../logger '@sentry/nextjs': - specifier: ^8.19.0 - version: 8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0) + specifier: ^8.35.0 + version: 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0) '@vercel/kv': specifier: ^0.2.2 version: 0.2.2 @@ -5190,6 +5190,13 @@ packages: '@opentelemetry/api': 1.9.0 dev: false + /@opentelemetry/api-logs@0.53.0: + resolution: {integrity: sha512-8HArjKx+RaAI8uEIgcORbZIPklyh1YLjPSBus8hjRmvLi6DeFzgOcdZ7KwPabKj8mXF8dX0hyfAyGfycz0DbFw==} + engines: {node: '>=14'} + dependencies: + '@opentelemetry/api': 1.9.0 + dev: false + /@opentelemetry/api@1.8.0: resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==} engines: {node: '>=8.0.0'} @@ -5238,243 +5245,331 @@ packages: '@opentelemetry/semantic-conventions': 1.27.0 dev: false - /@opentelemetry/instrumentation-connect@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-2/nRnx3pjYEmdPIaBwtgtSviTKHWnDZN3R+TkRUnhIVrvBKVcq+I5B2rtd6mr6Fe9cHlZ9Ojcuh7pkNh/xdWWg==} + /@opentelemetry/instrumentation-amqplib@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-fiuU6OKsqHJiydHWgTRQ7MnIrJ2lEqsdgFtNIH4LbAUJl/5XmrIeoDzDnox+hfkgWK65jsleFuQDtYb5hW1koQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-connect@0.39.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-pGBiKevLq7NNglMgqzmeKczF4XQMTOUOTkK8afRHMZMnrK3fcETyTH7lVaSozwiOM3Ws+SuEmXZT7DYrrhxGlg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 '@types/connect': 3.4.36 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-express@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-/B7fbMdaf3SYe5f1P973tkqd6s7XZirjpfkoJ63E7nltU30qmlgm9tY5XwZOzAFI0rHS9tbrFI2HFPAvQUFe/A==} + /@opentelemetry/instrumentation-dataloader@0.12.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-pnPxatoFE0OXIZDQhL2okF//dmbiWFzcSc8pUg9TqofCLYZySSxDCgQc69CJBo5JnI3Gz1KP+mOjS4WAeRIH4g==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-express@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-bxTIlzn9qPXJgrhz8/Do5Q3jIlqfpoJrSUtVGqH+90eM1v2PkPHc+SdE+zSqe4q9Y1UQJosmZ4N4bm7Zj/++MA==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-fastify@0.38.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-HBVLpTSYpkQZ87/Df3N0gAw7VzYZV3n28THIBrJWfuqw3Or7UqdhnjeuMIPQ04BKk3aZc0cWn2naSQObbh5vXw==} + /@opentelemetry/instrumentation-fastify@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-74qj4nG3zPtU7g2x4sm2T4R3/pBMyrYstTsqSZwdlhQk1SD4l8OSY9sPRX1qkhfxOuW3U4KZQAV/Cymb3fB6hg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-graphql@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-N8SOwoKL9KQSX7z3gOaw5UaTeVQcfDO1c21csVHnmnmGUoqsXbArK2B8VuwPWcv6/BC/i3io+xTo7QGRZ/z28Q==} + /@opentelemetry/instrumentation-fs@0.15.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-JWVKdNLpu1skqZQA//jKOcKdJC66TWKqa2FUFq70rKohvaSq47pmXlnabNO+B/BvLfmidfiaN35XakT5RyMl2Q==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-hapi@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-8U/w7Ifumtd2bSN1OLaSwAAFhb9FyqWUki3lMMB0ds+1+HdSxYBe9aspEJEgvxAqOkrQnVniAPTEGf1pGM7SOw==} + /@opentelemetry/instrumentation-generic-pool@0.39.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-y4v8Y+tSfRB3NNBvHjbjrn7rX/7sdARG7FuK6zR8PGb28CTa0kHpEGCJqvL9L8xkTNvTXo+lM36ajFGUaK1aNw==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-graphql@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-aI3YMmC2McGd8KW5du1a2gBA0iOMOGLqg4s9YjzwbjFwjlmMNFSK1P3AIg374GWg823RPUGfVTIgZ/juk9CVOA==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-hapi@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-jKDrxPNXDByPlYcMdZjNPYCvw0SQJjN+B1A+QH+sx+sAHsKSAf9hwFiJSrI6C4XdOls43V/f/fkp9ITkHhKFbQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-http@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==} + /@opentelemetry/instrumentation-http@0.53.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-H74ErMeDuZfj7KgYCTOFGWF5W9AfaPnqLQQxeFq85+D29wwV2yqHbz2IKLYpkOh7EI6QwDEl7rZCIxjJLyc/CQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 - semver: 7.6.2 + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + semver: 7.6.3 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-ioredis@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-P11H168EKvBB9TUSasNDOGJCSkpT44XgoM6d3gRIWAa9ghLpYhl0uRkS8//MqPzcJVHr3h3RmfXIpiYLjyIZTw==} + /@opentelemetry/instrumentation-ioredis@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-i3Dke/LdhZbiUAEImmRG3i7Dimm/BD7t8pDDzwepSvIQ6s2X6FPia7561gw+64w+nx0+G9X14D7rEfaMEmmjig==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-koa@0.42.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H1BEmnMhho8o8HuNRq5zEI4+SIHDIglNB7BPKohZyWG4fWNuR7yM4GTlR01Syq21vODAS7z5omblScJD/eZdKw==} + /@opentelemetry/instrumentation-kafkajs@0.3.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-UnkZueYK1ise8FXQeKlpBd7YYUtC7mM8J0wzUSccEfc/G8UqHQqAzIyYCUOUPUKp8GsjLnWOOK/3hJc4owb7Jg==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-koa@0.43.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-lDAhSnmoTIN6ELKmLJBplXzT/Jqs5jGZehuG22EdSMaTwgjMpxMDI1YtlKEhiWPWkrz5LUsd0aOO0ZRc9vn3AQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-mongodb@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-VF/MicZ5UOBiXrqBslzwxhN7TVqzu1/LN/QDpkskqM0Zm0aZ4CVRbUygL8d7lrjLn15x5kGIe8VsSphMfPJzlA==} + /@opentelemetry/instrumentation-lru-memoizer@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-21xRwZsEdMPnROu/QsaOIODmzw59IYpGFmuC4aFWvMj6stA8+Ei1tX67nkarJttlNjoM94um0N4X26AD7ff54A==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation-mongodb@0.47.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-yqyXRx2SulEURjgOQyJzhCECSh5i1uM49NUaq9TqLd6fA7g26OahyJfsr9NE38HFqGRHpi4loyrnfYGdrsoVjQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-metrics': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-mongoose@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-niRi5ZUnkgzRhIGMOozTyoZIvJKNJyhijQI4nF4iFSb+FUx2v5fngfR+8XLmdQAO7xmsD8E5vEGdDVYVtKbZew==} + /@opentelemetry/instrumentation-mongoose@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-AnWv+RaR86uG3qNEMwt3plKX1ueRM7AspfszJYVkvkehiicC3bHQA6vWdb6Zvy5HAE14RyFbu9+2hUUjR2NSyg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-mysql2@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-0xfS1xcqUmY7WE1uWjlmI67Xg3QsSUlNT+AcXHeA4BDUPwZtWqF4ezIwLgpVZfHOnkAEheqGfNSWd1PIu3Wnfg==} + /@opentelemetry/instrumentation-mysql2@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-REQB0x+IzVTpoNgVmy5b+UnH1/mDByrneimP6sbDHkp1j8QOl1HyWOrBH/6YWR0nrbU3l825Em5PlybjT3232g==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-mysql@0.40.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-d7ja8yizsOCNMYIJt5PH/fKZXjb/mS48zLROO4BzZTtDfhNCl2UM/9VIomP2qkGIFVouSJrGr/T00EzY7bPtKA==} + /@opentelemetry/instrumentation-mysql@0.41.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-jnvrV6BsQWyHS2qb2fkfbfSb1R/lmYwqEZITwufuRl37apTopswu9izc0b1CYRp/34tUG/4k/V39PND6eyiNvw==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 - '@types/mysql': 2.15.22 + '@types/mysql': 2.15.26 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-nestjs-core@0.39.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-mewVhEXdikyvIZoMIUry8eb8l3HUjuQjSjVbmLVTt4NQi35tkpnHQrG9bTRBrl3403LoWZ2njMPJyg4l6HfKvA==} + /@opentelemetry/instrumentation-nestjs-core@0.40.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-WF1hCUed07vKmf5BzEkL0wSPinqJgH7kGzOjjMAiTGacofNXjb/y4KQ8loj2sNsh5C/NN7s1zxQuCgbWbVTGKg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-pg@0.43.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-og23KLyoxdnAeFs1UWqzSonuCkePUzCX30keSYigIzJe/6WSYA8rnEI5lobcxPEzg+GcU06J7jzokuEHbjVJNw==} + /@opentelemetry/instrumentation-pg@0.44.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-oTWVyzKqXud1BYEGX1loo2o4k4vaU1elr3vPO8NZolrBtFvQ34nx4HgUaexUDuEog00qQt+MLR5gws/p+JXMLQ==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.9.0) '@types/pg': 8.6.1 - '@types/pg-pool': 2.0.4 + '@types/pg-pool': 2.0.6 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation-redis-4@0.41.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-H7IfGTqW2reLXqput4yzAe8YpDC0fmVNal95GHMLOrS89W+qWUKIqxolSh63hJyfmwPSFwXASzj7wpSk8Az+Dg==} + /@opentelemetry/instrumentation-redis-4@0.42.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-NaD+t2JNcOzX/Qa7kMy68JbmoVIV37fT/fJYzLKu2Wwd+0NCxt+K2OOsOakA8GVg8lSpFdbx4V/suzZZ2Pvdjg==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/redis-common': 0.36.2 '@opentelemetry/semantic-conventions': 1.27.0 transitivePeerDependencies: - supports-color dev: false - /@opentelemetry/instrumentation@0.46.0(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-a9TijXZZbk0vI5TGLZl+0kxyFfrXHhX6Svtz7Pp2/VBlCSKrazuULEyoJQrOknJyFWNMEmbbJgOciHCCpQcisw==} + /@opentelemetry/instrumentation-undici@0.6.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-ABJBhm5OdhGmbh0S/fOTE4N69IZ00CsHC5ijMYfzbw3E5NwLgpQk5xsljaECrJ8wz1SfXbO03FiSuu5AyRAkvQ==} + engines: {node: '>=14'} + peerDependencies: + '@opentelemetry/api': ^1.7.0 + dependencies: + '@opentelemetry/api': 1.9.0 + '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + transitivePeerDependencies: + - supports-color + dev: false + + /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} engines: {node: '>=14'} - requiresBuild: true peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 + '@opentelemetry/api-logs': 0.52.1 '@types/shimmer': 1.2.0 - import-in-the-middle: 1.7.1 + import-in-the-middle: 1.10.0 require-in-the-middle: 7.3.0 semver: 7.6.3 shimmer: 1.2.1 transitivePeerDependencies: - supports-color dev: false - optional: true - /@opentelemetry/instrumentation@0.52.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==} + /@opentelemetry/instrumentation@0.53.0(@opentelemetry/api@1.9.0): + resolution: {integrity: sha512-DMwg0hy4wzf7K73JJtl95m/e0boSoWhH07rfvHvYzQtBD3Bmv0Wc1x733vyZBqmFm8OjJD0/pfiUg1W3JjFX0A==} engines: {node: '>=14'} peerDependencies: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/api-logs': 0.52.1 + '@opentelemetry/api-logs': 0.53.0 '@types/shimmer': 1.2.0 import-in-the-middle: 1.10.0 require-in-the-middle: 7.3.0 @@ -5833,8 +5928,8 @@ packages: dependencies: '@prisma/debug': 5.21.1 - /@prisma/instrumentation@5.17.0: - resolution: {integrity: sha512-c1Sle4ji8aasMcYfBBHFM56We4ljfenVtRmS8aY06BllS7SoU6SmJBwG7vil+GHiR0Yrh+t9iBwt4AY0Jr4KNQ==} + /@prisma/instrumentation@5.19.1: + resolution: {integrity: sha512-VLnzMQq7CWroL5AeaW0Py2huiNKeoMfCH3SUxstdzPrlWQi6UQ9UrfcbUkNHlVFqOMacqy8X/8YtE0kuKDpD9w==} dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) @@ -7395,7 +7490,7 @@ packages: resolution: {integrity: sha512-Javneu5lsuhwNCryN+pXH93VPQ8g0dBX7wItHFgYiwQmzE1sVdg5tWHiOgHywzL2W21XQopa7IwIEnNbmeUJYA==} dev: true - /@rollup/plugin-commonjs@26.0.1(rollup@3.29.4): + /@rollup/plugin-commonjs@26.0.1(rollup@3.29.5): resolution: {integrity: sha512-UnsKoZK6/aGIH6AdkptXhNvhaqftcjq3zZdT+LY5Ftms6JR06nADcDsYp5hTU9E2lbJUEOhdlY5J4DNTneM+jQ==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: @@ -7404,16 +7499,16 @@ packages: rollup: optional: true dependencies: - '@rollup/pluginutils': 5.1.0(rollup@3.29.4) + '@rollup/pluginutils': 5.1.0(rollup@3.29.5) commondir: 1.0.1 estree-walker: 2.0.2 glob: 10.4.5 is-reference: 1.2.1 magic-string: 0.30.10 - rollup: 3.29.4 + rollup: 3.29.5 dev: false - /@rollup/pluginutils@5.1.0(rollup@3.29.4): + /@rollup/pluginutils@5.1.0(rollup@3.29.5): resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} engines: {node: '>=14.0.0'} peerDependencies: @@ -7425,7 +7520,7 @@ packages: '@types/estree': 1.0.5 estree-walker: 2.0.2 picomatch: 2.3.1 - rollup: 3.29.4 + rollup: 3.29.5 dev: false /@rushstack/eslint-patch@1.7.2: @@ -7598,47 +7693,42 @@ packages: - supports-color dev: false - /@sentry-internal/browser-utils@8.19.0: - resolution: {integrity: sha512-kM/2KlikKuBR63nFi2q7MGS3V9K9hakjvUknhr/jHZqDVfEuBKmp1ZlHFAdJtglKHHJy07gPj/XqDH7BbYh5yg==} + /@sentry-internal/browser-utils@8.35.0: + resolution: {integrity: sha512-uj9nwERm7HIS13f/Q52hF/NUS5Al8Ma6jkgpfYGeppYvU0uSjPkwMogtqoJQNbOoZg973tV8qUScbcWY616wNA==} engines: {node: '>=14.18'} dependencies: - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry-internal/feedback@8.19.0: - resolution: {integrity: sha512-Jc77H8fEaGcBhERc2U/o7Q8CZHvlZLT9vAlzq0ZZR20v/1vwYcJW1ysKfTuvmw22hCR6ukhFNl6pqJocXFVhvA==} + /@sentry-internal/feedback@8.35.0: + resolution: {integrity: sha512-7bjSaUhL0bDArozre6EiIhhdWdT/1AWNWBC1Wc5w1IxEi5xF7nvF/FfvjQYrONQzZAI3HRxc45J2qhLUzHBmoQ==} engines: {node: '>=14.18'} dependencies: - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry-internal/replay-canvas@8.19.0: - resolution: {integrity: sha512-l4pKJDHrXEctxrK7Xme/+fKToXpGwr/G2t77BzeE1WEw9LwSwADz/hi8HoMdZzuKWriM2BNbz20tpVS84sODxA==} + /@sentry-internal/replay-canvas@8.35.0: + resolution: {integrity: sha512-TUrH6Piv19kvHIiRyIuapLdnuwxk/Un/l1WDCQfq7mK9p1Pac0FkQ7Uufjp6zY3lyhDDZQ8qvCS4ioCMibCwQg==} engines: {node: '>=14.18'} dependencies: - '@sentry-internal/replay': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry-internal/replay': 8.35.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry-internal/replay@8.19.0: - resolution: {integrity: sha512-EW9e1J6XbqXUXQST1AfSIzT9O8OwPyeFOkhkn9/gqOQv08TJvQEIBtWJEoJS+XFMEUuB8IqIzVWNVko/DnGt9A==} + /@sentry-internal/replay@8.35.0: + resolution: {integrity: sha512-3wkW03vXYMyWtTLxl9yrtkV+qxbnKFgfASdoGWhXzfLjycgT6o4/04eb3Gn71q9aXqRwH17ISVQbVswnRqMcmA==} engines: {node: '>=14.18'} dependencies: - '@sentry-internal/browser-utils': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 - dev: false - - /@sentry/babel-plugin-component-annotate@2.20.1: - resolution: {integrity: sha512-4mhEwYTK00bIb5Y9UWIELVUfru587Vaeg0DQGswv4aIRHIiMKLyNqCEejaaybQ/fNChIZOKmvyqXk430YVd7Qg==} - engines: {node: '>= 14'} + '@sentry-internal/browser-utils': 8.35.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false /@sentry/babel-plugin-component-annotate@2.21.1: @@ -7646,25 +7736,30 @@ packages: engines: {node: '>= 14'} dev: true - /@sentry/browser@8.19.0: - resolution: {integrity: sha512-ZC1HxIFm4TIGONyy9MkPG6Dw8IAhzq43t5mq9PqrB1ehuWj8GX6Vk3E26kuc2sydAm4AXbj0562OmvZHsAJpUA==} + /@sentry/babel-plugin-component-annotate@2.22.3: + resolution: {integrity: sha512-OlHA+i+vnQHRIdry4glpiS/xTOtgjmpXOt6IBOUqynx5Jd/iK1+fj+t8CckqOx9wRacO/hru2wfW/jFq0iViLg==} + engines: {node: '>= 14'} + dev: false + + /@sentry/browser@8.35.0: + resolution: {integrity: sha512-WHfI+NoZzpCsmIvtr6ChOe7yWPLQyMchPnVhY3Z4UeC70bkYNdKcoj/4XZbX3m0D8+71JAsm0mJ9s9OC3Ue6MQ==} engines: {node: '>=14.18'} dependencies: - '@sentry-internal/browser-utils': 8.19.0 - '@sentry-internal/feedback': 8.19.0 - '@sentry-internal/replay': 8.19.0 - '@sentry-internal/replay-canvas': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry-internal/browser-utils': 8.35.0 + '@sentry-internal/feedback': 8.35.0 + '@sentry-internal/replay': 8.35.0 + '@sentry-internal/replay-canvas': 8.35.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry/bundler-plugin-core@2.20.1: - resolution: {integrity: sha512-6ipbmGzHekxeRCbp7eoefr6bdd/lW4cNA9eNnrmd9+PicubweGaZZbH2NjhFHsaxzgOezwipDHjrTaap2kTHgw==} + /@sentry/bundler-plugin-core@2.21.1: + resolution: {integrity: sha512-F8FdL/bS8cy1SY1Gw0Mfo3ROTqlrq9Lvt5QGvhXi22dpVcDkWmoTWE2k+sMEnXOa8SdThMc/gyC8lMwHGd3kFQ==} engines: {node: '>= 14'} dependencies: '@babel/core': 7.24.5 - '@sentry/babel-plugin-component-annotate': 2.20.1 + '@sentry/babel-plugin-component-annotate': 2.21.1 '@sentry/cli': 2.33.0 dotenv: 16.4.5 find-up: 5.0.0 @@ -7674,15 +7769,15 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false + dev: true - /@sentry/bundler-plugin-core@2.21.1: - resolution: {integrity: sha512-F8FdL/bS8cy1SY1Gw0Mfo3ROTqlrq9Lvt5QGvhXi22dpVcDkWmoTWE2k+sMEnXOa8SdThMc/gyC8lMwHGd3kFQ==} + /@sentry/bundler-plugin-core@2.22.3: + resolution: {integrity: sha512-DeoUl0WffcqZZRl5Wy9aHvX4WfZbbWt0QbJ7NJrcEViq+dRAI2FQTYECFLwdZi5Gtb3oyqZICO+P7k8wDnzsjQ==} engines: {node: '>= 14'} dependencies: '@babel/core': 7.24.5 - '@sentry/babel-plugin-component-annotate': 2.21.1 - '@sentry/cli': 2.33.0 + '@sentry/babel-plugin-component-annotate': 2.22.3 + '@sentry/cli': 2.38.0 dotenv: 16.4.5 find-up: 5.0.0 glob: 9.3.5 @@ -7691,13 +7786,22 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false /@sentry/cli-darwin@2.33.0: resolution: {integrity: sha512-LQFvD7uCOQ2P/vYru7IBKqJDHwJ9Rr2vqqkdjbxe2YCQS/N3NPXvi3eVM9hDJ284oyV/BMZ5lrmVTuIicf/hhw==} engines: {node: '>=10'} os: [darwin] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-darwin@2.38.0: + resolution: {integrity: sha512-OvOaV9Vg4+b9ObK2z1oFj3zbRoqOSpD/wSz9t/mtSWwMQi7wlUXj88XGGsL5ZwF7VGBYL+kX59X3Ygl+dHFPlg==} + engines: {node: '>=10'} + os: [darwin] + requiresBuild: true + dev: false optional: true /@sentry/cli-linux-arm64@2.33.0: @@ -7706,6 +7810,16 @@ packages: cpu: [arm64] os: [linux, freebsd] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-linux-arm64@2.38.0: + resolution: {integrity: sha512-oUiRTyek0Ixe30zoqNlEFsLY07B9hK3FRXKv5lw341rim9PiTteh5tk5ewpuD63K+QjbEAJqp4f3zM19DEASlg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux, freebsd] + requiresBuild: true + dev: false optional: true /@sentry/cli-linux-arm@2.33.0: @@ -7714,6 +7828,16 @@ packages: cpu: [arm] os: [linux, freebsd] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-linux-arm@2.38.0: + resolution: {integrity: sha512-lXMSEX1Sv9F2wXnnAlsS+kwy09iLQTfK10n08xzeJLIvUCLua/OFInwH6WUxNT3tIBPfBVQZPR7iQMRycH4Ilw==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux, freebsd] + requiresBuild: true + dev: false optional: true /@sentry/cli-linux-i686@2.33.0: @@ -7722,6 +7846,16 @@ packages: cpu: [x86, ia32] os: [linux, freebsd] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-linux-i686@2.38.0: + resolution: {integrity: sha512-+luFmbQymDON16O7R/A7bmnkUjtnq1nRSehnnRJjuFCtDABCKatZzBjWvan0KNgzHhCquMSvEqHKzfVSptHeHw==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [linux, freebsd] + requiresBuild: true + dev: false optional: true /@sentry/cli-linux-x64@2.33.0: @@ -7730,6 +7864,16 @@ packages: cpu: [x64] os: [linux, freebsd] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-linux-x64@2.38.0: + resolution: {integrity: sha512-yY593xXbf2W+afyHKDvO4QJwoWQX97/K0NYUAqnpg3TVmIfLV9DNVid+M1w6vKIif6n8UQgAFWtR1Ys4P75mBg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux, freebsd] + requiresBuild: true + dev: false optional: true /@sentry/cli-win32-i686@2.33.0: @@ -7738,6 +7882,16 @@ packages: cpu: [x86, ia32] os: [win32] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-win32-i686@2.38.0: + resolution: {integrity: sha512-ipDnBvXaMqi0ZbkT/pqB11F4AaicVz5YRoidn5oxi1IJPDUd8qF0mnqabALLH3mAd5TOtKBliY5pllCFG/TvzA==} + engines: {node: '>=10'} + cpu: [x86, ia32] + os: [win32] + requiresBuild: true + dev: false optional: true /@sentry/cli-win32-x64@2.33.0: @@ -7746,6 +7900,16 @@ packages: cpu: [x64] os: [win32] requiresBuild: true + dev: true + optional: true + + /@sentry/cli-win32-x64@2.38.0: + resolution: {integrity: sha512-NqlKOqNF8i239mygARkNZK9BPzwWK91j+HPEfCKoHsZKHeBT1JauoipgPykO21qn04erq5pJkA0MsiuNRNQnMA==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false optional: true /@sentry/cli@2.33.0: @@ -7770,40 +7934,66 @@ packages: transitivePeerDependencies: - encoding - supports-color + dev: true - /@sentry/core@8.19.0: - resolution: {integrity: sha512-MrgjsZCEjOJgQjIznnDSrLEy7qL+4LVpNieAvr49cV1rzBNSwGmWRnt/puVaPsLyCUgupVx/43BPUHB/HtKNUw==} + /@sentry/cli@2.38.0: + resolution: {integrity: sha512-ld9+1GdPkDaFr6T4SGocxoMcrBB/K6Z37TvBx8IMrDQC+eJDkBFiyqmHnzrj/8xoj5O220pqjPZCfvqzH268sQ==} + engines: {node: '>= 10'} + hasBin: true + requiresBuild: true + dependencies: + https-proxy-agent: 5.0.1 + node-fetch: 2.7.0 + progress: 2.0.3 + proxy-from-env: 1.1.0 + which: 2.0.2 + optionalDependencies: + '@sentry/cli-darwin': 2.38.0 + '@sentry/cli-linux-arm': 2.38.0 + '@sentry/cli-linux-arm64': 2.38.0 + '@sentry/cli-linux-i686': 2.38.0 + '@sentry/cli-linux-x64': 2.38.0 + '@sentry/cli-win32-i686': 2.38.0 + '@sentry/cli-win32-x64': 2.38.0 + transitivePeerDependencies: + - encoding + - supports-color + dev: false + + /@sentry/core@8.35.0: + resolution: {integrity: sha512-Ci0Nmtw5ETWLqQJGY4dyF+iWh7PWKy6k303fCEoEmqj2czDrKJCp7yHBNV0XYbo00prj2ZTbCr6I7albYiyONA==} engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry/nextjs@8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0): - resolution: {integrity: sha512-WafL2zXKEp1jQJ0bC8H15zEUGT4m6bDiCwlaP8xAI3dz5E1e6f29OFlStvgzU3Tpx/Wi6qNTs5AGuwV3wK9qdg==} + /@sentry/nextjs@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(next@14.2.5)(react@18.2.0)(webpack@5.93.0): + resolution: {integrity: sha512-7V6Yd0llWvarebVhtK2UyIqkfw/BzKn/hQxJAob/FQ6V9wKFjF5W0EFtE2n/T0RCetL2JPF8iHu3/b4/TVREmg==} engines: {node: '>=14.18'} peerDependencies: next: ^13.2.0 || ^14.0 || ^15.0.0-rc.0 - webpack: '>= 5.0.0' + webpack: '>=5.0.0' peerDependenciesMeta: webpack: optional: true dependencies: - '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 - '@rollup/plugin-commonjs': 26.0.1(rollup@3.29.4) - '@sentry/core': 8.19.0 - '@sentry/node': 8.19.0 - '@sentry/opentelemetry': 8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.25.1) - '@sentry/react': 8.19.0(react@18.2.0) - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 - '@sentry/vercel-edge': 8.19.0 - '@sentry/webpack-plugin': 2.20.1(webpack@5.93.0) + '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.27.0 + '@rollup/plugin-commonjs': 26.0.1(rollup@3.29.5) + '@sentry-internal/browser-utils': 8.35.0 + '@sentry/core': 8.35.0 + '@sentry/node': 8.35.0 + '@sentry/opentelemetry': 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.27.0) + '@sentry/react': 8.35.0(react@18.2.0) + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 + '@sentry/vercel-edge': 8.35.0 + '@sentry/webpack-plugin': 2.22.3(webpack@5.93.0) chalk: 3.0.0 next: 14.2.5(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(@playwright/test@1.47.2)(react-dom@18.2.0)(react@18.2.0) resolve: 1.22.8 - rollup: 3.29.4 + rollup: 3.29.5 stacktrace-parser: 0.1.10 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: @@ -7816,147 +8006,133 @@ packages: - supports-color dev: false - /@sentry/node@8.19.0: - resolution: {integrity: sha512-r7AeKxfB9eE/UW0NZT3AMh+hNA65NFEwtsMYO6iI52FPLFZh0DLOvzVOeNsmsJqPpyetooUGTtUYpBdinZldWA==} + /@sentry/node@8.35.0: + resolution: {integrity: sha512-B0FLOcZEfYe3CJ2t0l1N0HJcHXcIrLlGENQ2kf5HqR2zcOcOzRxyITJTSV5brCnmzVNgkz9PG8VWo3w0HXZQpA==} engines: {node: '>=14.18'} dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/context-async-hooks': 1.25.1(@opentelemetry/api@1.9.0) '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-connect': 0.38.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-express': 0.41.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-fastify': 0.38.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-graphql': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-hapi': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-http': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-ioredis': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-koa': 0.42.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongodb': 0.46.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mongoose': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-mysql2': 0.40.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-nestjs-core': 0.39.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-pg': 0.43.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation-redis-4': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-amqplib': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-connect': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-dataloader': 0.12.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-express': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fastify': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-fs': 0.15.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-generic-pool': 0.39.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-graphql': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-hapi': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-http': 0.53.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-ioredis': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-kafkajs': 0.3.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-koa': 0.43.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-lru-memoizer': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongodb': 0.47.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mongoose': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-mysql2': 0.41.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-nestjs-core': 0.40.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-pg': 0.44.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-redis-4': 0.42.0(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation-undici': 0.6.0(@opentelemetry/api@1.9.0) '@opentelemetry/resources': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 - '@prisma/instrumentation': 5.17.0 - '@sentry/core': 8.19.0 - '@sentry/opentelemetry': 8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.27.0) - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 - optionalDependencies: - opentelemetry-instrumentation-fetch-node: 1.2.3(@opentelemetry/api@1.9.0) + '@prisma/instrumentation': 5.19.1 + '@sentry/core': 8.35.0 + '@sentry/opentelemetry': 8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.27.0) + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 + import-in-the-middle: 1.11.2 transitivePeerDependencies: - supports-color dev: false - /@sentry/opentelemetry@8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.25.1): - resolution: {integrity: sha512-L1aSxO/aJJ7D3pIlTaVOgbiZJAnUHXezobTc8j5pqFCQACjxnLMSDrt53QfFV52CcjbliDWCYe4IB8umu4DgpA==} + /@sentry/opentelemetry@8.35.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.53.0)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.27.0): + resolution: {integrity: sha512-2mWMpEiIFop/omia9BqTJa+0Khe+tSsiZSUrxbnSpxM0zgw8DFIzJMHbiqw/I7Qaluz9pnO2HZXqgUTwNPsU8A==} engines: {node: '>=14.18'} peerDependencies: '@opentelemetry/api': ^1.9.0 '@opentelemetry/core': ^1.25.1 - '@opentelemetry/instrumentation': ^0.52.1 - '@opentelemetry/sdk-trace-base': ^1.25.1 - '@opentelemetry/semantic-conventions': ^1.25.1 + '@opentelemetry/instrumentation': ^0.53.0 + '@opentelemetry/sdk-trace-base': ^1.26.0 + '@opentelemetry/semantic-conventions': ^1.27.0 dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) - '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.25.1 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 - dev: false - - /@sentry/opentelemetry@8.19.0(@opentelemetry/api@1.9.0)(@opentelemetry/core@1.26.0)(@opentelemetry/instrumentation@0.52.1)(@opentelemetry/sdk-trace-base@1.26.0)(@opentelemetry/semantic-conventions@1.27.0): - resolution: {integrity: sha512-L1aSxO/aJJ7D3pIlTaVOgbiZJAnUHXezobTc8j5pqFCQACjxnLMSDrt53QfFV52CcjbliDWCYe4IB8umu4DgpA==} - engines: {node: '>=14.18'} - peerDependencies: - '@opentelemetry/api': ^1.9.0 - '@opentelemetry/core': ^1.25.1 - '@opentelemetry/instrumentation': ^0.52.1 - '@opentelemetry/sdk-trace-base': ^1.25.1 - '@opentelemetry/semantic-conventions': ^1.25.1 - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.26.0(@opentelemetry/api@1.9.0) - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.53.0(@opentelemetry/api@1.9.0) '@opentelemetry/sdk-trace-base': 1.26.0(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.27.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry/react@8.19.0(react@18.2.0): - resolution: {integrity: sha512-MzuMy4AEdSuIrBEyp3W7c4+v215+2MiU9ba7Y0KBKcC/Nrf1cGfRFRbjl9OYm/JIuxkaop7kgYs6sPMrVJVlrQ==} + /@sentry/react@8.35.0(react@18.2.0): + resolution: {integrity: sha512-8Y+s4pE9hvT2TwSo5JS/Enw2cNFlwiLcJDNGCj/Hho+FePFYA59hbN06ouTHWARnO+swANHKZQj24Wp57p1/tg==} engines: {node: '>=14.18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x dependencies: - '@sentry/browser': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/browser': 8.35.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 hoist-non-react-statics: 3.3.2 react: 18.2.0 dev: false - /@sentry/types@8.19.0: - resolution: {integrity: sha512-52C8X5V7mK2KIxMJt8MV5TxXAFHqrQR1RKm1oPTwKVWm8hKr1ZYJXINymNrWvpAc3oVIKLC/sa9WFYgXQh+YlA==} + /@sentry/types@8.35.0: + resolution: {integrity: sha512-AVEZjb16MlYPifiDDvJ19dPQyDn0jlrtC1PHs6ZKO+Rzyz+2EX2BRdszvanqArldexPoU1p5Bn2w81XZNXThBA==} engines: {node: '>=14.18'} dev: false - /@sentry/utils@8.19.0: - resolution: {integrity: sha512-8dWJJKaUN6Hf92Oxw2TBmHchGua2W3ZmonrZTTwLvl06jcAigbiQD0MGuF5ytZP8PHx860orV+SbTGKFzfU3Pg==} + /@sentry/utils@8.35.0: + resolution: {integrity: sha512-MdMb6+uXjqND7qIPWhulubpSeHzia6HtxeJa8jYI09OCvIcmNGPydv/Gx/LZBwosfMHrLdTWcFH7Y7aCxrq7cg==} engines: {node: '>=14.18'} dependencies: - '@sentry/types': 8.19.0 + '@sentry/types': 8.35.0 dev: false - /@sentry/vercel-edge@8.19.0: - resolution: {integrity: sha512-I1G19SGWKcwUo1VT57xD/c/ZBnl8qkz6V+6j+vCbms4i0GTFw3eASnUIAOd25kc59/Wih2tUVj5mfV6aX5/DFg==} + /@sentry/vercel-edge@8.35.0: + resolution: {integrity: sha512-Wp5HCkBb6hA1oE4gETzi4laMsPsc7UBqKCMY4H/UOkuD6HzgpyWuHZeS6nrs2A3MJWcoNoFZ2sJD1hdo4apzGQ==} engines: {node: '>=14.18'} dependencies: - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.35.0 + '@sentry/types': 8.35.0 + '@sentry/utils': 8.35.0 dev: false - /@sentry/webpack-plugin@2.20.1(webpack@5.93.0): - resolution: {integrity: sha512-U6LzoE09Ndt0OCWROoRaZqqIHGxyMRdKpBhbqoBqyyfVwXN/zGW3I/cWZ1e8rreiKFj+2+c7+X0kOS+NGMTUrg==} + /@sentry/webpack-plugin@2.21.1(webpack@5.93.0): + resolution: {integrity: sha512-mhKWQq7/eC35qrhhD8oXm/37vZ1BQqmCD8dUngFIr4D24rc7dwlGwPGOYv59yiBqjTS0fGJ+o0xC5PTRKljGQQ==} engines: {node: '>= 14'} peerDependencies: webpack: '>=4.40.0' dependencies: - '@sentry/bundler-plugin-core': 2.20.1 + '@sentry/bundler-plugin-core': 2.21.1 unplugin: 1.0.1 uuid: 9.0.1 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - encoding - supports-color - dev: false + dev: true - /@sentry/webpack-plugin@2.21.1(webpack@5.93.0): - resolution: {integrity: sha512-mhKWQq7/eC35qrhhD8oXm/37vZ1BQqmCD8dUngFIr4D24rc7dwlGwPGOYv59yiBqjTS0fGJ+o0xC5PTRKljGQQ==} + /@sentry/webpack-plugin@2.22.3(webpack@5.93.0): + resolution: {integrity: sha512-Sq1S6bL3nuoTP5typkj+HPjQ13dqftIE8kACAq4tKkXOpWO9bf6HtqcruEQCxMekbWDTdljsrknQ17ZBx2q66Q==} engines: {node: '>= 14'} peerDependencies: webpack: '>=4.40.0' dependencies: - '@sentry/bundler-plugin-core': 2.21.1 + '@sentry/bundler-plugin-core': 2.22.3 unplugin: 1.0.1 uuid: 9.0.1 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - encoding - supports-color - dev: true + dev: false /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -9159,8 +9335,8 @@ packages: /@types/ms@0.7.33: resolution: {integrity: sha512-AuHIyzR5Hea7ij0P9q7vx7xu4z0C28ucwjAZC0ja7JhINyCnOw8/DnvAPQQ9TfOlCtZAmCERKQX9+o1mgQhuOQ==} - /@types/mysql@2.15.22: - resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==} + /@types/mysql@2.15.26: + resolution: {integrity: sha512-DSLCOXhkvfS5WNNPbfn2KdICAmk8lLc+/PNvnPnF7gOdMZCxopXduqv0OQ13y/yA/zXTSikZZqVgybUxOEg6YQ==} dependencies: '@types/node': 18.18.5 dev: false @@ -9197,8 +9373,8 @@ packages: resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} dev: true - /@types/pg-pool@2.0.4: - resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==} + /@types/pg-pool@2.0.6: + resolution: {integrity: sha512-TaAUE5rq2VQYxab5Ts7WZhKNmuN78Q6PiFonTDdpbx8a1H0M1vhy3rhiMjl+e2iHmogyMw7jZF4FrE6eJUy5HQ==} dependencies: '@types/pg': 8.6.1 dev: false @@ -9930,16 +10106,6 @@ packages: acorn-walk: 8.2.0 dev: false - /acorn-import-assertions@1.9.0(acorn@8.12.1): - resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==} - requiresBuild: true - peerDependencies: - acorn: ^8 - dependencies: - acorn: 8.12.1 - dev: false - optional: true - /acorn-import-attributes@1.9.5(acorn@8.12.1): resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -15155,16 +15321,14 @@ packages: module-details-from-path: 1.0.3 dev: false - /import-in-the-middle@1.7.1: - resolution: {integrity: sha512-1LrZPDtW+atAxH42S6288qyDFNQ2YCty+2mxEPRtfazH6Z5QwkaBSTS2ods7hnVJioF6rkRfNoA6A/MstpFXLg==} - requiresBuild: true + /import-in-the-middle@1.11.2: + resolution: {integrity: sha512-gK6Rr6EykBcc6cVWRSBR5TWf8nn6hZMYSRYqCcHa0l0d1fPK7JSYo6+Mlmck76jIX9aL/IZ71c06U2VpFwl1zA==} dependencies: acorn: 8.12.1 - acorn-import-assertions: 1.9.0(acorn@8.12.1) + acorn-import-attributes: 1.9.5(acorn@8.12.1) cjs-module-lexer: 1.3.1 module-details-from-path: 1.0.3 dev: false - optional: true /import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} @@ -18983,21 +19147,6 @@ packages: resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==} dev: false - /opentelemetry-instrumentation-fetch-node@1.2.3(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-Qb11T7KvoCevMaSeuamcLsAD+pZnavkhDnlVL0kRozfhl42dKG5Q3anUklAFKJZjY3twLR+BnRa6DlwwkIE/+A==} - engines: {node: '>18.0.0'} - requiresBuild: true - peerDependencies: - '@opentelemetry/api': ^1.6.0 - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.46.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.27.0 - transitivePeerDependencies: - - supports-color - dev: false - optional: true - /opentracing@0.14.7: resolution: {integrity: sha512-vz9iS7MJ5+Bp1URw8Khvdyw1H/hGvzHWlKQ7eRrQojSCDL1/SrWfrY9QebLw97n2deyRtzHRC3MkQfVNUCo91Q==} engines: {node: '>=0.10'} @@ -21000,8 +21149,8 @@ packages: inherits: 2.0.4 dev: true - /rollup@3.29.4: - resolution: {integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==} + /rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true optionalDependencies: diff --git a/turbo.json b/turbo.json index 7e3581d11..1410a1344 100644 --- a/turbo.json +++ b/turbo.json @@ -21,12 +21,6 @@ "interactive": false, "cache": false }, - "dev-turbo": { - "dependsOn": ["^db-generate"], - "persistent": true, - "interactive": false, - "cache": false - }, "build": { "cache": true, "dependsOn": ["^build", "^db-generate"], From a157b9a6fececed09f43b7cca3989edd5e636a59 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Mon, 28 Oct 2024 19:03:47 +0000 Subject: [PATCH 033/127] fix: use prisma generate --no-engine and reinstate prompts script (#282) --- package.json | 4 ++-- packages/core/package.json | 4 ++-- packages/core/src/models/promptVariants.ts | 4 ++-- packages/core/src/scripts/setupPrompts.ts | 24 ++++++++++++++++++++++ packages/db/package.json | 4 ++-- turbo.json | 20 +++++++++++++++++- 6 files changed, 51 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index c3d10e5f6..4262247d2 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,8 @@ "doppler:run:stg": "doppler run -c stg --silent", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "lint": "turbo lint", - "prompts": "turbo prompts --filter=core", - "prompts:dev": "turbo prompts:dev --filter=core", + "prompts": "turbo prompts", + "prompts:dev": "turbo prompts:dev", "sort-package-json": "sort-package-json \"package.json\" \"packages/*/package.json\" \"apps/*/package.json\"", "type-check": "turbo type-check", "node-version": "node -v", diff --git a/packages/core/package.json b/packages/core/package.json index 2739092ae..17b7892ac 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -23,9 +23,9 @@ "process:lessons": "pnpm doppler:run:dev -- ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/scripts/processLessons.ts", "process:lessons:dev": "pnpm doppler:run:dev -- pnpm process:lessons", "prompts": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/scripts/setupPrompts.ts", - "prompts:dev": "pnpm doppler:run:dev -- ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/scripts/setupPrompts.ts", + "prompts:dev": "dotenv -e ../../.env -- ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/scripts/setupPrompts.ts", "seed": "ts-node --compiler-options {\\\"module\\\":\\\"CommonJS\\\"} ./src/scripts/seedLessons.ts", - "seed:dev": "pnpm doppler:run:dev -- pnpm seed", + "seed:dev": "dotenv -e ../../.env -- pnpm seed", "seed:prd": "pnpm doppler:run:prd -- pnpm seed", "seed:stg": "pnpm doppler:run:stg -- pnpm seed", "type-check": "tsc --noEmit" diff --git a/packages/core/src/models/promptVariants.ts b/packages/core/src/models/promptVariants.ts index babd890b9..19cd12c0a 100644 --- a/packages/core/src/models/promptVariants.ts +++ b/packages/core/src/models/promptVariants.ts @@ -1,13 +1,13 @@ import { PrismaClientWithAccelerate } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import dedent from "ts-dedent"; import { Md5 } from "ts-md5"; import errorHandling from "../prompts/shared/error-handling"; import promptInjection from "../prompts/shared/prompt-injection"; import { OakPromptDefinition, OakPromptVariant } from "../prompts/types"; -import { aiLogger } from "@oakai/logger"; -const log = aiLogger("prompts") +const log = aiLogger("prompts"); export class PromptVariants { definition: OakPromptDefinition; diff --git a/packages/core/src/scripts/setupPrompts.ts b/packages/core/src/scripts/setupPrompts.ts index c29eddba3..1b8712f33 100644 --- a/packages/core/src/scripts/setupPrompts.ts +++ b/packages/core/src/scripts/setupPrompts.ts @@ -4,8 +4,32 @@ import { PromptVariants } from "../models/promptVariants"; import { lessonPlannerPrompts, quizGeneratorPrompts } from "../prompts"; import { ailaGenerate } from "../prompts/lesson-assistant/variants"; +export const apps = [ + { + name: "Quiz Generator", + slug: "quiz-generator", + id: "quiz-generator", + }, + { + name: "Lesson planner", + slug: "lesson-planner", + id: "lesson-planner", + }, +]; + const main = async () => { try { + // Ensure the apps exist in the database + await prisma.$transaction( + apps.map((app) => + prisma.app.upsert({ + where: { id: app.id }, + create: app, + update: app, + }), + ), + ); + console.log("Setting up prompts"); console.log("Aila"); for (const variant of ailaGenerate.variants) { diff --git a/packages/db/package.json b/packages/db/package.json index 8eebfbbe1..0ea847800 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -16,8 +16,8 @@ "db-export": "DB_ENV=dev doppler run --config dev -- bash scripts/import-export/db_export.sh", "db-export:prd": "DB_ENV=prd doppler run --config prd -- bash scripts/import-export/db_export.sh", "db-export:stg": "DB_ENV=stg doppler run --config stg -- bash scripts/import-export/db_export.sh", - "db-generate": "pnpm with-env prisma generate", - "db-generate:no-engine": "pnpm with-env prisma generate --no-engine", + "db-generate": "echo 'Running db-generate...' && pnpm with-env prisma generate --schema=./prisma/schema.prisma", + "db-generate:no-engine": "echo 'Running db-generate:no-engine...' && pnpm with-env prisma generate --no-engine --schema=./prisma/schema.prisma", "db-generate:dev": "pnpm with-env prisma generate", "db-migrate-deploy": "pnpm with-env prisma migrate deploy", "db-migrate": "pnpm with-env prisma migrate dev", diff --git a/turbo.json b/turbo.json index 1410a1344..812ab5d62 100644 --- a/turbo.json +++ b/turbo.json @@ -3,6 +3,10 @@ "ui": "tui", "tasks": { + "db-generate:no-engine": { + "inputs": ["prisma/schema.prisma"], + "cache": false + }, "db-generate": { "inputs": ["prisma/schema.prisma"], "cache": false @@ -21,9 +25,21 @@ "interactive": false, "cache": false }, + "build:dev": { + "cache": true, + "dependsOn": ["^db-generate", "^prompts:dev"], + "outputs": [ + ".next/**", + "!.next/cache/**", + "apps/nextjs/.next/**", + "apps/openai/.next/**", + "!apps/nextjs/.next/cache/**", + "!apps/openai/.next/cache/**" + ] + }, "build": { "cache": true, - "dependsOn": ["^build", "^db-generate"], + "dependsOn": ["^db-generate:no-engine", "^prompts"], "outputs": [ ".next/**", "!.next/cache/**", @@ -43,9 +59,11 @@ "cache": false }, "prompts": { + "dependsOn": ["^db-generate:no-engine"], "cache": false }, "prompts:dev": { + "dependsOn": ["^db-generate"], "cache": false }, "type-check": { From 647c90415c6ea5f71e75973caad627abe49bfd0c Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:06:39 +0100 Subject: [PATCH 034/127] fix: use browserLogger for errors in the browser (#280) --- packages/logger/index.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 4d3238a44..c59bf75f5 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -1,5 +1,6 @@ import debug from "debug"; +import browserLogger from "./browser"; import structuredLogger, { StructuredLogger } from "./structuredLogger"; if (typeof window !== "undefined") { @@ -53,6 +54,11 @@ type ChildKey = | "trpc" | "ui"; +const errorLogger = + typeof window === "undefined" + ? structuredLogger.error.bind(structuredLogger) + : browserLogger.error.bind(browserLogger); + /** * The AI logger uses namespaces so that we can selectively toggle noisy logs. * Logs are selected with the DEBUG environment variable. @@ -73,7 +79,7 @@ export function aiLogger(childKey: ChildKey) { return { info: debugLogger, warn: debugLogger, - error: structuredLogger.error.bind(structuredLogger), + error: errorLogger.bind(structuredLogger), }; } From 3ed678f40ac089b55f0283f723700490b9b09fdc Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:32:35 +0100 Subject: [PATCH 035/127] test: prevent jest from bypassing inngest dev mode (#278) --- packages/core/src/client.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts index b255c1b71..e41f819bc 100644 --- a/packages/core/src/client.ts +++ b/packages/core/src/client.ts @@ -45,5 +45,5 @@ export const inngest = new Inngest({ env: inngestEnv, logger: structuredLogger, middleware: [eventLogger(inngestEnv, inngestEventKey)], - isDev: process.env.NODE_ENV === "development" && !isJestEnvironment(), + isDev: process.env.NODE_ENV === "development" || isJestEnvironment(), }); From 290855b52dcc62a06b7bee9e650d8a9f7a8d31c5 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:32:49 +0100 Subject: [PATCH 036/127] test: reduce workers for jest in CI (#277) --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 245182dad..76f13d76e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -43,4 +43,4 @@ jobs: inject-env-vars: true - name: Run tests - run: pnpm turbo test --cache-dir=".turbo" + run: pnpm turbo test --cache-dir=".turbo" -- --maxWorkers=33% From 3a1bfd4df3d2489dda9387752f19c79b16e5da92 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 10:21:16 +0000 Subject: [PATCH 037/127] fix: use noengine in ci (#283) --- .github/workflows/test.yml | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 76f13d76e..cad1aa60e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,7 +33,7 @@ jobs: run: pnpm install - name: Generate prisma client - run: pnpm db-generate + run: pnpm db-generate:no-engine - name: Inject Doppler env vars uses: dopplerhq/secrets-fetch-action@v1.2.0 diff --git a/package.json b/package.json index 4262247d2..511836859 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "clean:workspaces": "turbo clean", "db-export:dev": "doppler run --config dev -- bash scripts/db_export.sh", "db-generate": "turbo db-generate", + "db-generate:no-engine": "turbo db-generate:no-engine", "db-migrate": "turbo db-migrate", "db-publish:stg": "doppler run --config stg -- bash scripts/db_publish.sh", "db-push": "turbo db-push", From 5aaa1d91146b92c1c449ecbf4725c61ec226ec87 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 10:28:15 +0000 Subject: [PATCH 038/127] feat: add an incrementing iteration number to the chat (#263) --- .vscode/settings.json | 1 + .../AppComponents/Chat/chat-lhs-header.tsx | 12 +- ...seTemporaryLessonPlanWithStreamingEdits.ts | 2 +- .../recordings/roman-britain-1.chunks.txt | 177 ++- .../recordings/roman-britain-1.formatted.json | 42 +- .../roman-britain-1.moderation.json | 17 +- .../recordings/roman-britain-2.chunks.txt | 517 +++++-- .../recordings/roman-britain-2.formatted.json | 100 +- .../roman-britain-2.moderation.json | 17 +- .../recordings/roman-britain-3.chunks.txt | 1294 +++++++++++++---- .../recordings/roman-britain-3.formatted.json | 325 ++++- .../roman-britain-3.moderation.json | 17 +- .../recordings/roman-britain-4.chunks.txt | 1237 ++++------------ .../recordings/roman-britain-4.formatted.json | 296 +--- .../roman-britain-4.moderation.json | 17 +- .../recordings/roman-britain-5.chunks.txt | 102 +- .../recordings/roman-britain-5.formatted.json | 20 +- .../roman-britain-5.moderation.json | 17 +- .../aila-chat/full-romans.mobile.test.ts | 27 +- .../tests/aila-chat/full-romans.test.ts | 79 +- .../tests-e2e/tests/aila-chat/helpers.ts | 76 +- packages/aila/src/core/Aila.test.ts | 19 +- packages/aila/src/core/Aila.ts | 7 + packages/aila/src/core/AilaServices.ts | 10 +- packages/aila/src/core/chat/AilaChat.ts | 62 +- .../aila/src/core/chat/AilaStreamHandler.ts | 6 + .../aila/src/core/chat/PatchEnqueuer.test.ts | 1 + packages/aila/src/core/chat/PatchEnqueuer.ts | 1 + packages/aila/src/core/lesson/AilaLesson.ts | 34 +- .../features/persistence/AilaPersistence.ts | 14 +- .../persistence/adaptors/prisma/index.ts | 19 +- .../aila/src/protocol/jsonPatchProtocol.ts | 138 +- packages/aila/src/protocol/schema.ts | 60 +- packages/logger/index.ts | 2 + 34 files changed, 2840 insertions(+), 1925 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f17db0427..23f2c7ab4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -143,6 +143,7 @@ "turborepo", "uidotdev", "unjudged", + "unsets", "unsummarised", "untruncate", "untruncated", diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index f8b69e2f7..d0acd9c16 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -20,16 +20,14 @@ const ChatLhsHeader = ({ }: Readonly) => { const router = useRouter(); const chat = useLessonChat(); - return ( <>
- {process.env.NEXT_PUBLIC_ENVIRONMENT !== "prd" && ( -
- {chat.ailaStreamingStatus} + {process.env.NEXT_PUBLIC_ENVIRONMENT !== "production" && ( +
+
+ {chat.ailaStreamingStatus} +
)} { }; function extractPatchesFromMessage(message: Message) { - const { validPatches, partialPatches } = extractPatches(message.content, 100); + const { validPatches, partialPatches } = extractPatches(message.content); return { validPatches, partialPatches }; } diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-1.chunks.txt b/apps/nextjs/tests-e2e/recordings/roman-britain-1.chunks.txt index 125f54791..3bf050761 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-1.chunks.txt +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-1.chunks.txt @@ -3,52 +3,169 @@ type":" ll m Message -","patches":[] -,"prompt":{" +","sectionsToEdit":[" +learning +Outcome +"," +learning +Cycles +"],"patches":[ +{" type":" -text -","value":" -There +patch +","reasoning":" +I + have + created + new + learning + outcomes + and + learning + cycles + from + scratch + as + there are no existing Oak lessons + available for - the - specific + this topic - of - \" -End - of - Roman +."," +value":{" +type":" +string +","op":" +add +","path":"/ +learning +Outcome +","value":" +I + can + describe + why + the + Romans + left Britain -,\" - so - we'll - start - a - new + and + what + happened + afterwards +." +},"status":" +complete +"},{" +type":" +patch +","reasoning":" +This + patch + adds + learning + cycles + which + break + down + the lesson - from - scratch + into + achievable + tasks +, + focusing + on + Roman + departure + and + its + impact + on + Britain +."," +value":{" +type":" +string +-array +","op":" +add +","path":"/ +learning +Cycles +","value":[" +Explain + the + reasons + for + the + Romans + leaving + Britain +."," +Describe + the + changes + that + occurred + in + Britain + after + the + Romans + left +."," +Identify + the + role + of + archae +ologists + in + uncover +ing + Roman + history . - Let's - begin - by - outlining +"]},"status":" +complete +"}],"sectionsEdited":[" +learning +Outcome +"," +learning +Cycles +"],"prompt":{" +type":" +text +","value":" +Are the learning - outcomes + outcome and learning cycles + appropriate for - the - lesson + your + pupils +? + If + not +, + suggest + an + edit . - Tap + Otherwise +, + tap ** Continue ** @@ -60,4 +177,6 @@ Continue next step ." -}} \ No newline at end of file +},"status":" +complete +"} \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-1.formatted.json b/apps/nextjs/tests-e2e/recordings/roman-britain-1.formatted.json index 74d623c61..0a981311f 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-1.formatted.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-1.formatted.json @@ -1,8 +1,44 @@ { "type": "llmMessage", - "patches": [], + "sectionsToEdit": [ + "learningOutcome", + "learningCycles" + ], + "patches": [ + { + "type": "patch", + "reasoning": "I have created new learning outcomes and learning cycles from scratch as there are no existing Oak lessons available for this topic.", + "value": { + "type": "string", + "op": "add", + "path": "/learningOutcome", + "value": "I can describe why the Romans left Britain and what happened afterwards." + }, + "status": "complete" + }, + { + "type": "patch", + "reasoning": "This patch adds learning cycles which break down the lesson into achievable tasks, focusing on Roman departure and its impact on Britain.", + "value": { + "type": "string-array", + "op": "add", + "path": "/learningCycles", + "value": [ + "Explain the reasons for the Romans leaving Britain.", + "Describe the changes that occurred in Britain after the Romans left.", + "Identify the role of archaeologists in uncovering Roman history." + ] + }, + "status": "complete" + } + ], + "sectionsEdited": [ + "learningOutcome", + "learningCycles" + ], "prompt": { "type": "text", - "value": "There are no existing Oak lessons for the specific topic of \"End of Roman Britain,\" so we'll start a new lesson from scratch. Let's begin by outlining the learning outcomes and learning cycles for the lesson. Tap **Continue** to move on to the next step." - } + "value": "Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit. Otherwise, tap **Continue** to move on to the next step." + }, + "status": "complete" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-1.moderation.json b/apps/nextjs/tests-e2e/recordings/roman-britain-1.moderation.json index 44c0a6449..91cc3b3de 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-1.moderation.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-1.moderation.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-AAdBxHbh94auX6oHYcBB7BNt9CWkY", + "id": "chatcmpl-ANdWlVyt9QK14JTGZfiB9u3p3lQxr", "object": "chat.completion", - "created": 1727097297, + "created": 1730196851, "model": "gpt-4o-2024-08-06", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan on 'End of Roman Britain' is appropriate for key-stage-1 students. It does not contain any discriminatory language or behavior, offensive or strong language, depictions of conflict, violence, or sensitive topics. The content is historical and educational, focusing on the end of Roman rule in Britain without delving into upsetting, sensitive, or distressing details. There is no discussion of nudity, sexual content, or any physical activities involving exploration or equipment usage that require supervision. Additionally, the lesson plan does not include any toxic or harmful instructions or encouragements. Overall, the lesson is fully compliant with content guidelines for young learners.\",\"categories\":[]}", + "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":4,\"t\":5},\"justification\":\"The lesson plan is compliant in the categories of 'Language and discrimination', 'Violence and crime', 'Upsetting, disturbing and sensitive', 'Nudity and sex', and 'Toxic', as it does not contain any inappropriate content or discussion for these categories. However, the 'Physical activity and safety' category is rated a 4 because the topic of archaeology may involve exploration of objects or sites, which can require supervision or safety precautions, particularly for the key-stage 1 age group. Although this is minimal, it should be noted for safety purposes.\",\"categories\":[\"p/exploration-of-objects\"]}", "refusal": null }, "logprobs": null, @@ -16,12 +16,15 @@ } ], "usage": { - "prompt_tokens": 2234, - "completion_tokens": 162, - "total_tokens": 2396, + "prompt_tokens": 2283, + "completion_tokens": 154, + "total_tokens": 2437, + "prompt_tokens_details": { + "cached_tokens": 2048 + }, "completion_tokens_details": { "reasoning_tokens": 0 } }, - "system_fingerprint": "fp_7568d46099" + "system_fingerprint": "fp_90354628f2" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-2.chunks.txt b/apps/nextjs/tests-e2e/recordings/roman-britain-2.chunks.txt index 3e223820b..481f2de6f 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-2.chunks.txt +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-2.chunks.txt @@ -3,76 +3,125 @@ type":" ll m Message -","patches":[ +","sectionsToEdit":[" +prior +Knowledge +"," +key +Learning +Points +"," +mis +con +ceptions +"," +keywords +"],"patches":[ {" type":" patch ","reasoning":" -Est -ablish -ing - a - clear - learning - outcome - to - focus - the +Ident +ified + prior + knowledge + that + KS +1 + pupils + should + have + before + starting + this lesson on the - specific - impact - of - the end of Roman - rule - in Britain ."," value":{" type":" string +-array ","op":" add ","path":"/ -learning -Outcome -","value":" -I - can - describe - the - changes - in +prior +Knowledge +","value":[" +The + Romans + invaded Britain - after + and + ruled + for + several + centuries +."," +Roman + roads + and + towns + were + built + during + their + occupation +."," +Brit +ain + was + part + of the - Romans - left -." -}},{" + Roman + Empire +."," +The + concept + of + an + empire + and + its + importance +."," +Basic + understanding + of + what + archae +ologists + do +. +"]},"status":" +complete +"},{" type":" patch ","reasoning":" -Def -ining +Outlined + key learning - cycles - to - structure + points + that + pupils + should + take + away + from the lesson - effectively -, - increasing - complexity - as + on the - lesson - progresses + end + of + Roman + Britain ."," value":{" type":" @@ -81,82 +130,368 @@ string ","op":" add ","path":"/ -learning -Cycles +key +Learning +Points ","value":[" -Identify - key - reasons - why - the +The Romans left Britain + around + AD + +410 + due + to + pressures + on + their + empire ."," -Describe +After the - immediate - effects - on - local - communities + Romans + left +, + Britain + faced + invas +ions + from + other + groups + like + the + Sax +ons ."," -Explain - how +Many + Roman + buildings + and + roads + influenced + the + landscape + of Britain - changed - in +."," +Ar +chae +ologists + help + uncover the - years - following + history + of Roman - departure + Britain + through + excav +ations . -"]}}],"prompt":{" +"]},"status":" +complete +"},{" type":" -text -","value":" -I've - set +patch +","reasoning":" +Address +ed + common + misconceptions + about the - learning - outcome + end + of + Roman + Britain to - focus - on - describing + help + teachers + correct + them + during the - changes - in + lesson +."," +value":{" +type":" +mis +con +ceptions +","op":" +add +","path":"/ +mis +con +ceptions +","value":[ +{" +misconception":" +The + Romans + left Britain - after + because + they + were + defeated + by the + Brit +ons +","description":" +The Romans left + due + to + internal + problems + within + the + empire and - outlined + threats + elsewhere +, + not + because + they + were + defeated + by the - learning - cycles -. - Are + Brit +ons +." +},{" +misconception":" +Roman + Britain + ended + suddenly + without + any + lasting + impact +","description":" +Roman + influence + persisted + in + Britain's + infrastructure +, + such + as + roads + and + towns +, + even + after + their + departure +." +}]},"status":" +complete +"},{" +type":" +patch +","reasoning":" +Provided + keywords + that + will + be + used + throughout the - learning - outcome + lesson + to + ensure + pupils + understand + the + key + concepts +."," +value":{" +type":" +keywords +","op":" +add +","path":"/ +keywords +","value":[ +{" +keyword":" +Romans +","definition":" +People + from + the + ancient + city + of + Rome + who + ruled + over + Britain + for + several + centuries +." +},{" +keyword":" +Empire +","definition":" +A + large + group + of + territories + and + people + ruled + over + by + a + single + authority +, + like + the + Roman + Empire +." +},{" +keyword":" +Ar +chae +ologist +","definition":" +A + person + who + studies + human + history + by + digging + up and + examining + ancient + sites + and + objects +." +},{" +keyword":" +In +vasion +","definition":" +An + instance + of + invading + a + country + or + region + with + an + armed + force +." +},{" +keyword":" +S +ax +ons +","definition":" +A + group + of + tribes + from + northern + Europe + who + invaded + Britain + after + the + Romans + left +." +}]},"status":" +complete +"}],"sectionsEdited":[" +prior +Knowledge +"," +key +Learning +Points +"," +mis +con +ceptions +"," +keywords +"],"prompt":{" +type":" +text +","value":" +Are + the + prior + knowledge +, + key learning - cycles - appropriate + points +, + misconceptions +, + and + keywords + sections + suitable for your - pupils + class ? If not , - suggest - an - edit - below + reply + with + what + I + should + change +. + Otherwise +, + tap + ** +Continue +** + to + move + on + to + the + next + step ." -}} \ No newline at end of file +},"status":" +complete +"} \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-2.formatted.json b/apps/nextjs/tests-e2e/recordings/roman-britain-2.formatted.json index becabd619..617fb17e0 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-2.formatted.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-2.formatted.json @@ -1,33 +1,107 @@ { "type": "llmMessage", + "sectionsToEdit": [ + "priorKnowledge", + "keyLearningPoints", + "misconceptions", + "keywords" + ], "patches": [ { "type": "patch", - "reasoning": "Establishing a clear learning outcome to focus the lesson on the specific impact of the end of Roman rule in Britain.", + "reasoning": "Identified prior knowledge that KS1 pupils should have before starting this lesson on the end of Roman Britain.", "value": { - "type": "string", + "type": "string-array", "op": "add", - "path": "/learningOutcome", - "value": "I can describe the changes in Britain after the Romans left." - } + "path": "/priorKnowledge", + "value": [ + "The Romans invaded Britain and ruled for several centuries.", + "Roman roads and towns were built during their occupation.", + "Britain was part of the Roman Empire.", + "The concept of an empire and its importance.", + "Basic understanding of what archaeologists do." + ] + }, + "status": "complete" }, { "type": "patch", - "reasoning": "Defining learning cycles to structure the lesson effectively, increasing complexity as the lesson progresses.", + "reasoning": "Outlined key learning points that pupils should take away from the lesson on the end of Roman Britain.", "value": { "type": "string-array", "op": "add", - "path": "/learningCycles", + "path": "/keyLearningPoints", "value": [ - "Identify key reasons why the Romans left Britain.", - "Describe the immediate effects on local communities.", - "Explain how Britain changed in the years following Roman departure." + "The Romans left Britain around AD 410 due to pressures on their empire.", + "After the Romans left, Britain faced invasions from other groups like the Saxons.", + "Many Roman buildings and roads influenced the landscape of Britain.", + "Archaeologists help uncover the history of Roman Britain through excavations." ] - } + }, + "status": "complete" + }, + { + "type": "patch", + "reasoning": "Addressed common misconceptions about the end of Roman Britain to help teachers correct them during the lesson.", + "value": { + "type": "misconceptions", + "op": "add", + "path": "/misconceptions", + "value": [ + { + "misconception": "The Romans left Britain because they were defeated by the Britons", + "description": "The Romans left due to internal problems within the empire and threats elsewhere, not because they were defeated by the Britons." + }, + { + "misconception": "Roman Britain ended suddenly without any lasting impact", + "description": "Roman influence persisted in Britain's infrastructure, such as roads and towns, even after their departure." + } + ] + }, + "status": "complete" + }, + { + "type": "patch", + "reasoning": "Provided keywords that will be used throughout the lesson to ensure pupils understand the key concepts.", + "value": { + "type": "keywords", + "op": "add", + "path": "/keywords", + "value": [ + { + "keyword": "Romans", + "definition": "People from the ancient city of Rome who ruled over Britain for several centuries." + }, + { + "keyword": "Empire", + "definition": "A large group of territories and people ruled over by a single authority, like the Roman Empire." + }, + { + "keyword": "Archaeologist", + "definition": "A person who studies human history by digging up and examining ancient sites and objects." + }, + { + "keyword": "Invasion", + "definition": "An instance of invading a country or region with an armed force." + }, + { + "keyword": "Saxons", + "definition": "A group of tribes from northern Europe who invaded Britain after the Romans left." + } + ] + }, + "status": "complete" } ], + "sectionsEdited": [ + "priorKnowledge", + "keyLearningPoints", + "misconceptions", + "keywords" + ], "prompt": { "type": "text", - "value": "I've set the learning outcome to focus on describing the changes in Britain after the Romans left and outlined the learning cycles. Are the learning outcome and learning cycles appropriate for your pupils? If not, suggest an edit below." - } + "value": "Are the prior knowledge, key learning points, misconceptions, and keywords sections suitable for your class? If not, reply with what I should change. Otherwise, tap **Continue** to move on to the next step." + }, + "status": "complete" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-2.moderation.json b/apps/nextjs/tests-e2e/recordings/roman-britain-2.moderation.json index a0e65531b..171cc2eed 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-2.moderation.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-2.moderation.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-AAdCA5XfaGmUbEN7TkuDd6BEkXKK2", + "id": "chatcmpl-ANdX7W6LJidRv2NxImpbuB49osq2h", "object": "chat.completion", - "created": 1727097310, + "created": 1730196873, "model": "gpt-4o-2024-08-06", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan titled 'End of Roman Britain' is fully compliant across all categories for a Key Stage 1 audience. It focuses on historical events related to the end of Roman rule in Britain and its effects. There is no use of discriminatory language or behavior, no depiction or discussion of violence or sensitive topics, no nudity or sexual content, no physical activities that require safety considerations, and no toxic content or encouragement of harmful behavior. The content is appropriate for young learners and is presented in a factual and educational manner without any elements that could be considered inappropriate or harmful.\",\"categories\":[]}", + "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan on the 'End of Roman Britain' is fully compliant across all categories. It is designed for Key Stage 1 (ages 5-7) and focuses on historical facts about the Romans leaving Britain, without any inclusion of discriminatory language, offensive or strong language. There is no mention of violence, crime, or any upsetting, sensitive, or distressing content. The lesson does not include any depiction or discussion of nudity or sexual content. Additionally, there is no physical activity or safety concern involved, and no external content or use of equipment requiring supervision is mentioned. Finally, the lesson plan does not contain any toxic elements like encouragement of harmful behavior or instructions on creating weapons. All content is appropriate for the intended young audience and is presented in an educational and age-appropriate manner.\",\"categories\":[]}", "refusal": null }, "logprobs": null, @@ -16,12 +16,15 @@ } ], "usage": { - "prompt_tokens": 2282, - "completion_tokens": 147, - "total_tokens": 2429, + "prompt_tokens": 2600, + "completion_tokens": 193, + "total_tokens": 2793, + "prompt_tokens_details": { + "cached_tokens": 2048 + }, "completion_tokens_details": { "reasoning_tokens": 0 } }, - "system_fingerprint": "fp_7568d46099" + "system_fingerprint": "fp_90354628f2" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-3.chunks.txt b/apps/nextjs/tests-e2e/recordings/roman-britain-3.chunks.txt index a975baaf9..0bea461fd 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-3.chunks.txt +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-3.chunks.txt @@ -3,356 +3,1128 @@ type":" ll m Message -","patches":[ +","sectionsToEdit":[" +starter +Quiz +"," +cycle +1 +"," +cycle +2 +"," +cycle +3 +"," +exit +Quiz +"],"patches":[ {" type":" patch ","reasoning":" -Est -ablish -ing - key +Designed + a + starter + quiz + to + assess + pupils +' prior knowledge + on + Roman + Britain + before + the + main + lesson + content +."," +value":{" +type":" +quiz +","op":" +add +","path":"/ +starter +Quiz +","value":[ +{" +question":" +What + did + the + Romans + build + in + Britain + during + their + rule +? +","answers":[" +Road +s + and + towns +"],"distractors":[" +Cast +les + and + forts +"," +Schools + and + hospitals +"]},{" +question":" +How + long + did + the + Romans + rule + over + Britain +? +","answers":[" +Several + centuries +"],"distractors":[" +A + few + years +"," +A + decade +"]},{" +question":" +What + is + an + empire +? +","answers":[" +A + large + group + of + territories + ruled + by + one + authority +"],"distractors":[" +A + small + country +"," +A + type + of + government +"]},{" +question":" +What + role + do + archae +ologists + play + in + history +? +","answers":[" +They + study + human + history + by + digging + up + ancient + sites +"],"distractors":[" +They + write + history + books +"," +They + teach + history + in + schools +"]},{" +question":" +Which + empire + did + Britain + belong to - ensure + during + Roman + rule +? +","answers":[" +The + Roman + Empire +"],"distractors":[" +The + Greek + Empire +"," +The + Egyptian + Empire +"]},{" +question":" +What + is + one + thing pupils - have - the - foundational - understanding - necessary - for + should + understand + before this lesson +? +","answers":[" +The + concept + of + an + empire +"],"distractors":[" +The + history + of + the + Sax +ons +"," +The + fall + of + the + Roman + Empire +"]}]},"status":" +complete +"},{" +type":" +patch +","reasoning":" +Develop +ed + the + first + learning + cycle + focusing + on + explaining + why + the + Romans + left + Britain ."," value":{" type":" -string --array +cycle ","op":" add ","path":"/ -prior -Knowledge -","value":[" -Romans - invaded +cycle +1 +","value":{" +title":" +Why + the + Romans + Left Britain - in - -43 +","durationInMinutes":15 +,"explanation":{" +spokenExplanation":[" +Discuss + the + pressures + faced + by + the + Roman + Empire + around AD + +410 ."," -Roman - roads - connected - towns - across +Explain + how + internal + conflicts + weakened + the + Roman + control + over + distant + regions + like Britain ."," -Romans - built - Had -rian -'s - Wall - in +Describe the - north + external + threats + from + other + tribes + and + groups + that + required + Roman + attention + elsewhere +."," +Mention + how + resources + and + troops + were + needed + more + urgently + in + other + parts of + the + empire +."," +Highlight + that + the + decision + to + leave + was + strategic +, + not + due + to + defeat + by + Brit +ons +." +],"accompanyingSlideDetails":" +A + timeline + showing + key + events + leading + to + the + Roman + departure + from Britain ."," -Romans - introduced - new - food +imagePrompt":" +Roman + Empire + timeline + AD + +410 +","slideText":" +The + Romans + left + Britain + around + AD + +410 + due + to + internal and - customs + external + pressures + on + their + empire +." +},"checkForUnderstanding":[ +{" +question":" +Why + did + the + Romans + decide to + leave Britain -."," -Some +? +","answers":[" +Due + to + internal + and + external + pressures +"],"distractors":[" +They + were + defeated + by Brit ons - adopted +"," +They + found + new + lands +"]},{" +question":" +What + was + a + major + factor + in + the Roman - ways - of - life + departure + from + Britain +? +","answers":[" +Resource + needs + elsewhere +"],"distractors":[" +British + rebellion +"," +New + Roman + emperor +"]}],"practice":" +List + two + reasons + why + the + Romans + left + Britain + and + explain + how + these + reasons + impacted + their + empire +."," +feedback":" +Model + answer +: + The + Romans + left + due + to + internal + conflicts + and + external + threats . -"]}},{" + These + weakened + their + control +, + requiring + resources + elsewhere +." +}},"status":" +complete +"},{" type":" patch ","reasoning":" -Ident -ifying - key +Created + the + second learning - points + cycle to - ensure - the - lesson - covers - the - most - important - aspects - of - the - topic + describe + changes + in + Britain + post +-R +om +ans ."," value":{" type":" -string --array +cycle ","op":" add ","path":"/ -key -Learning -Points -","value":[" -The +cycle +2 +","value":{" +title":" +Changes + in + Post +-R +oman + Britain +","durationInMinutes":15 +,"explanation":{" +spokenExplanation":[" +Explain + how + the + departure + of Romans - left + led + to + power + vac +u +ums + in Britain - around - -410 - AD ."," +Discuss + the + arrival + of + the + Sax +ons + and + other + groups + invading + Britain +."," +Describe + the + changes + in + town + structures + and + the + decline + of + Roman +-built + infrastructure +."," +Highlight + the + blend + of + Roman + and + new + cultural + influences + in + Britain +."," +Show + examples + of + Roman + architecture + that + remained + influential +." +],"accompanyingSlideDetails":" +Images + showing + Roman + roads + and + Sax +on + settlements +."," +imagePrompt":" Roman - withdrawal + roads + and + Sax +on + settlements +","slideText":" +After + the + Romans left +, Britain - vulnerable - to + faced invas ions -."," -Many + and + cultural + changes + with + enduring Roman - structures - fell - into - dis -repair -."," -Local - communities - had + influences +." +},"checkForUnderstanding":[ +{" +question":" +What + happened to - adapt - without + Britain + after + the + Romans + left +? +","answers":[" +It + faced + invas +ions + from + other + groups +"],"distractors":[" +It + became + a Roman - support + province +"," +It + remained + peaceful +"]},{" +question":" +Which + group + invaded + Britain + after + the + Romans +? +","answers":[" +The + Sax +ons +"],"distractors":[" +The + Greeks +"," +The + Norm +ans +"]}],"practice":" +Draw + a + picture + showing + a + Roman + road +. + Label + it + and + describe + its + importance + in + post +-R +oman + Britain ."," -C -ultural +feedback":" +Model + answer +: + Roman + roads + remained + important + for + trade and - technological - changes - occurred + travel + in post -R oman - rule -. -"]}},{" + Britain +, + influencing + settlement + patterns +." +}},"status":" +complete +"},{" type":" patch ","reasoning":" -Address -ing - common - misconceptions - to - ensure - they - are - clarified - in +Form +ulated the - lesson + third + learning + cycle + focusing + on + archaeological + discoveries + of + Roman + Britain ."," value":{" type":" -mis -con -ceptions +cycle ","op":" add ","path":"/ -mis -con -ceptions -","value":[ -{" -misconception":" -Romans - were - forced - out +cycle +3 +","value":{" +title":" +Ar +chae +ologists + and + Roman + Britain +","durationInMinutes":15 +,"explanation":{" +spokenExplanation":[" +Define + the + role of + archae +ologists + in + studying + history +."," +Explain + how + archae +ologists + uncover + Roman + artifacts + through + excav +ations +."," +Discuss + the + types + of + artifacts + commonly + found + from + Roman Britain - by - barb -arians -","description":" -The - Romans - left +."," +Describe + how + these + discoveries + help + us + understand + Roman + life + and + influence +."," +Mention + key + archaeological + sites + in Britain - mainly - due + linked to - internal - issues - in the + Romans +." +],"accompanyingSlideDetails":" +Images + of Roman - Empire -. - This - can - be - clarified - by - explaining - the + artifacts + and + archaeological + sites + in + Britain +."," +imagePrompt":" +Roman + artifacts + from + Britain +","slideText":" +Ar +chae +ologists + uncover Roman - Empire -'s - struggles -." -},{" -misconception":" -Brit -ain - became - immediately - barbar -ic - after - Romans - left -","description":" -Although - there - were - challenges + history + through + excav +ations , + revealing + insights + into + life + in + Roman Britain - continued - to - have - structured - communities +." +},"checkForUnderstanding":[ +{" +question":" +What + do + archae +ologists + do +? +","answers":[" +Study + history + through + excav +ations +"],"distractors":[" +Write + history + books +"," +Teach + in + schools +"]},{" +question":" +Why + are + Roman + artifacts + important +? +","answers":[" +They + help + us + understand + Roman + life +"],"distractors":[" +They + are + decorative +"," +They + are + made + of + gold +"]}],"practice":" +Match + pictures + of + Roman + artifacts + with + their + descriptions and - slowly - adapted - without + explain + their + significance + in + Roman + history +."," +feedback":" +Model + answer +: Roman - governance + coins +, + pottery +, + and + tools + reveal + daily + life + and + trade + practices + in + Roman + Britain ." -}]}},{" +}},"status":" +complete +"},{" type":" patch ","reasoning":" -Including - keywords +Designed + an + exit + quiz to - bolster - vocabulary - comprehension - for + assess + pupils +' + understanding + of the lesson +'s + key + concepts ."," value":{" type":" -keywords +quiz ","op":" add ","path":"/ -keywords +exit +Quiz ","value":[ {" -keyword":" -Roman - Empire -","definition":" -The - vast +question":" +When + did + the + Romans + leave + Britain +? +","answers":[" +Around + AD + +410 +"],"distractors":[" +Around + AD + +300 +"," +Around + AD + +500 +"]},{" +question":" +What + was + one + reason + for + the + Roman + departure +? +","answers":[" +Internal empire - ruled + pressures +"],"distractors":[" +Def +eat by - Rome - that - included + Brit +ons +"," +L +ack + of + interest + in Britain - for - nearly - -400 - years -." -},{" -keyword":" -Withdrawal -","definition":" +"]},{" +question":" +Who + invaded + Britain + after + the + Romans + left +? +","answers":[" The - act - of - pulling - out - or - leaving - a - place - or - position -." -},{" -keyword":" -Inv -aders -","definition":" -People - or - groups - that - enter - a - region - to - take - control -." -}]}}],"prompt":{" + Sax +ons +"],"distractors":[" +The + Norm +ans +"," +The + Vikings +"]},{" +question":" +What + did + archae +ologists + find + in + Roman + Britain +? +","answers":[" +Artifacts + like + coins + and + pottery +"],"distractors":[" +Modern + buildings +"," +Anc +ient + books +"]},{" +question":" +Why + are + Roman + roads + significant +? +","answers":[" +They + influenced + travel + and + trade +"],"distractors":[" +They + were + all + destroyed +"," +They + were + only + decorative +"]},{" +question":" +What + role + do + archae +ologists + play + in + history +? +","answers":[" +They + uncover + past + through + excav +ations +"],"distractors":[" +They + build + new + sites +"," +They + rule + ancient + lands +"]}]},"status":" +complete +"}],"sectionsEdited":[" +starter +Quiz +"," +cycle +1 +"," +cycle +2 +"," +cycle +3 +"," +exit +Quiz +"],"prompt":{" type":" text ","value":" -The - prior - knowledge +Are + the + starter + quiz , - key learning - points -, - misconceptions + cycles , and - keywords - have - been - set -. - Are - these + exit + quiz sections suitable for @@ -360,13 +1132,29 @@ The class ? If - you - need - any - changes + not +, + reply + with + what + I + should + change +. + Otherwise , - let - me - know -!" -}} \ No newline at end of file + tap + ** +Continue +** + to + move + on + to + the + next + step +." +},"status":" +complete +"} \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-3.formatted.json b/apps/nextjs/tests-e2e/recordings/roman-britain-3.formatted.json index d7e4fd886..82d9720d8 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-3.formatted.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-3.formatted.json @@ -1,83 +1,318 @@ { "type": "llmMessage", + "sectionsToEdit": [ + "starterQuiz", + "cycle1", + "cycle2", + "cycle3", + "exitQuiz" + ], "patches": [ { "type": "patch", - "reasoning": "Establishing key prior knowledge to ensure pupils have the foundational understanding necessary for this lesson.", + "reasoning": "Designed a starter quiz to assess pupils' prior knowledge on Roman Britain before the main lesson content.", "value": { - "type": "string-array", + "type": "quiz", "op": "add", - "path": "/priorKnowledge", + "path": "/starterQuiz", "value": [ - "Romans invaded Britain in 43 AD.", - "Roman roads connected towns across Britain.", - "Romans built Hadrian's Wall in the north of Britain.", - "Romans introduced new food and customs to Britain.", - "Some Britons adopted Roman ways of life." + { + "question": "What did the Romans build in Britain during their rule?", + "answers": [ + "Roads and towns" + ], + "distractors": [ + "Castles and forts", + "Schools and hospitals" + ] + }, + { + "question": "How long did the Romans rule over Britain?", + "answers": [ + "Several centuries" + ], + "distractors": [ + "A few years", + "A decade" + ] + }, + { + "question": "What is an empire?", + "answers": [ + "A large group of territories ruled by one authority" + ], + "distractors": [ + "A small country", + "A type of government" + ] + }, + { + "question": "What role do archaeologists play in history?", + "answers": [ + "They study human history by digging up ancient sites" + ], + "distractors": [ + "They write history books", + "They teach history in schools" + ] + }, + { + "question": "Which empire did Britain belong to during Roman rule?", + "answers": [ + "The Roman Empire" + ], + "distractors": [ + "The Greek Empire", + "The Egyptian Empire" + ] + }, + { + "question": "What is one thing pupils should understand before this lesson?", + "answers": [ + "The concept of an empire" + ], + "distractors": [ + "The history of the Saxons", + "The fall of the Roman Empire" + ] + } ] - } + }, + "status": "complete" }, { "type": "patch", - "reasoning": "Identifying key learning points to ensure the lesson covers the most important aspects of the topic.", + "reasoning": "Developed the first learning cycle focusing on explaining why the Romans left Britain.", "value": { - "type": "string-array", + "type": "cycle", "op": "add", - "path": "/keyLearningPoints", - "value": [ - "The Romans left Britain around 410 AD.", - "Roman withdrawal left Britain vulnerable to invasions.", - "Many Roman structures fell into disrepair.", - "Local communities had to adapt without Roman support.", - "Cultural and technological changes occurred post-Roman rule." - ] - } + "path": "/cycle1", + "value": { + "title": "Why the Romans Left Britain", + "durationInMinutes": 15, + "explanation": { + "spokenExplanation": [ + "Discuss the pressures faced by the Roman Empire around AD 410.", + "Explain how internal conflicts weakened the Roman control over distant regions like Britain.", + "Describe the external threats from other tribes and groups that required Roman attention elsewhere.", + "Mention how resources and troops were needed more urgently in other parts of the empire.", + "Highlight that the decision to leave was strategic, not due to defeat by Britons." + ], + "accompanyingSlideDetails": "A timeline showing key events leading to the Roman departure from Britain.", + "imagePrompt": "Roman Empire timeline AD 410", + "slideText": "The Romans left Britain around AD 410 due to internal and external pressures on their empire." + }, + "checkForUnderstanding": [ + { + "question": "Why did the Romans decide to leave Britain?", + "answers": [ + "Due to internal and external pressures" + ], + "distractors": [ + "They were defeated by Britons", + "They found new lands" + ] + }, + { + "question": "What was a major factor in the Roman departure from Britain?", + "answers": [ + "Resource needs elsewhere" + ], + "distractors": [ + "British rebellion", + "New Roman emperor" + ] + } + ], + "practice": "List two reasons why the Romans left Britain and explain how these reasons impacted their empire.", + "feedback": "Model answer: The Romans left due to internal conflicts and external threats. These weakened their control, requiring resources elsewhere." + } + }, + "status": "complete" }, { "type": "patch", - "reasoning": "Addressing common misconceptions to ensure they are clarified in the lesson.", + "reasoning": "Created the second learning cycle to describe changes in Britain post-Romans.", "value": { - "type": "misconceptions", + "type": "cycle", "op": "add", - "path": "/misconceptions", - "value": [ - { - "misconception": "Romans were forced out of Britain by barbarians", - "description": "The Romans left Britain mainly due to internal issues in the Roman Empire. This can be clarified by explaining the Roman Empire's struggles." + "path": "/cycle2", + "value": { + "title": "Changes in Post-Roman Britain", + "durationInMinutes": 15, + "explanation": { + "spokenExplanation": [ + "Explain how the departure of Romans led to power vacuums in Britain.", + "Discuss the arrival of the Saxons and other groups invading Britain.", + "Describe the changes in town structures and the decline of Roman-built infrastructure.", + "Highlight the blend of Roman and new cultural influences in Britain.", + "Show examples of Roman architecture that remained influential." + ], + "accompanyingSlideDetails": "Images showing Roman roads and Saxon settlements.", + "imagePrompt": "Roman roads and Saxon settlements", + "slideText": "After the Romans left, Britain faced invasions and cultural changes with enduring Roman influences." }, - { - "misconception": "Britain became immediately barbaric after Romans left", - "description": "Although there were challenges, Britain continued to have structured communities and slowly adapted without Roman governance." - } - ] - } + "checkForUnderstanding": [ + { + "question": "What happened to Britain after the Romans left?", + "answers": [ + "It faced invasions from other groups" + ], + "distractors": [ + "It became a Roman province", + "It remained peaceful" + ] + }, + { + "question": "Which group invaded Britain after the Romans?", + "answers": [ + "The Saxons" + ], + "distractors": [ + "The Greeks", + "The Normans" + ] + } + ], + "practice": "Draw a picture showing a Roman road. Label it and describe its importance in post-Roman Britain.", + "feedback": "Model answer: Roman roads remained important for trade and travel in post-Roman Britain, influencing settlement patterns." + } + }, + "status": "complete" }, { "type": "patch", - "reasoning": "Including keywords to bolster vocabulary comprehension for the lesson.", + "reasoning": "Formulated the third learning cycle focusing on archaeological discoveries of Roman Britain.", "value": { - "type": "keywords", + "type": "cycle", "op": "add", - "path": "/keywords", + "path": "/cycle3", + "value": { + "title": "Archaeologists and Roman Britain", + "durationInMinutes": 15, + "explanation": { + "spokenExplanation": [ + "Define the role of archaeologists in studying history.", + "Explain how archaeologists uncover Roman artifacts through excavations.", + "Discuss the types of artifacts commonly found from Roman Britain.", + "Describe how these discoveries help us understand Roman life and influence.", + "Mention key archaeological sites in Britain linked to the Romans." + ], + "accompanyingSlideDetails": "Images of Roman artifacts and archaeological sites in Britain.", + "imagePrompt": "Roman artifacts from Britain", + "slideText": "Archaeologists uncover Roman history through excavations, revealing insights into life in Roman Britain." + }, + "checkForUnderstanding": [ + { + "question": "What do archaeologists do?", + "answers": [ + "Study history through excavations" + ], + "distractors": [ + "Write history books", + "Teach in schools" + ] + }, + { + "question": "Why are Roman artifacts important?", + "answers": [ + "They help us understand Roman life" + ], + "distractors": [ + "They are decorative", + "They are made of gold" + ] + } + ], + "practice": "Match pictures of Roman artifacts with their descriptions and explain their significance in Roman history.", + "feedback": "Model answer: Roman coins, pottery, and tools reveal daily life and trade practices in Roman Britain." + } + }, + "status": "complete" + }, + { + "type": "patch", + "reasoning": "Designed an exit quiz to assess pupils' understanding of the lesson's key concepts.", + "value": { + "type": "quiz", + "op": "add", + "path": "/exitQuiz", "value": [ { - "keyword": "Roman Empire", - "definition": "The vast empire ruled by Rome that included Britain for nearly 400 years." + "question": "When did the Romans leave Britain?", + "answers": [ + "Around AD 410" + ], + "distractors": [ + "Around AD 300", + "Around AD 500" + ] + }, + { + "question": "What was one reason for the Roman departure?", + "answers": [ + "Internal empire pressures" + ], + "distractors": [ + "Defeat by Britons", + "Lack of interest in Britain" + ] + }, + { + "question": "Who invaded Britain after the Romans left?", + "answers": [ + "The Saxons" + ], + "distractors": [ + "The Normans", + "The Vikings" + ] + }, + { + "question": "What did archaeologists find in Roman Britain?", + "answers": [ + "Artifacts like coins and pottery" + ], + "distractors": [ + "Modern buildings", + "Ancient books" + ] }, { - "keyword": "Withdrawal", - "definition": "The act of pulling out or leaving a place or position." + "question": "Why are Roman roads significant?", + "answers": [ + "They influenced travel and trade" + ], + "distractors": [ + "They were all destroyed", + "They were only decorative" + ] }, { - "keyword": "Invaders", - "definition": "People or groups that enter a region to take control." + "question": "What role do archaeologists play in history?", + "answers": [ + "They uncover past through excavations" + ], + "distractors": [ + "They build new sites", + "They rule ancient lands" + ] } ] - } + }, + "status": "complete" } ], + "sectionsEdited": [ + "starterQuiz", + "cycle1", + "cycle2", + "cycle3", + "exitQuiz" + ], "prompt": { "type": "text", - "value": "The prior knowledge, key learning points, misconceptions, and keywords have been set. Are these sections suitable for your class? If you need any changes, let me know!" - } + "value": "Are the starter quiz, learning cycles, and exit quiz sections suitable for your class? If not, reply with what I should change. Otherwise, tap **Continue** to move on to the next step." + }, + "status": "complete" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-3.moderation.json b/apps/nextjs/tests-e2e/recordings/roman-britain-3.moderation.json index a65498eef..33a48873c 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-3.moderation.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-3.moderation.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-AAdCU7gblV63CXLpunQCC7o2hxAZh", + "id": "chatcmpl-ANdXfnfoZyYbX8821gnToLzVG2k6N", "object": "chat.completion", - "created": 1727097330, + "created": 1730196907, "model": "gpt-4o-2024-08-06", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan on 'End of Roman Britain' for key-stage-1 students is fully compliant across all categories. There is no use of discriminatory or offensive language, nor is there any depiction or discussion of violence, serious conflict, or sexual violence. The content does not contain upsetting, sensitive, or distressing topics, such as criminal behavior or mental health issues, which are not appropriate for the intended young audience. Additionally, there is no depiction or discussion of nudity or sexual content. The lesson plan does not involve exploration of objects, use of potentially hazardous equipment, imitable behaviors, or physical activities that would require supervision. Finally, there is no presence of toxic content such as guides or encouragements of harmful or illegal activities. The lesson content is entirely appropriate for the key-stage-1 age group.\",\"categories\":[]}", + "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan titled 'End of Roman Britain' is fully compliant across all category groups for key-stage 1 history. It does not contain any discriminatory language, offensive content, or strong language. The content does not depict or discuss conflict, violence, or sensitive subjects, and there is no mention of nudity or sexual content. Physical activities, if any, are not hazardous or require special supervision. Furthermore, there are no guides or encouragements for harmful or illegal behavior. The content is appropriate for young learners, focusing on historical events and archaeological studies related to Roman Britain, presented in an educational and age-appropriate manner.\",\"categories\":[]}", "refusal": null }, "logprobs": null, @@ -16,12 +16,15 @@ } ], "usage": { - "prompt_tokens": 2530, - "completion_tokens": 195, - "total_tokens": 2725, + "prompt_tokens": 3796, + "completion_tokens": 158, + "total_tokens": 3954, + "prompt_tokens_details": { + "cached_tokens": 2432 + }, "completion_tokens_details": { "reasoning_tokens": 0 } }, - "system_fingerprint": "fp_7568d46099" + "system_fingerprint": "fp_90354628f2" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-4.chunks.txt b/apps/nextjs/tests-e2e/recordings/roman-britain-4.chunks.txt index 4f299f15d..0a04db3ce 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-4.chunks.txt +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-4.chunks.txt @@ -3,1015 +3,392 @@ type":" ll m Message -","patches":[ +","sectionsToEdit":[" +additional +Materials +"],"patches":[ {" type":" patch ","reasoning":" -Creating - a - Starter - Quiz - to - check - prior - knowledge - before - the - lesson - begins -."," -value":{" -type":" -quiz -","op":" -add -","path":"/ -starter -Quiz -","value":[ -{" -question":" -When - did - the - Romans - invade - Britain -? -","answers":[" -43 - AD -"],"distractors":[" -410 - AD -"," -106 -6 - AD -"]},{" -question":" -What - was - Had -rian -'s - Wall -? -","answers":[" -A - wall - built - by - Romans +Providing + additional + materials in the - north + form of - Britain -"],"distractors":[" -A - wall - built - by - Brit -ons + a + narrative + script to - keep - Romans - out -"," -A - wall + support + the + teacher in - Rome -"]},{" -question":" -What - connected - towns - across - Roman - Britain -? -","answers":[" -Roman - roads -"],"distractors":[" -Can -als -"," -R -ivers -"]},{" -question":" -What - did - Romans - introduce - to - Britain -? -","answers":[" -New - food - and - customs -"],"distractors":[" -The - English - language -"," -Modern - technology -"]},{" -question":" -Who - adopted + delivering + complex + topics + about Roman - ways - of - life -? -","answers":[" -Some - Brit -ons -"],"distractors":[" -All - Brit -ons -"," -None - of - the - Brit -ons -"]},{" -question":" -What - year - did - the - Romans - leave - Britain -? -","answers":[" -410 - AD -"],"distractors":[" -43 - AD -"," -106 -6 - AD -"]}]}},{" -type":" -patch -","reasoning":" -Design -ing - the - first - learning - cycle - to - introduce - why - the - Romans - left Britain ."," value":{" type":" -cycle +string ","op":" add ","path":"/ -cycle -1 -","value":{" -title":" -Reasons +additional +Materials +","value":" +## + Narrative + Script for - Roman - Departure -","durationInMinutes":15 -,"explanation":{" -spokenExplanation":[" -Discuss - the - internal - struggles - of - the - Roman - Empire - around - -410 - AD -."," -Explain - how - these - struggles - led + Teachers +\n +\n +\" +Today +, + we're + going + to + travel + back + in + time to + when the - withdrawal - from + Romans + were + in Britain -."," -Introduce - the - concept - of - ' -withdraw -al -' - as +. + Imagine + you + are a - strategic - move - by - the - Romans -."," -Link - how - the Roman - Empire -'s - focus - shifted - elsewhere - due - to - these - struggles -." -],"accompanyingSlideDetails":" -A - map - showing - the - Roman - Empire + soldier +, + wearing + your + armour and - its - extent + sandals , - highlighting - Britain -."," -imagePrompt":" -Roman - Empire - map - -410 - AD -","slideText":" -The - Romans - left - Britain + walking + the + long + roads + they + built +. + These + roads + were + very + important + because + they + connected + towns + and + made + travel + easier +. + But around + AD 410 - AD - due - to - internal - struggles -. - They - withdrew - strategically -." -},"checkForUnderstanding":[ -{" -question":" -Why - did - the - Romans - leave - Britain -? -","answers":[" -Internal - struggles - in +, the Roman Empire -"],"distractors":[" -British - rebellion -"," -Bar -bar -ian - invasion -"]},{" -question":" -What - does - ' -withdraw -al -' - mean - in - this - context -? -","answers":[" -Leaving - a - place - or - position -"],"distractors":[" -Att -acking - a - place -"," -Building - a - wall -"]}],"practice":" -List - two - reasons - why - the - Romans + was + facing + big + problems +. + They + needed + their + soldiers + back + home + to + help + fight + battles + elsewhere +. + So +, + they decided to - withdraw - from + leave Britain +.\ +n +\n +Now , - using + picture the - slide - for - guidance -."," -feedback":" -Model - answer -: - -1 -. - Internal - struggles + changes in + Britain + when the - Roman - Empire -. - -2 + Romans + left . - Shift -ed - focus - to - other - regions -." -}}},{" -type":" -patch -","reasoning":" -Organ -izing + New + groups + like the - second - learning - cycle + Sax +ons + came + and + started to - cover - the - immediate - effects - on - local - communities - after - Roman - withdrawal -."," -value":{" -type":" -cycle -","op":" -add -","path":"/ -cycle -2 -","value":{" -title":" -Immediate - Effects - on - Local - Communities -","durationInMinutes":15 -,"explanation":{" -spokenExplanation":[" -Describe - how + live + here +. + The Roman - withdrawal - left - Britain - vulnerable - to - invas -ions -."," -Explain - the - immediate - impact - on - local - communities + buildings + slowly + changed + or + disappeared , - such - as - lack - of - Roman - support -."," -Discuss - the - decline - of - Roman - structures - and + but + some + things +, + like roads - over - time -." -],"accompanyingSlideDetails":" -Images - of - Roman - ruins and - a - map - of - Britain - showing - vulnerable - areas - after - Roman - withdrawal -."," -imagePrompt":" -Roman - ruins - Britain -","slideText":" -Roman - withdrawal - left - Britain - vulnerable - to - invas -ions + towns +, + stayed and - local - communities - without - support -." -},"checkForUnderstanding":[ -{" -question":" -What - happened - to - Roman - structures - after - the - Romans - left -? -","answers":[" -They - fell - into - dis -repair -"],"distractors":[" -They - were - improved -"," -They - turned - into - castles -"]},{" -question":" -How - did - Roman - withdrawal - affect - local - communities -? -","answers":[" -Commun -ities - lacked - Roman - support -"],"distractors":[" -Commun -ities - became - wealth -ier -"," -Commun -ities - built - new - structures -"]}],"practice":" -Describe - one - immediate - effect - on - local - communities - after - the - Romans - left + influenced + how + people + lived +.\ +n +\n +Ar +chae +ologists + are + like + detectives . - Use - examples - from - the - slide - to - help - your - understanding -."," -feedback":" -Model - answer -: - Local - communities - lacked + They + dig + up + old Roman - support + coins +, + pottery +, and - faced - invas -ions -." -}}},{" -type":" -patch -","reasoning":" -Creating - the - third - learning - cycle + tools to - focus - on + learn + about how - Britain - changed - in + people + lived + back + then +. + These + discoveries + tell + us + exciting + stories + about + the + past +, + like + what the - years - following - Roman - departure -."," -value":{" -type":" -cycle -","op":" -add -","path":"/ -cycle -3 -","value":{" -title":" -Long --term - Changes - in - Britain -","durationInMinutes":15 -,"explanation":{" -spokenExplanation":[" -Explain - how - Britain - adapted - and - changed - culturally - and - technologically - post --R -oman - rule -."," -Discuss + Romans + ate +, the - blend - of - Roman + clothes + they + wore +, and - local - traditions - that - emerged -."," -Highlight the - technological - changes - that - occurred - as + games + they + played +.\ +n +\n +Remember +, + history + is a - result + puzzle +, + and + each + piece + helps + us + understand + more + about + our + world + today +. +\"\ +n +\n +### + Key + Points + for + Explanation +\n +- + Importance of Roman - influence -." -],"accompanyingSlideDetails":" -A - timeline - showing - cultural + roads and - technological - changes - in - Britain - after - Romans -."," -imagePrompt":" -Post --R -oman - Britain - timeline -","slideText":" -Post + infrastructure +.\ +n +- + Reasons + for + Roman + departure + due + to + empire + pressures +.\ +n +- + Post -R oman - Britain - saw - cultural - and - technological changes -, - blending - Roman and - local - traditions -." -},"checkForUnderstanding":[ -{" -question":" -How - did - Britain's - culture - change - after - the - Romans - left -? -","answers":[" -Bl -ended - Roman - and - local - traditions -"],"distractors":[" -B -ec -ame - entirely - Roman -"," -Forgot - Roman - ways -"]},{" -question":" -What - type + Sax +on + invas +ions +.\ +n +- + Role of - changes - occurred + archae +ologists in - Britain - after - Roman - rule -? -","answers":[" -C -ultural - and - technological -"],"distractors":[" -Only - political -"," -Only - economical -"]}],"practice":" -Explain - one - cultural - or - technological - change - in - Britain - after - the - Romans - left -. - Provide - an - example - from - the - slide -."," -feedback":" -Model - answer -: - The - blend - of - Roman - and - local - traditions - led - to - new - cultural - practices -." -}}},{" -type":" -patch -","reasoning":" -Creating - an - Exit - Quiz - to - assess - understanding - of - the - lesson - topics -."," -value":{" -type":" -quiz -","op":" -add -","path":"/ -exit -Quiz -","value":[ -{" -question":" -When - did - the - Romans - leave - Britain -? -","answers":[" -Around - -410 - AD -"],"distractors":[" -43 - AD -"," -106 -6 - AD -"]},{" -question":" -What - was - a - reason + uncover +ing + history +.\ +n +\n +Search + terms for - Roman - withdrawal -? -","answers":[" -Internal - struggles -"],"distractors":[" -British - alliance -"," + images +: + \" Roman - expansion -"]},{" -question":" -How - were - Roman - structures - affected - after - withdrawal -? -","answers":[" -F -ell - into - dis -repair -"],"distractors":[" -B -ec -ame - museums -"," -Were - exported -"]},{" -question":" -What - did - local - communities - lack - after - Roman - withdrawal -? -","answers":[" + soldier + armour +\", + \" Roman - support -"],"distractors":[" -Water - supply -"," -New roads -"]},{" -question":" -What - cultural - changes - occurred - after - Roman - rule -? -","answers":[" -Bl -ended - Roman - and - local - traditions -"],"distractors":[" -Ad -opt -ed - only - Roman - traditions -"," -El -imin -ated - Roman - influence -"]},{" -question":" -What - kind - of - changes - did - Britain - experience - post --R -oman - rule -? -","answers":[" -C -ultural - and - technological -"],"distractors":[" -Only - military -"," -Only - agricultural -"]}]}}],"prompt":{" +\", + \" +S +axon + settlements +\", + \" +Roman + artifacts +\" +." +},"status":" +complete +"}],"sectionsEdited":[" +additional +Materials +"],"prompt":{" type":" text ","value":" -The - quizzes - and - learning - cycles +I have - been - created -. - These - include - questions + added + a + narrative + script to - test - understanding - at - each - stage + support + the + lesson + delivery . Are the - quizzes - and - learning - cycles - at - an + additional + materials appropriate - difficulty - level for your pupils ? - Let - me - know - if - you - need - any - adjustments -!" -}} \ No newline at end of file + If + not +, + suggest + an + edit +. + Otherwise +, + tap + ** +Continue +** + to + complete + the + lesson + plan +." +},"status":" +complete +"} \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-4.formatted.json b/apps/nextjs/tests-e2e/recordings/roman-britain-4.formatted.json index 07ada930a..f584fab37 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-4.formatted.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-4.formatted.json @@ -1,293 +1,27 @@ { "type": "llmMessage", + "sectionsToEdit": [ + "additionalMaterials" + ], "patches": [ { "type": "patch", - "reasoning": "Creating a Starter Quiz to check prior knowledge before the lesson begins.", - "value": { - "type": "quiz", - "op": "add", - "path": "/starterQuiz", - "value": [ - { - "question": "When did the Romans invade Britain?", - "answers": [ - "43 AD" - ], - "distractors": [ - "410 AD", - "1066 AD" - ] - }, - { - "question": "What was Hadrian's Wall?", - "answers": [ - "A wall built by Romans in the north of Britain" - ], - "distractors": [ - "A wall built by Britons to keep Romans out", - "A wall in Rome" - ] - }, - { - "question": "What connected towns across Roman Britain?", - "answers": [ - "Roman roads" - ], - "distractors": [ - "Canals", - "Rivers" - ] - }, - { - "question": "What did Romans introduce to Britain?", - "answers": [ - "New food and customs" - ], - "distractors": [ - "The English language", - "Modern technology" - ] - }, - { - "question": "Who adopted Roman ways of life?", - "answers": [ - "Some Britons" - ], - "distractors": [ - "All Britons", - "None of the Britons" - ] - }, - { - "question": "What year did the Romans leave Britain?", - "answers": [ - "410 AD" - ], - "distractors": [ - "43 AD", - "1066 AD" - ] - } - ] - } - }, - { - "type": "patch", - "reasoning": "Designing the first learning cycle to introduce why the Romans left Britain.", - "value": { - "type": "cycle", - "op": "add", - "path": "/cycle1", - "value": { - "title": "Reasons for Roman Departure", - "durationInMinutes": 15, - "explanation": { - "spokenExplanation": [ - "Discuss the internal struggles of the Roman Empire around 410 AD.", - "Explain how these struggles led to the withdrawal from Britain.", - "Introduce the concept of 'withdrawal' as a strategic move by the Romans.", - "Link how the Roman Empire's focus shifted elsewhere due to these struggles." - ], - "accompanyingSlideDetails": "A map showing the Roman Empire and its extent, highlighting Britain.", - "imagePrompt": "Roman Empire map 410 AD", - "slideText": "The Romans left Britain around 410 AD due to internal struggles. They withdrew strategically." - }, - "checkForUnderstanding": [ - { - "question": "Why did the Romans leave Britain?", - "answers": [ - "Internal struggles in the Roman Empire" - ], - "distractors": [ - "British rebellion", - "Barbarian invasion" - ] - }, - { - "question": "What does 'withdrawal' mean in this context?", - "answers": [ - "Leaving a place or position" - ], - "distractors": [ - "Attacking a place", - "Building a wall" - ] - } - ], - "practice": "List two reasons why the Romans decided to withdraw from Britain, using the slide for guidance.", - "feedback": "Model answer: 1. Internal struggles in the Roman Empire. 2. Shifted focus to other regions." - } - } - }, - { - "type": "patch", - "reasoning": "Organizing the second learning cycle to cover the immediate effects on local communities after Roman withdrawal.", + "reasoning": "Providing additional materials in the form of a narrative script to support the teacher in delivering complex topics about Roman Britain.", "value": { - "type": "cycle", + "type": "string", "op": "add", - "path": "/cycle2", - "value": { - "title": "Immediate Effects on Local Communities", - "durationInMinutes": 15, - "explanation": { - "spokenExplanation": [ - "Describe how Roman withdrawal left Britain vulnerable to invasions.", - "Explain the immediate impact on local communities, such as lack of Roman support.", - "Discuss the decline of Roman structures and roads over time." - ], - "accompanyingSlideDetails": "Images of Roman ruins and a map of Britain showing vulnerable areas after Roman withdrawal.", - "imagePrompt": "Roman ruins Britain", - "slideText": "Roman withdrawal left Britain vulnerable to invasions and local communities without support." - }, - "checkForUnderstanding": [ - { - "question": "What happened to Roman structures after the Romans left?", - "answers": [ - "They fell into disrepair" - ], - "distractors": [ - "They were improved", - "They turned into castles" - ] - }, - { - "question": "How did Roman withdrawal affect local communities?", - "answers": [ - "Communities lacked Roman support" - ], - "distractors": [ - "Communities became wealthier", - "Communities built new structures" - ] - } - ], - "practice": "Describe one immediate effect on local communities after the Romans left. Use examples from the slide to help your understanding.", - "feedback": "Model answer: Local communities lacked Roman support and faced invasions." - } - } - }, - { - "type": "patch", - "reasoning": "Creating the third learning cycle to focus on how Britain changed in the years following Roman departure.", - "value": { - "type": "cycle", - "op": "add", - "path": "/cycle3", - "value": { - "title": "Long-term Changes in Britain", - "durationInMinutes": 15, - "explanation": { - "spokenExplanation": [ - "Explain how Britain adapted and changed culturally and technologically post-Roman rule.", - "Discuss the blend of Roman and local traditions that emerged.", - "Highlight the technological changes that occurred as a result of Roman influence." - ], - "accompanyingSlideDetails": "A timeline showing cultural and technological changes in Britain after Romans.", - "imagePrompt": "Post-Roman Britain timeline", - "slideText": "Post-Roman Britain saw cultural and technological changes, blending Roman and local traditions." - }, - "checkForUnderstanding": [ - { - "question": "How did Britain's culture change after the Romans left?", - "answers": [ - "Blended Roman and local traditions" - ], - "distractors": [ - "Became entirely Roman", - "Forgot Roman ways" - ] - }, - { - "question": "What type of changes occurred in Britain after Roman rule?", - "answers": [ - "Cultural and technological" - ], - "distractors": [ - "Only political", - "Only economical" - ] - } - ], - "practice": "Explain one cultural or technological change in Britain after the Romans left. Provide an example from the slide.", - "feedback": "Model answer: The blend of Roman and local traditions led to new cultural practices." - } - } - }, - { - "type": "patch", - "reasoning": "Creating an Exit Quiz to assess understanding of the lesson topics.", - "value": { - "type": "quiz", - "op": "add", - "path": "/exitQuiz", - "value": [ - { - "question": "When did the Romans leave Britain?", - "answers": [ - "Around 410 AD" - ], - "distractors": [ - "43 AD", - "1066 AD" - ] - }, - { - "question": "What was a reason for Roman withdrawal?", - "answers": [ - "Internal struggles" - ], - "distractors": [ - "British alliance", - "Roman expansion" - ] - }, - { - "question": "How were Roman structures affected after withdrawal?", - "answers": [ - "Fell into disrepair" - ], - "distractors": [ - "Became museums", - "Were exported" - ] - }, - { - "question": "What did local communities lack after Roman withdrawal?", - "answers": [ - "Roman support" - ], - "distractors": [ - "Water supply", - "New roads" - ] - }, - { - "question": "What cultural changes occurred after Roman rule?", - "answers": [ - "Blended Roman and local traditions" - ], - "distractors": [ - "Adopted only Roman traditions", - "Eliminated Roman influence" - ] - }, - { - "question": "What kind of changes did Britain experience post-Roman rule?", - "answers": [ - "Cultural and technological" - ], - "distractors": [ - "Only military", - "Only agricultural" - ] - } - ] - } + "path": "/additionalMaterials", + "value": "## Narrative Script for Teachers\n\n\"Today, we're going to travel back in time to when the Romans were in Britain. Imagine you are a Roman soldier, wearing your armour and sandals, walking the long roads they built. These roads were very important because they connected towns and made travel easier. But around AD 410, the Roman Empire was facing big problems. They needed their soldiers back home to help fight battles elsewhere. So, they decided to leave Britain.\n\nNow, picture the changes in Britain when the Romans left. New groups like the Saxons came and started to live here. The Roman buildings slowly changed or disappeared, but some things, like roads and towns, stayed and influenced how people lived.\n\nArchaeologists are like detectives. They dig up old Roman coins, pottery, and tools to learn about how people lived back then. These discoveries tell us exciting stories about the past, like what the Romans ate, the clothes they wore, and the games they played.\n\nRemember, history is a puzzle, and each piece helps us understand more about our world today.\"\n\n### Key Points for Explanation\n- Importance of Roman roads and infrastructure.\n- Reasons for Roman departure due to empire pressures.\n- Post-Roman changes and Saxon invasions.\n- Role of archaeologists in uncovering history.\n\nSearch terms for images: \"Roman soldier armour\", \"Roman roads\", \"Saxon settlements\", \"Roman artifacts\"." + }, + "status": "complete" } ], + "sectionsEdited": [ + "additionalMaterials" + ], "prompt": { "type": "text", - "value": "The quizzes and learning cycles have been created. These include questions to test understanding at each stage. Are the quizzes and learning cycles at an appropriate difficulty level for your pupils? Let me know if you need any adjustments!" - } + "value": "I have added a narrative script to support the lesson delivery. Are the additional materials appropriate for your pupils? If not, suggest an edit. Otherwise, tap **Continue** to complete the lesson plan." + }, + "status": "complete" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-4.moderation.json b/apps/nextjs/tests-e2e/recordings/roman-britain-4.moderation.json index 415d84318..fb7df491f 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-4.moderation.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-4.moderation.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-AAdCsMbt9HTt6nlyK1VwS7c30H6ma", + "id": "chatcmpl-ANdXxPFyq8YyfRXY85uI1uNrgJ0N9", "object": "chat.completion", - "created": 1727097354, + "created": 1730196925, "model": "gpt-4o-2024-08-06", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan titled 'End of Roman Britain' is designed for Key Stage 1 history students. It focuses on the historical events surrounding the end of Roman rule in Britain and its effects, aiming to describe changes in Britain after the Romans left. The content is age-appropriate and free from any discriminatory language, offensive language, or strong language. There is no depiction or discussion of violence, serious violence, or sexual violence. The lesson does not contain upsetting, sensitive, or distressing content, such as depictions of criminal behavior, unsafe situations, or sensitive topics like mental health or death. There is no depiction or discussion of nudity or sexual content. The lesson does not involve exploration of objects, use of equipment requiring supervision, imitable behaviors, external content, or physical activity. Finally, the lesson plan does not contain any guides or encouragement of harmful behaviors, illegal activity, or violence. Overall, the lesson is fully compliant across all categories.\",\"categories\":[]}", + "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan for \\\"End of Roman Britain\\\" is appropriate for Key Stage 1 students, with content that is educational and age-appropriate. It focuses on historical events and changes without delving into any potentially offensive or sensitive topics. There is no discriminatory language or behavior. The lesson does not discuss or depict violence or crime, nor does it contain upsetting or sensitive content. There is no mention of nudity or sexual content. The lesson includes standard educational activities and discussions, without involving physical activities requiring safety considerations. Additionally, there is no toxic content, such as instructions or encouragement of harmful or illegal activities. Overall, the lesson is fully compliant with guidelines across all categories.\",\"categories\":[]}", "refusal": null }, "logprobs": null, @@ -16,12 +16,15 @@ } ], "usage": { - "prompt_tokens": 3627, - "completion_tokens": 224, - "total_tokens": 3851, + "prompt_tokens": 4098, + "completion_tokens": 168, + "total_tokens": 4266, + "prompt_tokens_details": { + "cached_tokens": 3584 + }, "completion_tokens_details": { "reasoning_tokens": 0 } }, - "system_fingerprint": "fp_7568d46099" + "system_fingerprint": "fp_90354628f2" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-5.chunks.txt b/apps/nextjs/tests-e2e/recordings/roman-britain-5.chunks.txt index 7fdb79e62..c7e49eda5 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-5.chunks.txt +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-5.chunks.txt @@ -3,67 +3,53 @@ type":" ll m Message -","patches":[ -{" -type":" -patch -","reasoning":" -Adding - a - placeholder - for - potential - additional - materials - or - narratives - that - may - support - the - lesson - plan -."," -value":{" -type":" -string -","op":" -add -","path":"/ -additional -Materials -","value":" -None -"}}],"prompt":{" +","sectionsToEdit":[] +,"patches":[] +,"sectionsEdited":[] +,"prompt":{" type":" text ","value":" -Would - you - like - to - add - any - additional - materials -, - such - as - a - narrative - to - support - your - explanations -, - instructions +I + have + checked for - practical -s + British + spelling + and + grammar , + coherence +, + and + accuracy +. + You + can + now + share + your + lesson or - extra - homework - questions -?" -}} \ No newline at end of file + download + your + resources +.\ +n +\n +Click + on + the + ** +Menu +** + button + to + find + previously + created + lessons +." +},"status":" +complete +"} \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-5.formatted.json b/apps/nextjs/tests-e2e/recordings/roman-britain-5.formatted.json index e12063cac..c446088cb 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-5.formatted.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-5.formatted.json @@ -1,19 +1,11 @@ { "type": "llmMessage", - "patches": [ - { - "type": "patch", - "reasoning": "Adding a placeholder for potential additional materials or narratives that may support the lesson plan.", - "value": { - "type": "string", - "op": "add", - "path": "/additionalMaterials", - "value": "None" - } - } - ], + "sectionsToEdit": [], + "patches": [], + "sectionsEdited": [], "prompt": { "type": "text", - "value": "Would you like to add any additional materials, such as a narrative to support your explanations, instructions for practicals, or extra homework questions?" - } + "value": "I have checked for British spelling and grammar, coherence, and accuracy. You can now share your lesson or download your resources.\n\nClick on the **Menu** button to find previously created lessons." + }, + "status": "complete" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/recordings/roman-britain-5.moderation.json b/apps/nextjs/tests-e2e/recordings/roman-britain-5.moderation.json index 6e9784362..221611a02 100644 --- a/apps/nextjs/tests-e2e/recordings/roman-britain-5.moderation.json +++ b/apps/nextjs/tests-e2e/recordings/roman-britain-5.moderation.json @@ -1,14 +1,14 @@ { - "id": "chatcmpl-AAdD8dTCVQDaTA5fC2CxjBBenZ9yu", + "id": "chatcmpl-ANdYBeKYKlYscRigozQrSkrUxlhAQ", "object": "chat.completion", - "created": 1727097370, + "created": 1730196939, "model": "gpt-4o-2024-08-06", "choices": [ { "index": 0, "message": { "role": "assistant", - "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan for 'End of Roman Britain' is fully compliant with all content guidelines. It does not contain any discriminatory language, offensive or strong language, or depict any violence or crime. The content is sensitive and age-appropriate for Key Stage 1 students, avoiding topics that could be upsetting, disturbing, or sensitive. There are no discussions or depictions of nudity or sexual content. The lesson does not involve any physical activities or use of equipment that require safety supervision. Additionally, there are no toxic elements such as guides for harmful behavior or creation of weapons. The lesson focuses on historical changes in Britain after the Roman withdrawal, presented in a factual and educational manner suitable for young learners.\",\"categories\":[]}", + "content": "{\"scores\":{\"l\":5,\"v\":5,\"u\":5,\"s\":5,\"p\":5,\"t\":5},\"justification\":\"The lesson plan on 'End of Roman Britain' is fully compliant across all categories. It does not contain any discriminatory language, offensive language, or strong language. There is no depiction or discussion of conflict, violence, serious violence, or sexual violence. The content is age-appropriate for Key Stage 1 and does not include any upsetting, sensitive, or distressing content. There is no depiction or discussion of nudity or sexual content. The lesson does not involve exploration of objects, use of equipment requiring supervision, imitable behaviors, external content, or physical activity that might require adult supervision. Lastly, it does not contain any toxic content such as guides or encouragements for harmful or illegal activities. The lesson focuses on historical facts in a sensitive and educational manner suitable for young learners.\",\"categories\":[]}", "refusal": null }, "logprobs": null, @@ -16,12 +16,15 @@ } ], "usage": { - "prompt_tokens": 3632, - "completion_tokens": 172, - "total_tokens": 3804, + "prompt_tokens": 4098, + "completion_tokens": 190, + "total_tokens": 4288, + "prompt_tokens_details": { + "cached_tokens": 3968 + }, "completion_tokens_details": { "reasoning_tokens": 0 } }, - "system_fingerprint": "fp_7568d46099" + "system_fingerprint": "fp_90354628f2" } \ No newline at end of file diff --git a/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.mobile.test.ts b/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.mobile.test.ts index 6575dc56e..8b50af64e 100644 --- a/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.mobile.test.ts +++ b/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.mobile.test.ts @@ -9,13 +9,16 @@ import { continueChat, expectFinished, expectSectionsComplete, + expectStreamingStatus, + letUiSettle, + scrollLessonPlanFromTopToBottom, waitForGeneration, } from "./helpers"; // -------- // CHANGE "replay" TO "record" TO RECORD A NEW FIXTURE // -------- -// const FIXTURE_MODE = "record" as FixtureMode; +//const FIXTURE_MODE = "record" as FixtureMode; const FIXTURE_MODE = "replay" as FixtureMode; async function closePreview(page: Page) { @@ -32,7 +35,7 @@ async function expectPreviewVisible(page: Page) { test( "Full aila flow with Romans fixture", { tag: "@mobile-common-auth" }, - async ({ page }) => { + async ({ page }, testInfo) => { const generationTimeout = FIXTURE_MODE === "record" ? 75000 : 50000; test.setTimeout(generationTimeout * 5); @@ -49,8 +52,7 @@ test( await test.step("Fill in the chat box", async () => { const textbox = page.getByTestId("chat-input"); const sendMessage = page.getByTestId("send-message"); - const message = - "Create a KS1 lesson on the end of Roman Britain. Ask a question for each quiz and cycle"; + const message = "Create a KS1 lesson on the end of Roman Britain"; await textbox.fill(message); await expect(textbox).toContainText(message); @@ -67,22 +69,28 @@ test( await waitForGeneration(page, generationTimeout); await expectPreviewVisible(page); - await expectSectionsComplete(page, 1); + await expectSectionsComplete(page, 3); await closePreview(page); + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + await letUiSettle(page, testInfo); setFixture("roman-britain-2"); await continueChat(page); await waitForGeneration(page, generationTimeout); await expectPreviewVisible(page); - await expectSectionsComplete(page, 3); + await expectSectionsComplete(page, 7); await closePreview(page); + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + await letUiSettle(page, testInfo); setFixture("roman-britain-3"); await continueChat(page); await waitForGeneration(page, generationTimeout); await expectPreviewVisible(page); - await expectSectionsComplete(page, 7); + await expectSectionsComplete(page, 10); await closePreview(page); + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + await letUiSettle(page, testInfo); setFixture("roman-britain-4"); await continueChat(page); @@ -90,12 +98,17 @@ test( await expectPreviewVisible(page); await expectSectionsComplete(page, 10); await closePreview(page); + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + await letUiSettle(page, testInfo); setFixture("roman-britain-5"); await continueChat(page); await waitForGeneration(page, generationTimeout); await expectPreviewVisible(page); await expectSectionsComplete(page, 10); + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + await letUiSettle(page, testInfo); + await scrollLessonPlanFromTopToBottom(page); await expectFinished(page); }); }, diff --git a/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.test.ts b/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.test.ts index de65ad42b..b93b7fa7d 100644 --- a/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.test.ts +++ b/apps/nextjs/tests-e2e/tests/aila-chat/full-romans.test.ts @@ -1,5 +1,5 @@ import { setupClerkTestingToken } from "@clerk/testing/playwright"; -import { test, expect, Page } from "@playwright/test"; +import { test, expect } from "@playwright/test"; import { TEST_BASE_URL } from "../../config/config"; import { bypassVercelProtection } from "../../helpers/vercel"; @@ -8,29 +8,25 @@ import { applyLlmFixtures, continueChat, expectFinished, - expectSectionsComplete, - waitForGeneration, + expectStreamingStatus, + isFinished, + scrollLessonPlanFromTopToBottom, + waitForStreamingStatusChange, } from "./helpers"; // -------- // CHANGE "replay" TO "record" TO RECORD A NEW FIXTURE // -------- -// const FIXTURE_MODE = "record" as FixtureMode; +//const FIXTURE_MODE = "record" as FixtureMode; const FIXTURE_MODE = "replay" as FixtureMode; test( "Full aila flow with Romans fixture", { tag: "@common-auth" }, - async ({ page }, testInfo) => { + async ({ page }) => { const generationTimeout = FIXTURE_MODE === "record" ? 75000 : 50000; test.setTimeout(generationTimeout * 5); - // The chat UI has a race condition when you submit a message too quickly after the previous response - // This is a temporary fix to fix test flake - async function letUiSettle() { - return await page.waitForTimeout(testInfo.retry === 0 ? 500 : 6000); - } - await test.step("Setup", async () => { await bypassVercelProtection(page); await setupClerkTestingToken({ page }); @@ -44,8 +40,7 @@ test( await test.step("Fill in the chat box", async () => { const textbox = page.getByTestId("chat-input"); const sendMessage = page.getByTestId("send-message"); - const message = - "Create a KS1 lesson on the end of Roman Britain. Ask a question for each quiz and cycle"; + const message = "Create a KS1 lesson on the end of Roman Britain"; await textbox.fill(message); await expect(textbox).toContainText(message); @@ -59,33 +54,39 @@ test( await test.step("Iterate through the fixtures", async () => { await page.waitForURL(/\/aila\/.+/); - await waitForGeneration(page, generationTimeout); - await expectSectionsComplete(page, 1); - await letUiSettle(); - - setFixture("roman-britain-2"); - await continueChat(page); - await waitForGeneration(page, generationTimeout); - await expectSectionsComplete(page, 3); - await letUiSettle(); - - setFixture("roman-britain-3"); - await continueChat(page); - await waitForGeneration(page, generationTimeout); - await expectSectionsComplete(page, 7); - await letUiSettle(); - - setFixture("roman-britain-4"); - await continueChat(page); - await waitForGeneration(page, generationTimeout); - await expectSectionsComplete(page, 10); - await letUiSettle(); - - setFixture("roman-britain-5"); - await continueChat(page); - await waitForGeneration(page, generationTimeout); - await expectSectionsComplete(page, 10); + const maxIterations = 20; + + for ( + let iterationCount = 1; + iterationCount <= maxIterations; + iterationCount++ + ) { + setFixture(`roman-britain-${iterationCount}`); + await expectStreamingStatus(page, "RequestMade", { timeout: 5000 }); + + await waitForStreamingStatusChange( + page, + "RequestMade", + "Idle", + generationTimeout, + ); + + await expectStreamingStatus(page, "Idle", { timeout: 5000 }); + + if (await isFinished(page)) { + break; + } + await continueChat(page); + await waitForStreamingStatusChange( + page, + "Idle", + "RequestMade", + generationTimeout, + ); + } + + await scrollLessonPlanFromTopToBottom(page); await expectFinished(page); }); }, diff --git a/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts b/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts index 563adb244..7f33455bd 100644 --- a/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts +++ b/apps/nextjs/tests-e2e/tests/aila-chat/helpers.ts @@ -1,4 +1,4 @@ -import { expect, Page, test } from "@playwright/test"; +import { expect, Page, test, TestInfo } from "@playwright/test"; import { AilaStreamingStatus } from "@/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; @@ -8,7 +8,33 @@ export async function expectStreamingStatus( args?: { timeout: number }, ) { const statusElement = page.getByTestId("chat-aila-streaming-status"); - await expect(statusElement).toHaveText(status, args); + await expect(statusElement).toContainText(status, args); +} + +export async function waitForStreamingStatusChange( + page: Page, + currentStatus: AilaStreamingStatus, + expectedStatus: AilaStreamingStatus, + timeout: number, +) { + await page.waitForFunction( + ([currentStatus, expectedStatus]) => { + const statusElement = document.querySelector( + '[data-testid="chat-aila-streaming-status"]', + ); + return ( + statusElement && + currentStatus && + expectedStatus && + !statusElement.textContent?.includes(currentStatus) && + statusElement.textContent?.includes(expectedStatus) + ); + }, + [currentStatus, expectedStatus], + { timeout }, + ); + + await expectStreamingStatus(page, expectedStatus); } export async function waitForGeneration(page: Page, generationTimeout: number) { @@ -35,6 +61,17 @@ export async function expectFinished(page: Page) { ); } +export async function getSectionsComplete(page: Page): Promise { + const progressText = await page.getByTestId("chat-progress").textContent(); + const match = (progressText ?? "").match(/(\d+) of 10 sections complete/); + + if (match && match[1]) { + return parseInt(match[1], 10); + } else { + return 0; + } +} + export async function expectSectionsComplete( page: Page, numberOfSections: number, @@ -67,3 +104,38 @@ export const applyLlmFixtures = async ( }, }; }; + +// The chat UI has a race condition when you submit a message too quickly after the previous response +// This is a temporary fix to fix test flake +export async function letUiSettle(page, testInfo: TestInfo) { + return await page.waitForTimeout(testInfo.retry === 0 ? 500 : 6000); +} + +// So that we can capture the lesson plan in the Playwright screenshot +// recording, we need to scroll the lesson plan from top to bottom. +export async function scrollLessonPlanFromTopToBottom(page: Page) { + await page.evaluate(async () => { + const scrollableParent = document.querySelector( + '[data-testid="chat-right-hand-side-lesson"]', + ) as HTMLElement; + + if (scrollableParent) { + const scrollHeight = scrollableParent.scrollHeight; + const clientHeight = scrollableParent.clientHeight; + const scrollStep = 100; // Adjust this value to control the scroll speed + const scrollDuration = 200; // Adjust this value to control the pause between each scroll step + + for ( + let scrollTop = 0; + scrollTop < scrollHeight - clientHeight; + scrollTop += scrollStep + ) { + scrollableParent.scrollTop = scrollTop; + await new Promise((resolve) => setTimeout(resolve, scrollDuration)); + } + + // Ensure we scroll to the very bottom + scrollableParent.scrollTop = scrollHeight; + } + }); +} diff --git a/packages/aila/src/core/Aila.test.ts b/packages/aila/src/core/Aila.test.ts index 51b11afbd..567944c60 100644 --- a/packages/aila/src/core/Aila.test.ts +++ b/packages/aila/src/core/Aila.test.ts @@ -195,6 +195,16 @@ describe("Aila", () => { describe("generateSync", () => { // Should return a stream when generating a lesson plan with valid input it("should set the initial title, subject and key stage when presented with a valid initial user input", async () => { + const mockChatCategoriser = new MockCategoriser({ + mockedLessonPlan: { + title: "Glaciation", + topic: "The Landscapes of the UK", + subject: "geography", + keyStage: "key-stage-3", + }, + }); + const mockLLMService = new MockLLMService(); + const ailaInstance = new Aila({ lessonPlan: {}, chat: { id: "123", userId: "user123" }, @@ -205,6 +215,10 @@ describe("Aila", () => { useModeration: false, }, plugins: [], + services: { + chatLlmService: mockLLMService, + chatCategoriser: mockChatCategoriser, + }, }); expect(ailaInstance.lesson.plan.title).not.toBeDefined(); @@ -320,7 +334,7 @@ describe("Aila", () => { expect(ailaInstance.lesson.plan.title).toBe("Mocked Lesson Plan"); expect(ailaInstance.lesson.plan.subject).toBe("Mocked Subject"); expect(ailaInstance.lesson.plan.keyStage).toBe("key-stage-3"); - }); + }, 8000); }); describe("categorisation and LLM service", () => { @@ -365,9 +379,10 @@ describe("Aila", () => { // Use MockLLMService to generate a response await ailaInstance.generateSync({ input: "Test input" }); + console.log("Generated"); // Check if MockLLMService updates were applied expect(ailaInstance.lesson.plan.title).toBe("Updated Mocked Lesson Plan"); expect(ailaInstance.lesson.plan.subject).toBe("Updated Mocked Subject"); - }); + }, 8000); }); }); diff --git a/packages/aila/src/core/Aila.ts b/packages/aila/src/core/Aila.ts index 05c31f14e..9b19b9266 100644 --- a/packages/aila/src/core/Aila.ts +++ b/packages/aila/src/core/Aila.ts @@ -1,4 +1,5 @@ import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { DEFAULT_MODEL, @@ -34,6 +35,7 @@ import { AilaInitializationOptions, } from "./types"; +const log = aiLogger("aila"); export class Aila implements AilaServices { private _analytics?: AilaAnalyticsFeature; private _chat: AilaChatService; @@ -112,6 +114,10 @@ export class Aila implements AilaServices { public async initialise() { this.checkUserIdPresentIfPersisting(); await this.loadChatIfPersisting(); + const persistedLessonPlan = this._chat.persistedChat?.lessonPlan; + if (persistedLessonPlan) { + this._lesson.setPlan(persistedLessonPlan); + } await this._lesson.setUpInitialLessonPlan(this._chat.messages); } @@ -248,6 +254,7 @@ export class Aila implements AilaServices { ); } if (input) { + log.info("Initiate chat with input", input); const message: Message = { id: generateMessageId({ role: "user" }), role: "user", diff --git a/packages/aila/src/core/AilaServices.ts b/packages/aila/src/core/AilaServices.ts index 9a713530e..38614fe0d 100644 --- a/packages/aila/src/core/AilaServices.ts +++ b/packages/aila/src/core/AilaServices.ts @@ -8,7 +8,11 @@ import { AilaThreatDetectionFeature, } from "../features/types"; import { MessagePart } from "../protocol/jsonPatchProtocol"; -import { AilaRagRelevantLesson, LooseLessonPlan } from "../protocol/schema"; +import { + AilaPersistedChat, + AilaRagRelevantLesson, + LooseLessonPlan, +} from "../protocol/schema"; import { Message } from "./chat"; import { AilaOptionsWithDefaultFallbackValues } from "./index"; import { AilaPlugin } from "./plugins"; @@ -23,6 +27,7 @@ export interface AilaAnalyticsService { export interface AilaLessonService { readonly plan: LooseLessonPlan; readonly hasSetInitialState: boolean; + setPlan(plan: LooseLessonPlan): void; applyPatches(patches: string): void; initialise(plan: LooseLessonPlan): void; setUpInitialLessonPlan(messages: Message[]): Promise; @@ -32,6 +37,9 @@ export interface AilaChatService { readonly userId: string | undefined; readonly id: string; readonly messages: Message[]; + readonly iteration: number | undefined; + readonly createdAt: Date | undefined; + readonly persistedChat: AilaPersistedChat | undefined; get relevantLessons(): AilaRagRelevantLesson[]; set relevantLessons(lessons: AilaRagRelevantLesson[]); readonly parsedMessages: MessagePart[][]; diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 5395275ee..9760b51ca 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -4,7 +4,6 @@ import { subjectWarnings, } from "@oakai/core/src/utils/subjects"; import invariant from "tiny-invariant"; -import { z } from "zod"; import { AilaChatService, AilaError, AilaServices } from "../.."; import { DEFAULT_MODEL, DEFAULT_TEMPERATURE } from "../../constants"; @@ -15,11 +14,13 @@ import { import { generateMessageId } from "../../helpers/chat/generateMessageId"; import { JsonPatchDocumentOptional, - LLMPatchDocumentSchema, - TextDocumentSchema, + LLMMessageSchema, parseMessageParts, } from "../../protocol/jsonPatchProtocol"; -import { AilaRagRelevantLesson } from "../../protocol/schema"; +import { + AilaPersistedChat, + AilaRagRelevantLesson, +} from "../../protocol/schema"; import { LLMService } from "../llm/LLMService"; import { OpenAIService } from "../llm/OpenAIService"; import { AilaPromptBuilder } from "../prompt/AilaPromptBuilder"; @@ -40,6 +41,9 @@ export class AilaChat implements AilaChatService { private readonly _patchEnqueuer: PatchEnqueuer; private readonly _llmService: LLMService; private readonly _promptBuilder: AilaPromptBuilder; + private _iteration: number | undefined; + private _createdAt: Date | undefined; + private _persistedChat: AilaPersistedChat | undefined; constructor({ id, @@ -87,6 +91,14 @@ export class AilaChat implements AilaChatService { return this._isShared; } + public get iteration() { + return this._iteration; + } + + public get createdAt() { + return this._createdAt; + } + public get messages() { return this._messages; } @@ -95,6 +107,10 @@ export class AilaChat implements AilaChatService { return this._messages.map((m) => parseMessageParts(m.content)); } + public get persistedChat() { + return this._persistedChat; + } + public get relevantLessons() { return this._relevantLessons; } @@ -214,7 +230,7 @@ export class AilaChat implements AilaChatService { } public async enqueue(message: JsonPatchDocumentOptional) { - // Optional "?"" necessary to avoid a "terminated" error + // Optional "?" Necessary to avoid a "terminated" error if (this?._patchEnqueuer) { await this._patchEnqueuer.enqueueMessage(message); } @@ -224,7 +240,7 @@ export class AilaChat implements AilaChatService { path: string, value: string | string[] | number | object, ) { - // Optional "?"" necessary to avoid a "terminated" error + // Optional "?" necessary to avoid a "terminated" error if (this?._patchEnqueuer) { await this._patchEnqueuer.enqueuePatch(path, value); } @@ -291,6 +307,9 @@ export class AilaChat implements AilaChatService { if (persistedChat) { this._relevantLessons = persistedChat.relevantLessons ?? []; this._isShared = persistedChat.isShared; + this._iteration = persistedChat.iteration ?? 1; + this._createdAt = new Date(persistedChat.createdAt); + this._persistedChat = persistedChat; } } @@ -314,14 +333,6 @@ export class AilaChat implements AilaChatService { return assistantMessage; } - private async enqueueFinalState() { - await this.enqueue({ - type: "state", - reasoning: "final", - value: this._aila.lesson.plan, - }); - } - private async enqueueMessageId(messageId: string) { await this.enqueue({ type: "id", @@ -338,21 +349,9 @@ export class AilaChat implements AilaChatService { } public async createChatCompletionObjectStream(messages: Message[]) { - const schema = z.object({ - type: z.literal("llmMessage"), - patches: z - .array(LLMPatchDocumentSchema) - .describe( - "This is the set of patches you have generated to edit the lesson plan. Follow the instructions in the system prompt to ensure that you produce a valid patch. For instance, if you are providing a patch to add a cycle, the op should be 'add' and the value should be the JSON object representing the full, valid cycle. The same applies for all of the other parts of the lesson plan. This should not include more than one 'add' patch for the same section of the lesson plan. These edits will overwrite each other and result in unexpected results. If you want to do multiple updates on the same section, it is best to generate one 'add' patch with all of your edits included.", - ), - prompt: TextDocumentSchema.describe( - "If you imagine the user talking to you, this is where you would put your human-readable reply that would explain the changes you have made (if any), ask them questions, and prompt them to send their next message. This should not contain any of the lesson plan content. That should all be delivered in patches.", - ), - }); - return this._llmService.createChatCompletionObjectStream({ model: this._aila.options.model ?? DEFAULT_MODEL, - schema, + schema: LLMMessageSchema, schemaName: "response", messages, temperature: this._aila.options.temperature ?? DEFAULT_TEMPERATURE, @@ -360,10 +359,6 @@ export class AilaChat implements AilaChatService { } public async complete() { - await this.enqueue({ - type: "comment", - value: "CHAT_COMPLETE", - }); await this.reportUsageMetrics(); this.applyEdits(); const assistantMessage = this.appendAssistantMessage(); @@ -372,6 +367,10 @@ export class AilaChat implements AilaChatService { await this.moderate(); await this.persistChat(); await this.persistGeneration("SUCCESS"); + await this.enqueue({ + type: "comment", + value: "CHAT_COMPLETE", + }); } public async saveSnapshot({ messageId }: { messageId: string }) { @@ -415,7 +414,6 @@ export class AilaChat implements AilaChatService { public async setupGeneration() { await this.startNewGeneration(); - await this.persistChat(); await this.persistGeneration("REQUESTED"); } diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 1f171825c..3fc01419b 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -1,3 +1,4 @@ +import { aiLogger } from "@oakai/logger"; import { ReadableStreamDefaultController } from "stream/web"; import invariant from "tiny-invariant"; @@ -6,6 +7,7 @@ import { AilaChatError } from "../AilaError"; import { AilaChat } from "./AilaChat"; import { PatchEnqueuer } from "./PatchEnqueuer"; +const log = aiLogger("aila:stream"); export class AilaStreamHandler { private _chat: AilaChat; private _controller?: ReadableStreamDefaultController; @@ -43,15 +45,18 @@ export class AilaStreamHandler { } } catch (e) { this.handleStreamError(e); + log.info("Stream error", e, this._chat.iteration, this._chat.id); } finally { this._isStreaming = false; try { await this._chat.complete(); + log.info("Chat completed", this._chat.iteration, this._chat.id); } catch (e) { this._chat.aila.errorReporter?.reportError(e); throw new AilaChatError("Chat completion failed", { cause: e }); } finally { this.closeController(); + log.info("Stream closed", this._chat.iteration, this._chat.id); } } } @@ -94,6 +99,7 @@ export class AilaStreamHandler { await this.fetchChunkFromStream(); } catch (error) { await this._chat.generationFailed(error); + throw error; } } diff --git a/packages/aila/src/core/chat/PatchEnqueuer.test.ts b/packages/aila/src/core/chat/PatchEnqueuer.test.ts index 4d8283c8e..fd2729f39 100644 --- a/packages/aila/src/core/chat/PatchEnqueuer.test.ts +++ b/packages/aila/src/core/chat/PatchEnqueuer.test.ts @@ -19,6 +19,7 @@ describe("PatchEnqueuer", () => { type: "patch", reasoning: "generated", value: { op: "add", path, value }, + status: "complete", })}\n␞\n`; expect(controller.enqueue).toHaveBeenCalledWith(expectedPatch); }); diff --git a/packages/aila/src/core/chat/PatchEnqueuer.ts b/packages/aila/src/core/chat/PatchEnqueuer.ts index 36f210cb8..3978630ed 100644 --- a/packages/aila/src/core/chat/PatchEnqueuer.ts +++ b/packages/aila/src/core/chat/PatchEnqueuer.ts @@ -65,6 +65,7 @@ export class PatchEnqueuer { type: "patch", reasoning: "generated", value: { op: "add", path, value }, + status: "complete", }; } diff --git a/packages/aila/src/core/lesson/AilaLesson.ts b/packages/aila/src/core/lesson/AilaLesson.ts index 48159b9a8..4981c8b59 100644 --- a/packages/aila/src/core/lesson/AilaLesson.ts +++ b/packages/aila/src/core/lesson/AilaLesson.ts @@ -1,3 +1,4 @@ +import { aiLogger } from "@oakai/logger"; import { deepClone } from "fast-json-patch"; import { AilaCategorisation } from "../../features/categorisation/categorisers/AilaCategorisation"; @@ -11,6 +12,8 @@ import { LooseLessonPlan } from "../../protocol/schema"; import { AilaLessonService, AilaServices } from "../AilaServices"; import { Message } from "../chat"; +const log = aiLogger("aila:lesson"); + export class AilaLesson implements AilaLessonService { private _aila: AilaServices; private _plan: LooseLessonPlan; @@ -28,6 +31,7 @@ export class AilaLesson implements AilaLessonService { lessonPlan?: LooseLessonPlan; categoriser?: AilaCategorisationFeature; }) { + log.info("Creating AilaLesson", lessonPlan?.title); this._aila = aila; this._plan = lessonPlan ?? {}; this._categoriser = @@ -47,6 +51,10 @@ export class AilaLesson implements AilaLessonService { this._plan = plan; } + public setPlan(plan: LooseLessonPlan) { + this._plan = plan; + } + public get hasSetInitialState(): boolean { return this._hasSetInitialState; } @@ -72,13 +80,26 @@ export class AilaLesson implements AilaLessonService { public applyPatches(patches: string) { let workingLessonPlan = deepClone(this._plan); + const beforeKeys = Object.keys(workingLessonPlan).filter( + (k) => workingLessonPlan[k], + ); + log.info( + "Apply patches: Lesson state before:", + `${beforeKeys.length} keys`, + beforeKeys.join("|"), + ); // TODO do we need to apply all patches even if they are partial? - const { validPatches, partialPatches } = extractPatches(patches, 100); + const { validPatches, partialPatches } = extractPatches(patches); for (const patch of partialPatches) { this._invalidPatches.push(patch); } + if (this._invalidPatches.length > 0) { + // This should never occur server-side. If it does, we should log it. + log.warn("Invalid patches found. Not applying", this._invalidPatches); + } + for (const patch of validPatches) { const newWorkingLessonPlan = applyLessonPlanPatch( workingLessonPlan, @@ -91,12 +112,23 @@ export class AilaLesson implements AilaLessonService { for (const patch of validPatches) { this._appliedPatches.push(patch); + log.info("Applied patch", patch.value.path); } + const afterKeys = Object.keys(workingLessonPlan).filter( + (k) => workingLessonPlan[k], + ); + log.info( + "Apply patches: Lesson state after:", + `${afterKeys.length} keys`, + afterKeys.join("|"), + ); + this._plan = workingLessonPlan; } public async setUpInitialLessonPlan(messages: Message[]) { + log.info("Setting up initial lesson plan", this._plan.title); const shouldCategoriseBasedOnInitialMessages = Boolean( !this._plan.subject && !this._plan.keyStage && !this._plan.title, ); diff --git a/packages/aila/src/features/persistence/AilaPersistence.ts b/packages/aila/src/features/persistence/AilaPersistence.ts index bb9994b3f..98b46c7f5 100644 --- a/packages/aila/src/features/persistence/AilaPersistence.ts +++ b/packages/aila/src/features/persistence/AilaPersistence.ts @@ -34,7 +34,15 @@ export abstract class AilaPersistence { } protected createChatPayload(): ChatPersistencePayload { - const { id, userId, messages, isShared, relevantLessons } = this._chat; + const { + id, + userId, + messages, + isShared, + relevantLessons, + iteration, + createdAt, + } = this._chat; invariant(userId, "userId is required for chat persistence"); @@ -48,7 +56,9 @@ export abstract class AilaPersistence { subject, keyStage, topic, - createdAt: Date.now(), + createdAt: createdAt ? createdAt.getTime() : Date.now(), + updatedAt: Date.now(), + iteration: iteration ? iteration + 1 : 1, isShared, path: `/aila/${id}`, lessonPlan: lesson.plan, diff --git a/packages/aila/src/features/persistence/adaptors/prisma/index.ts b/packages/aila/src/features/persistence/adaptors/prisma/index.ts index 48fa4df82..698b9cafe 100644 --- a/packages/aila/src/features/persistence/adaptors/prisma/index.ts +++ b/packages/aila/src/features/persistence/adaptors/prisma/index.ts @@ -11,7 +11,11 @@ import { AilaChatService, AilaServices, } from "../../../../core"; -import { AilaPersistedChat, chatSchema } from "../../../../protocol/schema"; +import { + AilaPersistedChat, + LessonPlanKeys, + chatSchema, +} from "../../../../protocol/schema"; import { AilaGeneration } from "../../../generation"; const log = aiLogger("aila:persistence"); @@ -55,7 +59,12 @@ export class AilaPrismaPersistence extends AilaPersistence { } async upsertChat(): Promise { + const currentIteration = this._chat.iteration; const payload = this.createChatPayload(); + const keys = (Object.keys(payload.lessonPlan) as LessonPlanKeys[]).filter( + (k) => payload.lessonPlan[k], + ); + if (!payload.id || !payload.userId) { log.info("No ID or userId found for chat. Not persisting."); return; @@ -71,11 +80,19 @@ export class AilaPrismaPersistence extends AilaPersistence { userId: payload.userId, appId: "lesson-planner", output: payload, + createdAt: new Date(), }, update: { output: payload, + updatedAt: new Date(), }, }); + log.info( + `Chat updated from ${currentIteration} to ${payload.iteration}`, + `${keys.length}`, + keys.join("|"), + payload.id, + ); } async upsertGeneration(generation?: AilaGeneration): Promise { diff --git a/packages/aila/src/protocol/jsonPatchProtocol.ts b/packages/aila/src/protocol/jsonPatchProtocol.ts index cb3a0e9f7..834092ec0 100644 --- a/packages/aila/src/protocol/jsonPatchProtocol.ts +++ b/packages/aila/src/protocol/jsonPatchProtocol.ts @@ -7,7 +7,8 @@ import { deepClone, JsonPatchError, } from "fast-json-patch"; -import { ZodError, z } from "zod"; +import untruncateJson from "untruncate-json"; +import { z } from "zod"; import zodToJsonSchema from "zod-to-json-schema"; import { @@ -282,6 +283,7 @@ export const PatchDocumentSchema = z.object({ type: z.literal("patch"), reasoning: z.string().optional(), value: JsonPatchValueSchema, + status: z.string().optional(), }); // This is the schema that we send when requesting Structured Outputs @@ -304,6 +306,7 @@ export const LLMPatchDocumentSchema = z.object({ PatchCycleForLLM, JsonPatchRemoveSchemaForLLM, ]), + status: z.literal("complete"), }); export type PatchDocument = z.infer; @@ -312,6 +315,7 @@ const PatchDocumentOptionalSchema = z.object({ type: z.literal("patch"), reasoning: z.string(), value: JsonPatchValueOptionalSchema, + status: z.literal("complete").optional(), }); export type PatchDocumentOptional = z.infer; @@ -320,6 +324,7 @@ export const PromptDocumentSchema = z.object({ type: z.literal("prompt"), message: z.string(), options: z.array(z.object({ id: z.string(), title: z.string() })).optional(), + status: z.literal("complete").optional(), }); export const PromptDocumentSchemaWithoutOptions = z.object({ @@ -390,6 +395,7 @@ export const UnknownDocumentSchema = z.object({ type: z.literal("unknown"), value: z.unknown().optional(), error: z.unknown().optional(), + status: z.string().optional(), }); export type UnknownDocument = z.infer; @@ -506,14 +512,34 @@ export type MessagePart = z.infer; export const LLMMessageSchema = z.object({ type: z.literal("llmMessage"), - patches: z.array(PatchDocumentSchema), - prompt: TextDocumentSchema, + sectionsToEdit: z + .array(z.string()) + .describe( + "The sections of the lesson plan that you are considering editing. This should be an array of strings, where each string is the key of the section you are proposing to edit. For example, if you are proposing to edit the 'priorKnowledge' section, you should include 'priorKnowledge' in this array. If you are proposing to edit multiple sections, you should include all of the keys in this array.", + ), + patches: z + .array(LLMPatchDocumentSchema) + .describe( + "This is the set of patches you have generated to edit the lesson plan. Follow the instructions in the system prompt to ensure that you produce a valid patch. For instance, if you are providing a patch to add a cycle, the op should be 'add' and the value should be the JSON object representing the full, valid cycle. The same applies for all of the other parts of the lesson plan. This should not include more than one 'add' patch for the same section of the lesson plan. These edits will overwrite each other and result in unexpected results. If you want to do multiple updates on the same section, it is best to generate one 'add' patch with all of your edits included.", + ), + sectionsEdited: z + .array(z.string()) + .describe( + "The sections of the lesson plan that you are actually edited. This should be an array of strings, where each string is the key of the section you are proposing to edit. For example, if you edited the 'priorKnowledge' section, you should include 'priorKnowledge' in this array. If you edited multiple sections, you should include all of the keys in this array.", + ), + prompt: TextDocumentSchema.describe( + "If you imagine the user talking to you, this is where you would put your human-readable reply that would explain the changes you have made (if any), ask them questions, and prompt them to send their next message. This should not contain any of the lesson plan content. That should all be delivered in patches. If you have made edits, you should respond to the user by asking if they are happy with the changes you have made if any, and prompt them with what they can do next", + ), + status: z.literal("complete"), }); const LLMMessageSchemaWhileStreaming = z.object({ type: z.literal("llmMessage"), + sectionsToEdit: z.array(z.string()).optional(), patches: z.array(z.object({}).passthrough()).optional(), + sectionsEdited: z.array(z.string()).optional(), prompt: TextDocumentSchema.optional(), + status: z.literal("complete").optional(), }); function tryParseJson(str: string): { @@ -530,11 +556,25 @@ function tryParseJson(str: string): { } return { parsed, isPartial: false }; } catch (e) { - // If parsing fails, assume it's partial - return { parsed: null, isPartial: true }; + try { + // Is this a JSON streaming in? + const parsedTruncated = JSON.parse(untruncateJson(str)); + if (isObject(parsedTruncated) && "type" in parsedTruncated) { + return { parsed: parsedTruncated, isPartial: true }; + } else { + throw new Error("The parsed JSON object does not have a type"); + } + } catch (e) { + // If parsing fails, assume it's partial + return { parsed: null, isPartial: true }; + } } } +function isObject(value: unknown): value is { type: string } { + return typeof value === "object" && value !== null && "type" in value; +} + export function tryParsePart( obj: object, ): MessagePartDocument | UnknownDocument { @@ -564,7 +604,6 @@ export function tryParsePatch(obj: object): PatchDocument | UnknownDocument { const patchDocument: PatchDocument = parsed.data; return patchDocument; } else { - log.info("Unable to parse patch", parsed, parsed.error); return { type: "unknown", value: JSON.stringify(obj), error: parsed.error }; } } @@ -629,7 +668,7 @@ export function parseMessageRow(row: string, index: number): MessagePart[] { result.push({ type: "message-part", document: parsedPatch, - isPartial, + isPartial: parsedPatch.status !== "complete", id: `${index}-patch-${i}`, }); i++; @@ -640,7 +679,7 @@ export function parseMessageRow(row: string, index: number): MessagePart[] { result.push({ type: "message-part", document: parsedPrompt, - isPartial, + isPartial: llmMessage.status !== "complete", id: `${index}-prompt`, }); } @@ -681,61 +720,32 @@ export function parseMessageParts(content: string): MessagePart[] { return messageParts; } -export function extractPatches( - edit: string, - mostRecent: number = 2, -): { +export function extractPatches(edit: string): { validPatches: PatchDocument[]; partialPatches: PatchDocument[]; } { - const validPatches: PatchDocument[] = []; - const partialPatches: PatchDocument[] = []; - - const messageParts = parseMessageParts(edit); - const relevantParts = messageParts.slice(-mostRecent); - - for (const part of relevantParts) { - if (part.document.type === "patch") { - try { - const validatedPatch = PatchDocumentSchema.parse(part.document); - if (part.isPartial) { - partialPatches.push(validatedPatch); - } else { - validPatches.push(validatedPatch); - } - } catch (e) { - if (e instanceof ZodError) { - log.info("Failed to parse patch due to Zod validation errors:", part); - e.errors.forEach((error, index) => { - log.info(`Error ${index + 1}:`); - log.info(` Path: ${error.path.join(".")}`); - log.info(` Message: ${error.message}`); - if (error.code) log.info(` Code: ${error.code}`); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const errorValue = error.path.reduce((obj: any, key) => { - if (obj && typeof obj === "object" && key in obj) { - return (obj as Record)[key]; - } - return undefined; - }, part); - log.error(` Invalid value:`, errorValue); - }); - } else { - log.error("Failed to parse patch:", e); - } - - Sentry.withScope(function (scope) { - scope.setLevel("error"); - if (e instanceof ZodError) { - scope.setExtra("zodErrors", e.errors); - } - scope.setExtra("parsedData", part); - Sentry.captureException(e); - }); - } - } + const parts: MessagePart[] | undefined = parseMessageParts(edit); + + if (!parts) { + // Handle parsing failure + throw new Error("Failed to parse the edit content"); } + const patchMessageParts: MessagePart[] = parts.filter( + (p) => p.document.type === "patch", + ); + const validPatches: PatchDocument[] = patchMessageParts + .filter((p) => !p.isPartial) + .map((p) => p.document as PatchDocument); + const partialPatches: PatchDocument[] = patchMessageParts + .filter((p) => p.isPartial) + .map((p) => p.document as PatchDocument); + + log.info( + "Extracted patches", + validPatches.map((i) => i.value.path), + partialPatches.map((i) => i.value.path), + ); return { validPatches, partialPatches }; } @@ -756,6 +766,7 @@ export function applyLessonPlanPatch( lessonPlan: LooseLessonPlan, command: JsonPatchDocument, ) { + log.info("Apply patch", JSON.stringify(command)); let updatedLessonPlan = { ...lessonPlan }; if (command.type !== "patch") return lessonPlan; const patch = command.value as Operation; @@ -775,8 +786,17 @@ export function applyLessonPlanPatch( extra["index"] = e.index; extra["operation"] = e.operation; extra["tree"] = e.tree; + log.error("JSON Patch Error:", e, extra); + } else if (e instanceof z.ZodError) { + log.error( + "Zod Error:", + e.errors + .map((err) => `${err.path.join(".")}: ${err.message}`) + .join(", "), + ); + } else { + log.error("Failed to apply patch", patch, e); } - log.error("Failed to apply patch", patch, e); Sentry.withScope(function (scope) { scope.setLevel("info"); Sentry.captureException(e, { extra }); diff --git a/packages/aila/src/protocol/schema.ts b/packages/aila/src/protocol/schema.ts index 390842fff..f25bd115c 100644 --- a/packages/aila/src/protocol/schema.ts +++ b/packages/aila/src/protocol/schema.ts @@ -10,7 +10,9 @@ export const BasedOnSchema = z ), title: z.string().describe("The human-readable title of the lesson."), }) - .describe("A reference to a lesson plan that this lesson is based on."); + .describe( + "A reference to a lesson plan that this lesson is based on. This value should only be set if the user has explicitly chosen to base their lesson on an existing lesson plan by selecting one from a selection of options, otherwise this should be blank.", + ); export const BasedOnOptionalSchema = z.object({ id: z.string().optional(), @@ -78,6 +80,9 @@ export const MisconceptionsOptionalSchema = z.array( export type Misconception = z.infer; export type MisconceptionOptional = z.infer; +export type MisconceptionsOptional = z.infer< + typeof MisconceptionsOptionalSchema +>; export const QuizQuestionOptionalSchema = z.object({ question: z.string().optional(), @@ -501,24 +506,27 @@ export const LessonPlanSchemaWhilstStreaming = LessonPlanSchema; // TODO old - refactor these to the new types export type LooseLessonPlan = z.infer; -export type LessonPlanKeys = - | "title" - | "subject" - | "keyStage" - | "topic" - | "learningOutcome" - | "learningCycles" - | "priorKnowledge" - | "keyLearningPoints" - | "misconceptions" - | "keywords" - | "basedOn" - | "starterQuiz" - | "exitQuiz" - | "cycle1" - | "cycle2" - | "cycle3" - | "additionalMaterials"; +export const LessonPlanKeysSchema = z.enum([ + "title", + "subject", + "keyStage", + "topic", + "learningOutcome", + "learningCycles", + "priorKnowledge", + "keyLearningPoints", + "misconceptions", + "keywords", + "basedOn", + "starterQuiz", + "exitQuiz", + "cycle1", + "cycle2", + "cycle3", + "additionalMaterials", +]); + +export type LessonPlanKeys = z.infer; export const quizSchema = z.array(QuizSchema); export const cycleSchema = CycleSchema; @@ -592,6 +600,8 @@ export const chatSchema = z relevantLessons: z.array(AilaRagRelevantLessonSchema).optional(), isShared: z.boolean().optional(), createdAt: z.union([z.date(), z.number()]), + updatedAt: z.union([z.date(), z.number()]).optional(), + iteration: z.number().optional(), startingMessage: z.string().optional(), messages: z.array( z @@ -623,6 +633,8 @@ export const chatSchemaWithMissingMessageIds = z lessonPlan: LessonPlanSchemaWhilstStreaming, isShared: z.boolean().optional(), createdAt: z.union([z.date(), z.number()]), + updatedAt: z.union([z.date(), z.number()]).optional(), + iteration: z.number().optional(), startingMessage: z.string().optional(), messages: z.array( z @@ -646,3 +658,13 @@ export const chatSchemaWithMissingMessageIds = z export type AilaPersistedChatWithMissingMessageIds = z.infer< typeof chatSchemaWithMissingMessageIds >; + +export type LessonPlanSectionWhileStreaming = + | BasedOnOptional + | MisconceptionsOptional + | KeywordOptional[] + | QuizOptional + | CycleOptional + | string + | string[] + | number; diff --git a/packages/logger/index.ts b/packages/logger/index.ts index c59bf75f5..02e00c6ff 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -16,12 +16,14 @@ type ChildKey = | "aila" | "aila:analytics" | "aila:errors" + | "aila:lesson" | "aila:llm" | "aila:moderation" | "aila:moderation:response" | "aila:persistence" | "aila:prompt" | "aila:protocol" + | "aila:stream" | "aila:rag" | "aila:testing" | "analytics" From adc1efa686a7cc332a1755a3e77f5e50a831df7d Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 11:41:09 +0000 Subject: [PATCH 039/127] fix: reintroduce custom eslint (#284) Co-authored-by: Adam Howard <91115+codeincontext@users.noreply.github.com> --- .eslintrc.cjs | 7 +- apps/nextjs/.eslintrc.cjs | 3 + apps/nextjs/package.json | 2 +- package.json | 2 + packages/aila/.eslintrc.cjs | 24 +- packages/db/tsconfig.json | 2 +- packages/eslint-config-custom/index.js | 28 +- packages/eslint-config-custom/package.json | 13 +- pnpm-lock.yaml | 940 +++++++++++---------- 9 files changed, 553 insertions(+), 468 deletions(-) diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 691d8e4f4..8e608153e 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -1,7 +1,7 @@ /** @type {import("eslint").Linter.Config} */ module.exports = { root: true, - parser: "@typescript-eslint/parser", + extends: ["eslint-config-custom"], parserOptions: { tsconfigRootDir: __dirname, project: [ @@ -10,9 +10,4 @@ module.exports = { "./packages/*/tsconfig.json", ], }, - plugins: ["@typescript-eslint"], - extends: ["plugin:@typescript-eslint/recommended"], - rules: { - "no-console": "warn" - } }; diff --git a/apps/nextjs/.eslintrc.cjs b/apps/nextjs/.eslintrc.cjs index cc5073297..d815e14a5 100644 --- a/apps/nextjs/.eslintrc.cjs +++ b/apps/nextjs/.eslintrc.cjs @@ -16,4 +16,7 @@ module.exports = { }, ], }, + parserOptions: { + project: __dirname + "/tsconfig.json", + }, }; diff --git a/apps/nextjs/package.json b/apps/nextjs/package.json index 6afe0a841..fd0e25a4f 100644 --- a/apps/nextjs/package.json +++ b/apps/nextjs/package.json @@ -149,7 +149,7 @@ "concurrently": "^8.2.2", "dotenv-cli": "^6.0.0", "eslint": "^8.56.0", - "eslint-config-next": "14.0.4", + "eslint-config-next": "15.0.1", "eslint-plugin-storybook": "^0.8.0", "graphql": "^16.9.0", "jest": "^29.7.0", diff --git a/package.json b/package.json index 511836859..d504cac36 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "doppler:run:stg": "doppler run -c stg --silent", "format": "prettier --write \"**/*.{ts,tsx,md}\"", "lint": "turbo lint", + "lint:fix": "pnpm lint -- --fix", "prompts": "turbo prompts", "prompts:dev": "turbo prompts:dev", "sort-package-json": "sort-package-json \"package.json\" \"packages/*/package.json\" \"apps/*/package.json\"", @@ -48,6 +49,7 @@ "@types/jest": "^29.5.12", "autoprefixer": "^10.4.16", "eslint-config-custom": "0.0.0", + "eslint-plugin-turbo": "^2.2.3", "husky": "^8.0.3", "lint-staged": "^15.2.0", "next": "14.2.5", diff --git a/packages/aila/.eslintrc.cjs b/packages/aila/.eslintrc.cjs index c057acbbf..9526c3c79 100644 --- a/packages/aila/.eslintrc.cjs +++ b/packages/aila/.eslintrc.cjs @@ -1,25 +1,7 @@ /** @type {import("eslint").Linter.Config} */ module.exports = { - extends: ["../../.eslintrc.cjs", "next"], - //plugins: ["import"], - rules: { - // ... other rules ... - // "import/order": [ - // "error", - // { - // groups: [ - // "builtin", - // "external", - // "internal", - // "parent", - // "sibling", - // "index", - // ], - // alphabetize: { - // order: "asc", - // caseInsensitive: true, - // }, - // }, - // ], + extends: ["eslint-config-custom"], + parserOptions: { + project: __dirname + "/tsconfig.json", }, }; diff --git a/packages/db/tsconfig.json b/packages/db/tsconfig.json index e89d913bb..c4b45432a 100644 --- a/packages/db/tsconfig.json +++ b/packages/db/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../../tsconfig.json", - "include": ["index.ts", "prisma/**/*.ts"] + "include": ["index.ts", "prisma/**/*.ts", "client/index.ts"] } diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index 63e2d80c1..76d707d25 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -1,6 +1,6 @@ module.exports = { extends: ["eslint:recommended", "prettier"], - plugins: ["turbo", "@typescript-eslint"], + plugins: ["turbo", "@typescript-eslint",], ignorePatterns: ["node_modules", "dist", "../../node_modules", "../../dist"], root: true, env: { @@ -20,9 +20,30 @@ module.exports = { parser: "@typescript-eslint/parser", files: ["*.{ts,tsx}"], rules: { - "@typescript-eslint/quotes": ["error", "double"], + "no-console": "warn", + "no-extra-boolean-cast": "warn", + "no-useless-escape": "warn", + "no-unsafe-finally": "warn", + "no-constant-condition": "warn", + "no-prototype-builtins": "warn", + "no-inner-declarations": "warn", + "@typescript-eslint/no-unsafe-enum-comparison": "warn", + "@typescript-eslint/no-unnecessary-type-assertion": "warn", + "@typescript-eslint/consistent-type-imports": "warn", "@typescript-eslint/comma-dangle": "off", "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/require-await": "warn", + "@typescript-eslint/no-unsafe-return": "warn", + "@typescript-eslint/no-misused-promises": "warn", + "@typescript-eslint/no-unsafe-argument": "warn", + "@typescript-eslint/no-unsafe-assignment": "warn", + "@typescript-eslint/no-floating-promises": "warn", + "@typescript-eslint/unbound-method": "off", + "@typescript-eslint/no-unsafe-member-access": "warn", + "@typescript-eslint/restrict-template-expressions": "warn", + "@typescript-eslint/no-redundant-type-constituents": "warn", + "@typescript-eslint/await-thenable": "warn", + "@typescript-eslint/no-unsafe-call": "warn", "@typescript-eslint/explicit-function-return-type": [ "off", { @@ -44,7 +65,4 @@ module.exports = { }, }, ], - rules: { - quotes: ["error", "double"], - }, }; diff --git a/packages/eslint-config-custom/package.json b/packages/eslint-config-custom/package.json index 678aabdff..b4ca03cb0 100644 --- a/packages/eslint-config-custom/package.json +++ b/packages/eslint-config-custom/package.json @@ -14,15 +14,16 @@ "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", "@typescript-eslint/typescript-estree": "^7.18.0", - "eslint-config-next": "14.0.4", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-jest": "^27.6.0", - "eslint-plugin-react": "^7.33.2", - "eslint-plugin-react-hooks": "^4.5.0", - "eslint-plugin-turbo": "^1.11.2" + "eslint-config-next": "15.0.1", + "eslint-plugin-jest": "^28.8.3", + "eslint-plugin-react": "^7.37.2", + "eslint-plugin-react-hooks": "^5.0.0", + "eslint-plugin-turbo": "^2.2.3" }, "devDependencies": { "eslint": "^8.56.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", "prettier": "^3.1.1", "typescript": "5.3.3" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 061c87228..d655dc25a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: eslint-config-custom: specifier: 0.0.0 version: link:packages/eslint-config-custom + eslint-plugin-turbo: + specifier: ^2.2.3 + version: 2.2.3(eslint@8.56.0) husky: specifier: ^8.0.3 version: 8.0.3 @@ -58,7 +61,7 @@ importers: version: 8.4.35 prettier: specifier: ^3.1.1 - version: 3.2.5 + version: 3.3.3 semantic-release: specifier: ^21.1.1 version: 21.1.2(typescript@5.3.3) @@ -439,8 +442,8 @@ importers: specifier: ^8.56.0 version: 8.56.0 eslint-config-next: - specifier: 14.0.4 - version: 14.0.4(eslint@8.56.0)(typescript@5.3.3) + specifier: 15.0.1 + version: 15.0.1(eslint@8.56.0)(typescript@5.3.3) eslint-plugin-storybook: specifier: ^0.8.0 version: 0.8.0(eslint@8.56.0)(typescript@5.3.3) @@ -773,30 +776,33 @@ importers: specifier: ^7.18.0 version: 7.18.0(typescript@5.3.3) eslint-config-next: - specifier: 14.0.4 - version: 14.0.4(eslint@8.56.0)(typescript@5.3.3) - eslint-config-prettier: - specifier: ^9.1.0 - version: 9.1.0(eslint@8.56.0) + specifier: 15.0.1 + version: 15.0.1(eslint@8.56.0)(typescript@5.3.3) eslint-plugin-jest: - specifier: ^27.6.0 - version: 27.9.0(@typescript-eslint/eslint-plugin@7.18.0)(eslint@8.56.0)(typescript@5.3.3) + specifier: ^28.8.3 + version: 28.8.3(@typescript-eslint/eslint-plugin@7.18.0)(eslint@8.56.0)(typescript@5.3.3) eslint-plugin-react: - specifier: ^7.33.2 - version: 7.33.2(eslint@8.56.0) + specifier: ^7.37.2 + version: 7.37.2(eslint@8.56.0) eslint-plugin-react-hooks: - specifier: ^4.5.0 - version: 4.6.0(eslint@8.56.0) + specifier: ^5.0.0 + version: 5.0.0(eslint@8.56.0) eslint-plugin-turbo: - specifier: ^1.11.2 - version: 1.12.4(eslint@8.56.0) + specifier: ^2.2.3 + version: 2.2.3(eslint@8.56.0) devDependencies: eslint: specifier: ^8.56.0 version: 8.56.0 + eslint-config-prettier: + specifier: ^9.1.0 + version: 9.1.0(eslint@8.56.0) + eslint-plugin-prettier: + specifier: ^5.2.1 + version: 5.2.1(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.3.3) prettier: specifier: ^3.1.1 - version: 3.2.5 + version: 3.3.3 typescript: specifier: 5.3.3 version: 5.3.3 @@ -2587,6 +2593,7 @@ packages: engines: {node: '>=6.9.0'} dependencies: regenerator-runtime: 0.14.1 + dev: false /@babel/runtime@7.23.9: resolution: {integrity: sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==} @@ -3264,7 +3271,6 @@ packages: /@eslint-community/regexpp@4.11.0: resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - dev: false /@eslint-community/regexpp@4.6.2: resolution: {integrity: sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==} @@ -4941,10 +4947,10 @@ packages: /@next/env@14.2.5: resolution: {integrity: sha512-/zZGkrTOsraVfYjGP8uM0p6r0BDT6xWpkjdVbcz66PJVSpwXX3yNiRycxAuDfBKGWBrZBXRuK/YVlkNgxHGwmA==} - /@next/eslint-plugin-next@14.0.4: - resolution: {integrity: sha512-U3qMNHmEZoVmHA0j/57nRfi3AscXNvkOnxDmle/69Jz/G0o/gWjXTDdlgILZdrxQ0Lw/jv2mPW8PGy0EGIHXhQ==} + /@next/eslint-plugin-next@15.0.1: + resolution: {integrity: sha512-bKWsMaGPbiFAaGqrDJvbE8b4Z0uKicGVcgOI77YM2ui3UfjHMr4emFPrZTLeZVchi7fT1mooG2LxREfUUClIKw==} dependencies: - glob: 7.1.7 + fast-glob: 3.3.1 /@next/swc-darwin-arm64@14.2.5: resolution: {integrity: sha512-/9zVxJ+K9lrzSGli1///ujyRfon/ZneeZ+v4ptpiPoOU+GKZnm8Wj8ELWU1Pm7GHltYRBklmXMTUqM/DqQ99FQ==} @@ -5683,6 +5689,11 @@ packages: dev: false optional: true + /@pkgr/core@0.1.1: + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + dev: true + /@pkgr/utils@2.3.1: resolution: {integrity: sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} @@ -7523,8 +7534,15 @@ packages: rollup: 3.29.5 dev: false + /@rtsao/scc@1.1.0: + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + /@rushstack/eslint-patch@1.10.4: + resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + /@rushstack/eslint-patch@1.7.2: resolution: {integrity: sha512-RbhOOTCNoCrbfkRyoXODZp75MlpiHMgbE5MEBZAnnnLyQNgrigEj4p0lzsMDyc1zVsJDLrivB58tgg3emX0eEA==} + dev: false /@sanity/client@6.21.3: resolution: {integrity: sha512-oE2+4kKRTZhFCc4IIsojkzKF0jIhsSYSRxkPZjScZ1k/EQ3Y2tEcQYiKwvvotzaXoaWsIL3RTpulE+R4iBYiBw==} @@ -7995,7 +8013,7 @@ packages: resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 - webpack: 5.93.0(esbuild@0.21.5) + webpack: 5.93.0 transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/core' @@ -8128,7 +8146,7 @@ packages: '@sentry/bundler-plugin-core': 2.22.3 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.93.0(esbuild@0.21.5) + webpack: 5.93.0 transitivePeerDependencies: - encoding - supports-color @@ -8418,7 +8436,7 @@ packages: magic-string: 0.30.10 path-browserify: 1.0.1 process: 0.11.10 - semver: 7.6.2 + semver: 7.6.3 storybook: 8.2.7 style-loader: 3.3.4(webpack@5.93.0) terser-webpack-plugin: 5.3.10(esbuild@0.21.5)(webpack@5.93.0) @@ -8470,7 +8488,7 @@ packages: globby: 14.0.1 jscodeshift: 0.15.2(@babel/preset-env@7.25.3) lodash: 4.17.21 - prettier: 3.2.5 + prettier: 3.3.3 recast: 0.23.9 tiny-invariant: 1.3.3 transitivePeerDependencies: @@ -8684,7 +8702,7 @@ packages: react-docgen: 7.0.3 react-dom: 18.2.0(react@18.2.0) resolve: 1.22.8 - semver: 7.6.2 + semver: 7.6.3 storybook: 8.2.7 tsconfig-paths: 4.2.0 typescript: 5.3.3 @@ -9538,27 +9556,6 @@ packages: typescript: 5.3.3 transitivePeerDependencies: - supports-color - dev: false - - /@typescript-eslint/parser@6.20.0(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-bYerPDF/H5v6V76MdMYhjwmwgMA+jlPVqjSDq2cRqMi8bP5sR3Z+RLOiOMad3nsnmDVmn2gAFCyNgh/dIrfP/w==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/scope-manager': 6.20.0 - '@typescript-eslint/types': 6.20.0 - '@typescript-eslint/typescript-estree': 6.20.0(typescript@5.3.3) - '@typescript-eslint/visitor-keys': 6.20.0 - debug: 4.3.7(supports-color@5.5.0) - eslint: 8.56.0 - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color /@typescript-eslint/parser@7.18.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} @@ -9579,7 +9576,6 @@ packages: typescript: 5.3.3 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/scope-manager@5.62.0: resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} @@ -9587,13 +9583,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - - /@typescript-eslint/scope-manager@6.20.0: - resolution: {integrity: sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.20.0 - '@typescript-eslint/visitor-keys': 6.20.0 + dev: true /@typescript-eslint/scope-manager@7.18.0: resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} @@ -9601,7 +9591,6 @@ packages: dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - dev: false /@typescript-eslint/type-utils@7.18.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} @@ -9621,20 +9610,15 @@ packages: typescript: 5.3.3 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/types@5.62.0: resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - - /@typescript-eslint/types@6.20.0: - resolution: {integrity: sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==} - engines: {node: ^16.0.0 || >=18.0.0} + dev: true /@typescript-eslint/types@7.18.0: resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} engines: {node: ^18.18.0 || >=20.0.0} - dev: false /@typescript-eslint/typescript-estree@5.62.0(typescript@5.3.3): resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} @@ -9655,27 +9639,7 @@ packages: typescript: 5.3.3 transitivePeerDependencies: - supports-color - - /@typescript-eslint/typescript-estree@6.20.0(typescript@5.3.3): - resolution: {integrity: sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: - '@typescript-eslint/types': 6.20.0 - '@typescript-eslint/visitor-keys': 6.20.0 - debug: 4.3.7(supports-color@5.5.0) - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.3.3) - typescript: 5.3.3 - transitivePeerDependencies: - - supports-color + dev: true /@typescript-eslint/typescript-estree@7.18.0(typescript@5.3.3): resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} @@ -9697,7 +9661,6 @@ packages: typescript: 5.3.3 transitivePeerDependencies: - supports-color - dev: false /@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} @@ -9717,6 +9680,7 @@ packages: transitivePeerDependencies: - supports-color - typescript + dev: true /@typescript-eslint/utils@7.18.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} @@ -9732,7 +9696,6 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: false /@typescript-eslint/visitor-keys@5.62.0: resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} @@ -9740,13 +9703,7 @@ packages: dependencies: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - - /@typescript-eslint/visitor-keys@6.20.0: - resolution: {integrity: sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==} - engines: {node: ^16.0.0 || >=18.0.0} - dependencies: - '@typescript-eslint/types': 6.20.0 - eslint-visitor-keys: 3.4.3 + dev: true /@typescript-eslint/visitor-keys@7.18.0: resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} @@ -9754,7 +9711,6 @@ packages: dependencies: '@typescript-eslint/types': 7.18.0 eslint-visitor-keys: 3.4.3 - dev: false /@uidotdev/usehooks@2.4.1(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-1I+RwWyS+kdv3Mv0Vmc+p0dPYH0DTRAo04HLyXReYBL9AeseDWUJyi4THuksBJcu9F0Pih69Ak150VDnqbVnXg==} @@ -10462,6 +10418,10 @@ packages: dependencies: dequal: 2.0.3 + /aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + /array-buffer-byte-length@1.0.1: resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} engines: {node: '>= 0.4'} @@ -10476,23 +10436,14 @@ packages: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} dev: false - /array-includes@3.1.6: - resolution: {integrity: sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.1.4 - es-abstract: 1.20.5 - get-intrinsic: 1.2.4 - is-string: 1.0.7 - - /array-includes@3.1.7: - resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + /array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 @@ -10500,24 +10451,26 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - /array.prototype.filter@1.0.3: - resolution: {integrity: sha512-VizNcj/RGJiUyQBgzwxzE5oHdeuXY5hSbbmKMlphj1cy1Vl7Pn2asCGbSrru6hSQjmCzqTBPVWAF/whmEOVHbw==} + /array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - es-array-method-boxes-properly: 1.0.0 - is-string: 1.0.7 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 + es-shim-unscopables: 1.0.2 - /array.prototype.findlastindex@1.2.4: - resolution: {integrity: sha512-hzvSHUshSpCflDR1QMUBLHGHP1VIEBegT4pix9H/Z92Xw3ySoy6c2qh7lJWTJnRJ8JCZ9bJNCgTyYaJGcJu6xQ==} + /array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-errors: 1.3.0 + es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 /array.prototype.flat@1.3.2: @@ -10529,15 +10482,6 @@ packages: es-abstract: 1.22.4 es-shim-unscopables: 1.0.2 - /array.prototype.flatmap@1.3.1: - resolution: {integrity: sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.1.4 - es-abstract: 1.20.5 - es-shim-unscopables: 1.0.0 - /array.prototype.flatmap@1.3.2: resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} engines: {node: '>= 0.4'} @@ -10547,14 +10491,15 @@ packages: es-abstract: 1.22.4 es-shim-unscopables: 1.0.2 - /array.prototype.tosorted@1.1.1: - resolution: {integrity: sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ==} + /array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 + es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - get-intrinsic: 1.2.4 /arraybuffer.prototype.slice@1.0.3: resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} @@ -10644,11 +10589,6 @@ packages: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} dev: false - /asynciterator.prototype@1.0.0: - resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==} - dependencies: - has-symbols: 1.0.3 - /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: false @@ -10720,8 +10660,8 @@ packages: - supports-color dev: true - /axe-core@4.7.0: - resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==} + /axe-core@4.10.2: + resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} /axios@1.6.8: @@ -10734,16 +10674,9 @@ packages: - debug dev: false - /axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} - dependencies: - dequal: 2.0.3 - - /axobject-query@4.0.0: - resolution: {integrity: sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==} - dependencies: - dequal: 2.0.3 - dev: false + /axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} /b4a@1.6.6: resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} @@ -12004,7 +11937,7 @@ packages: handlebars: 4.7.8 json-stringify-safe: 5.0.1 meow: 8.1.2 - semver: 7.6.2 + semver: 7.6.3 split: 1.0.1 dev: false @@ -12301,7 +12234,7 @@ packages: postcss-modules-scope: 3.2.0(postcss@8.4.38) postcss-modules-values: 4.0.0(postcss@8.4.38) postcss-value-parser: 4.2.0 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.93.0(esbuild@0.21.5) dev: true @@ -12397,6 +12330,30 @@ packages: whatwg-url: 11.0.0 dev: false + /data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + /data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + + /data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + is-data-view: 1.0.1 + /dataloader@2.2.2: resolution: {integrity: sha512-8YnDaaf7N3k/q5HnTJVuzSyLETjoZjVmHc4AeKAzOvKHEFQKcn64OKBfzHYtE9zGjctNM7V9I0MfnUVLpi7M5g==} dev: true @@ -12502,7 +12459,6 @@ packages: optional: true dependencies: ms: 2.1.2 - dev: false /debug@4.3.7(supports-color@5.5.0): resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} @@ -12656,13 +12612,6 @@ packages: engines: {node: '>=12'} dev: true - /define-properties@1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} - engines: {node: '>= 0.4'} - dependencies: - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - /define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -13048,13 +12997,6 @@ packages: objectorarray: 1.0.5 dev: true - /enhanced-resolve@5.15.0: - resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==} - engines: {node: '>=10.13.0'} - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - /enhanced-resolve@5.17.1: resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} engines: {node: '>=10.13.0'} @@ -13104,47 +13046,67 @@ packages: stackframe: 1.3.4 dev: true - /es-abstract@1.20.5: - resolution: {integrity: sha512-7h8MM2EQhsCA7pU/Nv78qOXFpD8Rhqd12gYiSJVkrH9+e8VuA8JlPJK/hQjjlLv6pJvx/z1iRFKzYb0XT/RuAQ==} + /es-abstract@1.22.4: + resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==} engines: {node: '>= 0.4'} dependencies: + array-buffer-byte-length: 1.0.1 + arraybuffer.prototype.slice: 1.0.3 + available-typed-arrays: 1.0.7 call-bind: 1.0.7 + es-define-property: 1.0.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.0.2 es-to-primitive: 1.2.1 - function-bind: 1.1.2 - function.prototype.name: 1.1.5 + function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 - get-symbol-description: 1.0.0 + get-symbol-description: 1.0.2 + globalthis: 1.0.3 gopd: 1.0.1 - has: 1.0.3 has-property-descriptors: 1.0.2 + has-proto: 1.0.1 has-symbols: 1.0.3 + hasown: 2.0.1 internal-slot: 1.0.7 + is-array-buffer: 3.0.4 is-callable: 1.2.7 is-negative-zero: 2.0.2 is-regex: 1.1.4 is-shared-array-buffer: 1.0.2 is-string: 1.0.7 + is-typed-array: 1.1.13 is-weakref: 1.0.2 object-inspect: 1.13.1 object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - safe-regex-test: 1.0.0 - string.prototype.trimend: 1.0.6 - string.prototype.trimstart: 1.0.6 + safe-array-concat: 1.1.0 + safe-regex-test: 1.0.3 + string.prototype.trim: 1.2.8 + string.prototype.trimend: 1.0.7 + string.prototype.trimstart: 1.0.7 + typed-array-buffer: 1.0.2 + typed-array-byte-length: 1.0.0 + typed-array-byte-offset: 1.0.1 + typed-array-length: 1.0.4 unbox-primitive: 1.0.2 + which-typed-array: 1.1.14 - /es-abstract@1.22.4: - resolution: {integrity: sha512-vZYJlk2u6qHYxBOTjAeg7qUxHdNfih64Uu2J8QqWgXZ2cri0ZpJAkzDUK/q593+mvKwlxyaxr6F1Q+3LKoQRgg==} + /es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} engines: {node: '>= 0.4'} dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 available-typed-arrays: 1.0.7 call-bind: 1.0.7 + data-view-buffer: 1.0.1 + data-view-byte-length: 1.0.1 + data-view-byte-offset: 1.0.0 es-define-property: 1.0.0 es-errors: 1.3.0 - es-set-tostringtag: 2.0.2 + es-object-atoms: 1.0.0 + es-set-tostringtag: 2.0.3 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 get-intrinsic: 1.2.4 @@ -13152,15 +13114,16 @@ packages: globalthis: 1.0.3 gopd: 1.0.1 has-property-descriptors: 1.0.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 - hasown: 2.0.1 + hasown: 2.0.2 internal-slot: 1.0.7 is-array-buffer: 3.0.4 is-callable: 1.2.7 - is-negative-zero: 2.0.2 + is-data-view: 1.0.1 + is-negative-zero: 2.0.3 is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 + is-shared-array-buffer: 1.0.3 is-string: 1.0.7 is-typed-array: 1.1.13 is-weakref: 1.0.2 @@ -13168,20 +13131,17 @@ packages: object-keys: 1.1.1 object.assign: 4.1.5 regexp.prototype.flags: 1.5.2 - safe-array-concat: 1.1.0 + safe-array-concat: 1.1.2 safe-regex-test: 1.0.3 - string.prototype.trim: 1.2.8 - string.prototype.trimend: 1.0.7 - string.prototype.trimstart: 1.0.7 + string.prototype.trim: 1.2.9 + string.prototype.trimend: 1.0.8 + string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.2 - typed-array-byte-length: 1.0.0 - typed-array-byte-offset: 1.0.1 - typed-array-length: 1.0.4 + typed-array-byte-length: 1.0.1 + typed-array-byte-offset: 1.0.2 + typed-array-length: 1.0.6 unbox-primitive: 1.0.2 - which-typed-array: 1.1.14 - - /es-array-method-boxes-properly@1.0.0: - resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + which-typed-array: 1.1.15 /es-define-property@1.0.0: resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} @@ -13207,29 +13167,34 @@ packages: stop-iteration-iterator: 1.0.0 dev: false - /es-iterator-helpers@1.0.17: - resolution: {integrity: sha512-lh7BsUqelv4KUbR5a/ZTaGGIMLCjPGPqJ6q+Oq24YP0RdyptX1uzm4vvaqzk7Zx3bpl/76YLTTDj9L7uYQ92oQ==} + /es-iterator-helpers@1.1.0: + resolution: {integrity: sha512-/SurEfycdyssORP/E+bj4sEu1CWw4EmLDsHynHwSXQ7utgbrMRWW195pTrCjFgFCddf/UkYm3oqKPRq5i8bJbw==} engines: {node: '>= 0.4'} dependencies: - asynciterator.prototype: 1.0.0 call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 es-errors: 1.3.0 - es-set-tostringtag: 2.0.2 + es-set-tostringtag: 2.0.3 function-bind: 1.1.2 get-intrinsic: 1.2.4 - globalthis: 1.0.3 + globalthis: 1.0.4 has-property-descriptors: 1.0.2 - has-proto: 1.0.1 + has-proto: 1.0.3 has-symbols: 1.0.3 internal-slot: 1.0.7 - iterator.prototype: 1.1.2 - safe-array-concat: 1.1.0 + iterator.prototype: 1.1.3 + safe-array-concat: 1.1.2 /es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + /es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + dependencies: + es-errors: 1.3.0 + /es-set-tostringtag@2.0.2: resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==} engines: {node: '>= 0.4'} @@ -13238,10 +13203,13 @@ packages: has-tostringtag: 1.0.2 hasown: 2.0.1 - /es-shim-unscopables@1.0.0: - resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + /es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} dependencies: - has: 1.0.3 + get-intrinsic: 1.2.4 + has-tostringtag: 1.0.2 + hasown: 2.0.2 /es-shim-unscopables@1.0.2: resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} @@ -13344,25 +13312,26 @@ packages: optionalDependencies: source-map: 0.6.1 - /eslint-config-next@14.0.4(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-9/xbOHEQOmQtqvQ1UsTQZpnA7SlDMBtuKJ//S4JnoyK3oGLhILKXdBgu/UO7lQo/2xOykQULS1qQ6p2+EpHgAQ==} + /eslint-config-next@15.0.1(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-3cYCrgbH6GS/ufApza7XCKz92vtq4dAdYhx++rMFNlH2cAV+/GsAKkrr4+bohYOACmzG2nAOR+uWprKC1Uld6A==} peerDependencies: - eslint: ^7.23.0 || ^8.0.0 + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 typescript: '>=3.3.1' peerDependenciesMeta: typescript: optional: true dependencies: - '@next/eslint-plugin-next': 14.0.4 - '@rushstack/eslint-patch': 1.7.2 - '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) + '@next/eslint-plugin-next': 15.0.1 + '@rushstack/eslint-patch': 1.10.4 + '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 - eslint-import-resolver-node: 0.3.6 - eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.29.1)(eslint@8.56.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) - eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0) - eslint-plugin-react: 7.33.2(eslint@8.56.0) - eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.31.0)(eslint@8.56.0) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) + eslint-plugin-jsx-a11y: 6.10.2(eslint@8.56.0) + eslint-plugin-react: 7.37.2(eslint@8.56.0) + eslint-plugin-react-hooks: 5.0.0(eslint@8.56.0) typescript: 5.3.3 transitivePeerDependencies: - eslint-import-resolver-webpack @@ -13375,26 +13344,18 @@ packages: eslint: '>=7.0.0' dependencies: eslint: 8.56.0 - dev: false - - /eslint-import-resolver-node@0.3.6: - resolution: {integrity: sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==} - dependencies: - debug: 3.2.7 - resolve: 1.22.4 - transitivePeerDependencies: - - supports-color + dev: true /eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} dependencies: debug: 3.2.7 - is-core-module: 2.13.1 + is-core-module: 2.15.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color - /eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.29.1)(eslint@8.56.0): + /eslint-import-resolver-typescript@3.5.2(eslint-plugin-import@2.31.0)(eslint@8.56.0): resolution: {integrity: sha512-zX4ebnnyXiykjhcBvKIf5TNvt8K7yX6bllTRZ14MiurKPjDpCAZujlszTdB8pcNXhZcOf+god4s9SjQa5GnytQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: @@ -13402,19 +13363,19 @@ packages: eslint-plugin-import: '*' dependencies: debug: 4.3.7(supports-color@5.5.0) - enhanced-resolve: 5.15.0 + enhanced-resolve: 5.17.1 eslint: 8.56.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) - get-tsconfig: 4.2.0 + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.18.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) + get-tsconfig: 4.7.5 globby: 13.1.2 - is-core-module: 2.13.1 + is-core-module: 2.15.1 is-glob: 4.0.3 synckit: 0.8.4 transitivePeerDependencies: - supports-color - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0): - resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + /eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0): + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -13434,54 +13395,56 @@ packages: eslint-import-resolver-webpack: optional: true dependencies: - '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.56.0)(typescript@5.3.3) debug: 3.2.7 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.29.1)(eslint@8.56.0) + eslint-import-resolver-typescript: 3.5.2(eslint-plugin-import@2.31.0)(eslint@8.56.0) transitivePeerDependencies: - supports-color - /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0): - resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==} + /eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0): + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 peerDependenciesMeta: '@typescript-eslint/parser': optional: true dependencies: - '@typescript-eslint/parser': 6.20.0(eslint@8.56.0)(typescript@5.3.3) - array-includes: 3.1.7 - array.prototype.findlastindex: 1.2.4 + '@rtsao/scc': 1.1.0 + '@typescript-eslint/parser': 7.18.0(eslint@8.56.0)(typescript@5.3.3) + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 eslint: 8.56.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.20.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) - hasown: 2.0.1 - is-core-module: 2.13.1 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.2)(eslint@8.56.0) + hasown: 2.0.2 + is-core-module: 2.15.1 is-glob: 4.0.3 minimatch: 3.1.2 - object.fromentries: 2.0.7 - object.groupby: 1.0.2 - object.values: 1.1.7 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.0 semver: 6.3.1 + string.prototype.trimend: 1.0.8 tsconfig-paths: 3.15.0 transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - /eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.18.0)(eslint@8.56.0)(typescript@5.3.3): - resolution: {integrity: sha512-QIT7FH7fNmd9n4se7FFKHbsLKGQiw885Ds6Y/sxKgCZ6natwCsXdgPOADnYVxN2QrRweF0FZWbJ6S7Rsn7llug==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + /eslint-plugin-jest@28.8.3(@typescript-eslint/eslint-plugin@7.18.0)(eslint@8.56.0)(typescript@5.3.3): + resolution: {integrity: sha512-HIQ3t9hASLKm2IhIOqnu+ifw7uLZkIlR7RYNv7fMcEi/p0CIiJmfriStQS2LDkgtY4nyLbIZAD+JL347Yc2ETQ==} + engines: {node: ^16.10.0 || ^18.12.0 || >=20.0.0} peerDependencies: - '@typescript-eslint/eslint-plugin': ^5.0.0 || ^6.0.0 || ^7.0.0 - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/eslint-plugin': ^6.0.0 || ^7.0.0 || ^8.0.0 + eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 jest: '*' peerDependenciesMeta: '@typescript-eslint/eslint-plugin': @@ -13490,68 +13453,90 @@ packages: optional: true dependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@7.18.0)(eslint@8.56.0)(typescript@5.3.3) - '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.3.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.56.0)(typescript@5.3.3) eslint: 8.56.0 transitivePeerDependencies: - supports-color - typescript dev: false - /eslint-plugin-jsx-a11y@6.8.0(eslint@8.56.0): - resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==} + /eslint-plugin-jsx-a11y@6.10.2(eslint@8.56.0): + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} engines: {node: '>=4.0'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 dependencies: - '@babel/runtime': 7.23.2 - aria-query: 5.3.0 - array-includes: 3.1.7 + aria-query: 5.3.2 + array-includes: 3.1.8 array.prototype.flatmap: 1.3.2 ast-types-flow: 0.0.8 - axe-core: 4.7.0 - axobject-query: 3.2.1 + axe-core: 4.10.2 + axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - es-iterator-helpers: 1.0.17 eslint: 8.56.0 - hasown: 2.0.1 + hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.2 - object.entries: 1.1.7 - object.fromentries: 2.0.7 + object.fromentries: 2.0.8 + safe-regex-test: 1.0.3 + string.prototype.includes: 2.0.1 + + /eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.56.0)(prettier@3.3.3): + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + dependencies: + eslint: 8.56.0 + eslint-config-prettier: 9.1.0(eslint@8.56.0) + prettier: 3.3.3 + prettier-linter-helpers: 1.0.0 + synckit: 0.9.2 + dev: true - /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0): - resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==} + /eslint-plugin-react-hooks@5.0.0(eslint@8.56.0): + resolution: {integrity: sha512-hIOwI+5hYGpJEc4uPRmz2ulCjAGD/N13Lukkh8cLV0i2IRk/bdZDYjgLVHj+U9Z704kLIdIO6iueGvxNur0sgw==} engines: {node: '>=10'} peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 dependencies: eslint: 8.56.0 - /eslint-plugin-react@7.33.2(eslint@8.56.0): - resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==} + /eslint-plugin-react@7.37.2(eslint@8.56.0): + resolution: {integrity: sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==} engines: {node: '>=4'} peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 dependencies: - array-includes: 3.1.6 - array.prototype.flatmap: 1.3.1 - array.prototype.tosorted: 1.1.1 + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.2 + array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.0.17 + es-iterator-helpers: 1.1.0 eslint: 8.56.0 estraverse: 5.3.0 - jsx-ast-utils: 3.3.3 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.6 - object.fromentries: 2.0.6 - object.hasown: 1.1.2 - object.values: 1.1.6 + object.entries: 1.1.8 + object.fromentries: 2.0.8 + object.values: 1.2.0 prop-types: 15.8.1 - resolve: 2.0.0-next.4 + resolve: 2.0.0-next.5 semver: 6.3.1 - string.prototype.matchall: 4.0.8 + string.prototype.matchall: 4.0.11 + string.prototype.repeat: 1.0.0 /eslint-plugin-storybook@0.8.0(eslint@8.56.0)(typescript@5.3.3): resolution: {integrity: sha512-CZeVO5EzmPY7qghO2t64oaFM+8FTaD4uzOEjHKp516exyTKo+skKAL9GI3QALS2BXhyALJjNtwbmr1XinGE8bA==} @@ -13569,8 +13554,8 @@ packages: - typescript dev: true - /eslint-plugin-turbo@1.12.4(eslint@8.56.0): - resolution: {integrity: sha512-3AGmXvH7E4i/XTWqBrcgu+G7YKZJV/8FrEn79kTd50ilNsv+U3nS2IlcCrQB6Xm2m9avGD9cadLzKDR1/UF2+g==} + /eslint-plugin-turbo@2.2.3(eslint@8.56.0): + resolution: {integrity: sha512-LHt35VwxthdGVO6hQRfvmFb6ee8/exAzAYWCy4o87Bnp7urltP8qg7xMd4dPSLAhtfnI2xSo1WgeVaR3MeItxw==} peerDependencies: eslint: '>6.6.0' dependencies: @@ -13861,6 +13846,10 @@ packages: /fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + /fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + dev: true + /fast-fifo@1.3.2: resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} dev: false @@ -14331,15 +14320,6 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - /function.prototype.name@1.1.5: - resolution: {integrity: sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - functions-have-names: 1.2.3 - /function.prototype.name@1.1.6: resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} engines: {node: '>= 0.4'} @@ -14463,13 +14443,6 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} - /get-symbol-description@1.0.0: - resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - /get-symbol-description@1.0.2: resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} engines: {node: '>= 0.4'} @@ -14478,14 +14451,10 @@ packages: es-errors: 1.3.0 get-intrinsic: 1.2.4 - /get-tsconfig@4.2.0: - resolution: {integrity: sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==} - /get-tsconfig@4.7.5: resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} dependencies: resolve-pkg-maps: 1.0.0 - dev: false /giget@1.2.3: resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} @@ -14560,16 +14529,6 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob@7.1.7: - resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Glob versions prior to v9 are no longer supported @@ -14626,6 +14585,13 @@ packages: dependencies: define-properties: 1.2.1 + /globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + dependencies: + define-properties: 1.2.1 + gopd: 1.0.1 + /globalyzer@0.1.0: resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} @@ -14896,6 +14862,10 @@ packages: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} + /has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + /has-symbols@1.0.3: resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} engines: {node: '>= 0.4'} @@ -14911,12 +14881,6 @@ packages: engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} dev: true - /has@1.0.3: - resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} - engines: {node: '>= 0.4.0'} - dependencies: - function-bind: 1.1.2 - /hash-base@3.0.4: resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} engines: {node: '>=4'} @@ -14946,6 +14910,12 @@ packages: dependencies: function-bind: 1.1.2 + /hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + dependencies: + function-bind: 1.1.2 + /hast-util-heading-rank@3.0.0: resolution: {integrity: sha512-EJKb8oMUXVHcWZTDepnr+WNbfnXKFNf9duMesmr4S8SXTJBJ9M4Yok08pu9vxdJwdlGRhVumk9mEhkEvKGifwA==} dependencies: @@ -15495,14 +15465,6 @@ packages: resolution: {integrity: sha512-v7cSY1J8ydZ0GyjUHqF+1bshJ6cnEVLo9EnjB8p+4HDRPZc9N5jjmvUV7NvEsqQOKyH0pmIBFWXVQbiS0+OBbA==} dev: false - /internal-slot@1.0.3: - resolution: {integrity: sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==} - engines: {node: '>= 0.4'} - dependencies: - get-intrinsic: 1.2.4 - has: 1.0.3 - side-channel: 1.0.4 - /internal-slot@1.0.7: resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} engines: {node: '>= 0.4'} @@ -15641,6 +15603,18 @@ packages: dependencies: hasown: 2.0.1 + /is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + dependencies: + hasown: 2.0.2 + + /is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + dependencies: + is-typed-array: 1.1.13 + /is-date-object@1.0.5: resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} engines: {node: '>= 0.4'} @@ -15777,6 +15751,10 @@ packages: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} + /is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + /is-npm@6.0.0: resolution: {integrity: sha512-JEjxbSmtPSt1c8XTkVrlujcXdKV1/tvuQ7GwKcAlyiVLeYFQ2VHat8xfrDJsIkhCdF/tZ7CiIR3sy141c6+gPQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -15862,6 +15840,12 @@ packages: dependencies: call-bind: 1.0.7 + /is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + /is-stream@1.1.0: resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} engines: {node: '>=0.10.0'} @@ -16079,8 +16063,9 @@ packages: resolution: {integrity: sha512-RaKa8RHmSay1GvkTLOYRT8Ju9/Cf0DRK9z7YzS14sID4e2hkP4eknzDhTtzTboO8shZIysbVEEnmjJEHxOVIMQ==} dev: false - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + /iterator.prototype@1.1.3: + resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} + engines: {node: '>= 0.4'} dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 @@ -16803,21 +16788,14 @@ packages: semver: 7.6.2 dev: true - /jsx-ast-utils@3.3.3: - resolution: {integrity: sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw==} - engines: {node: '>=4.0'} - dependencies: - array-includes: 3.1.7 - object.assign: 4.1.4 - /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} dependencies: - array-includes: 3.1.7 + array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 - object.values: 1.1.7 + object.values: 1.2.0 /jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} @@ -18310,12 +18288,6 @@ packages: dependencies: brace-expansion: 2.0.1 - /minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - dependencies: - brace-expansion: 2.0.1 - /minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -18964,15 +18936,6 @@ packages: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} - /object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - has-symbols: 1.0.3 - object-keys: 1.1.1 - /object.assign@4.1.5: resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} engines: {node: '>= 0.4'} @@ -18982,68 +18945,38 @@ packages: has-symbols: 1.0.3 object-keys: 1.1.1 - /object.entries@1.1.6: - resolution: {integrity: sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w==} + /object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-object-atoms: 1.0.0 - /object.entries@1.1.7: - resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==} + /object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - - /object.fromentries@2.0.6: - resolution: {integrity: sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.1.4 - es-abstract: 1.20.5 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 - /object.fromentries@2.0.7: - resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + /object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 - - /object.groupby@1.0.2: - resolution: {integrity: sha512-bzBq58S+x+uo0VjurFT0UktpKHOZmv4/xePiOA1nbB9pMqpGK7rUPNgf+1YC+7mE+0HzhTMqNUuCqvKhj6FnBw==} - dependencies: - array.prototype.filter: 1.0.3 - call-bind: 1.0.7 - define-properties: 1.2.1 - es-abstract: 1.22.4 - es-errors: 1.3.0 - - /object.hasown@1.1.2: - resolution: {integrity: sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw==} - dependencies: - define-properties: 1.2.1 - es-abstract: 1.22.4 - - /object.values@1.1.6: - resolution: {integrity: sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.1.4 - es-abstract: 1.20.5 + es-abstract: 1.23.3 - /object.values@1.1.7: - resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + /object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-object-atoms: 1.0.0 /objectorarray@1.0.5: resolution: {integrity: sha512-eJJDYkhJFFbBBAxeh8xW+weHlkI28n2ZdQV/J/DNfWfSKlGEf2xcfAbZTv3riEXHAhL9SVOTs2pRmXiSTf78xg==} @@ -19896,7 +19829,7 @@ packages: cosmiconfig: 9.0.0(typescript@5.3.3) jiti: 1.21.6 postcss: 8.4.38 - semver: 7.6.2 + semver: 7.6.3 webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - typescript @@ -20058,6 +19991,13 @@ packages: resolution: {integrity: sha512-D5n2lCfmACTpARhHPFh9eqpQCGjLor3khMkqzi/EhYeZ8+Xe3Q+6c5oZIpNMZQybsRm4ijn2ihQniue61juQhg==} dev: true + /prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + dependencies: + fast-diff: 1.3.0 + dev: true + /prettier-plugin-tailwindcss@0.5.11(@trivago/prettier-plugin-sort-imports@4.3.0)(prettier@3.2.5): resolution: {integrity: sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==} engines: {node: '>=14.21.3'} @@ -20116,6 +20056,11 @@ packages: engines: {node: '>=14'} hasBin: true + /prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + /pretty-error@4.0.0: resolution: {integrity: sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==} dependencies: @@ -20830,14 +20775,6 @@ packages: resolution: {integrity: sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==} dev: true - /regexp.prototype.flags@1.4.3: - resolution: {integrity: sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==} - engines: {node: '>= 0.4'} - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - functions-have-names: 1.2.3 - /regexp.prototype.flags@1.5.2: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} @@ -21036,7 +20973,6 @@ packages: /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - dev: false /resolve-url-loader@5.0.0: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} @@ -21069,8 +21005,8 @@ packages: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.4: - resolution: {integrity: sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==} + /resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true dependencies: is-core-module: 2.13.1 @@ -21208,19 +21144,21 @@ packages: has-symbols: 1.0.3 isarray: 2.0.5 + /safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + dependencies: + call-bind: 1.0.7 + get-intrinsic: 1.2.4 + has-symbols: 1.0.3 + isarray: 2.0.5 + /safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} /safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - /safe-regex-test@1.0.0: - resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} - dependencies: - call-bind: 1.0.7 - get-intrinsic: 1.2.4 - is-regex: 1.1.4 - /safe-regex-test@1.0.3: resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} engines: {node: '>= 0.4'} @@ -21509,7 +21447,7 @@ packages: dependencies: color: 4.2.3 detect-libc: 2.0.3 - semver: 7.6.2 + semver: 7.6.3 optionalDependencies: '@img/sharp-darwin-arm64': 0.33.4 '@img/sharp-darwin-x64': 0.33.4 @@ -21889,7 +21827,7 @@ packages: jscodeshift: 0.15.2(@babel/preset-env@7.25.3) leven: 3.1.0 ora: 5.4.1 - prettier: 3.2.5 + prettier: 3.3.3 prompts: 2.4.2 semver: 7.6.2 strip-json-comments: 3.1.1 @@ -22008,17 +21946,36 @@ packages: strip-ansi: 7.1.0 dev: false - /string.prototype.matchall@4.0.8: - resolution: {integrity: sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg==} + /string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 + + /string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-abstract: 1.23.3 + es-errors: 1.3.0 + es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 + gopd: 1.0.1 has-symbols: 1.0.3 - internal-slot: 1.0.3 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 + internal-slot: 1.0.7 + regexp.prototype.flags: 1.5.2 + set-function-name: 2.0.2 + side-channel: 1.0.6 + + /string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + dependencies: + define-properties: 1.2.1 + es-abstract: 1.22.4 /string.prototype.trim@1.2.8: resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} @@ -22028,12 +21985,14 @@ packages: define-properties: 1.2.1 es-abstract: 1.22.4 - /string.prototype.trimend@1.0.6: - resolution: {integrity: sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==} + /string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-abstract: 1.23.3 + es-object-atoms: 1.0.0 /string.prototype.trimend@1.0.7: resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} @@ -22042,12 +22001,12 @@ packages: define-properties: 1.2.1 es-abstract: 1.22.4 - /string.prototype.trimstart@1.0.6: - resolution: {integrity: sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==} + /string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - es-abstract: 1.22.4 + es-object-atoms: 1.0.0 /string.prototype.trimstart@1.0.7: resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} @@ -22056,6 +22015,14 @@ packages: define-properties: 1.2.1 es-abstract: 1.22.4 + /string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-object-atoms: 1.0.0 + /string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} dependencies: @@ -22272,8 +22239,8 @@ packages: '@jridgewell/trace-mapping': 0.3.25 '@types/estree': 1.0.5 acorn: 8.12.1 - aria-query: 5.3.0 - axobject-query: 4.0.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 code-red: 1.0.4 css-tree: 2.3.1 estree-walker: 3.0.3 @@ -22333,6 +22300,14 @@ packages: '@pkgr/utils': 2.3.1 tslib: 2.7.0 + /synckit@0.9.2: + resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==} + engines: {node: ^14.18.0 || >=16.0.0} + dependencies: + '@pkgr/core': 0.1.1 + tslib: 2.7.0 + dev: true + /tailwind-merge@2.2.1: resolution: {integrity: sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==} dependencies: @@ -22461,6 +22436,31 @@ packages: serialize-javascript: 6.0.2 terser: 5.31.3 webpack: 5.93.0(esbuild@0.21.5) + dev: true + + /terser-webpack-plugin@5.3.10(webpack@5.93.0): + resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + jest-worker: 27.5.1 + schema-utils: 3.3.0 + serialize-javascript: 6.0.2 + terser: 5.31.3 + webpack: 5.93.0 + dev: false /terser@5.31.3: resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} @@ -22819,6 +22819,7 @@ packages: dependencies: tslib: 1.14.1 typescript: 5.3.3 + dev: true /tsx@4.16.0: resolution: {integrity: sha512-MPgN+CuY+4iKxGoJNPv+1pyo5YWZAQ5XfsyobUG+zoKG7IkvCPLZDEyoIb8yLS2FcWci1nlxAqmvPlFWD5AFiQ==} @@ -22990,6 +22991,16 @@ packages: has-proto: 1.0.1 is-typed-array: 1.1.13 + /typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + /typed-array-byte-offset@1.0.1: resolution: {integrity: sha512-tcqKMrTRXjqvHN9S3553NPCaGL0VPgFI92lXszmrE8DMhiDPLBYLlvo8Uu4WZAAX/aGqp/T1sbA4ph8EWjDF9Q==} engines: {node: '>= 0.4'} @@ -23001,6 +23012,17 @@ packages: has-proto: 1.0.1 is-typed-array: 1.1.13 + /typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + /typed-array-length@1.0.4: resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} dependencies: @@ -23008,6 +23030,17 @@ packages: for-each: 0.3.3 is-typed-array: 1.1.13 + /typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-proto: 1.0.3 + is-typed-array: 1.1.13 + possible-typed-array-names: 1.0.0 + /typedarray-to-buffer@3.1.5: resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} dependencies: @@ -23617,6 +23650,46 @@ packages: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} dev: true + /webpack@5.93.0: + resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.5 + '@webassemblyjs/ast': 1.12.1 + '@webassemblyjs/wasm-edit': 1.12.1 + '@webassemblyjs/wasm-parser': 1.12.1 + acorn: 8.12.1 + acorn-import-attributes: 1.9.5(acorn@8.12.1) + browserslist: 4.23.3 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.17.1 + es-module-lexer: 1.5.4 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.3.0 + mime-types: 2.1.35 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.1 + terser-webpack-plugin: 5.3.10(webpack@5.93.0) + watchpack: 2.4.1 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - '@swc/core' + - esbuild + - uglify-js + dev: false + /webpack@5.93.0(esbuild@0.21.5): resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} engines: {node: '>=10.13.0'} @@ -23655,6 +23728,7 @@ packages: - '@swc/core' - esbuild - uglify-js + dev: true /webvtt-parser@2.2.0: resolution: {integrity: sha512-FzmaED+jZyt8SCJPTKbSsimrrnQU8ELlViE1wuF3x1pgiQUM8Llj5XWj2j/s6Tlk71ucPfGSMFqZWBtKn/0uEA==} @@ -23742,6 +23816,16 @@ packages: gopd: 1.0.1 has-tostringtag: 1.0.2 + /which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.7 + for-each: 0.3.3 + gopd: 1.0.1 + has-tostringtag: 1.0.2 + /which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true From f9c93ecb4933d4e6b56aed49cea63841bd22434f Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 11:48:17 +0000 Subject: [PATCH 040/127] chore: ensure we follow the eslint rule to use "import type" notation (#285) Co-authored-by: Adam Howard <91115+codeincontext@users.noreply.github.com> --- packages/aila/src/constants.ts | 2 +- .../aila/src/core/Aila.liveWithOpenAI.test.ts | 3 ++- packages/aila/src/core/Aila.testHelpers.ts | 4 ++-- packages/aila/src/core/Aila.ts | 18 +++++++------- packages/aila/src/core/AilaFeatureFactory.ts | 9 +++---- packages/aila/src/core/AilaServices.ts | 18 +++++++------- packages/aila/src/core/chat/AilaChat.ts | 19 ++++++++------- .../aila/src/core/chat/AilaStreamHandler.ts | 6 ++--- packages/aila/src/core/chat/PatchEnqueuer.ts | 2 +- packages/aila/src/core/lesson/AilaLesson.ts | 11 +++++---- packages/aila/src/core/llm/LLMService.ts | 4 ++-- packages/aila/src/core/llm/MockLLMService.ts | 2 +- packages/aila/src/core/llm/OpenAIService.ts | 10 ++++---- packages/aila/src/core/plugins/types.ts | 6 ++--- .../aila/src/core/prompt/AilaPromptBuilder.ts | 4 ++-- .../builders/AilaLessonPromptBuilder.ts | 15 +++++++----- packages/aila/src/core/types.ts | 22 ++++++++--------- .../src/features/analytics/AilaAnalytics.ts | 4 ++-- .../analytics/adapters/AnalyticsAdapter.ts | 2 +- .../adapters/PosthogAnalyticsAdapter.ts | 2 +- .../categorisers/AilaCategorisation.ts | 6 ++--- .../categorisers/MockCategoriser.ts | 4 ++-- .../reporters/AilaErrorReporter.ts | 4 ++-- .../reporters/SentryErrorReporter.ts | 2 +- .../src/features/generation/AilaGeneration.ts | 9 +++---- .../src/features/generation/index.test.ts | 6 +++-- .../src/features/moderation/AilaModeration.ts | 21 ++++++++-------- .../moderation/getSessionModerations.ts | 3 ++- .../src/features/moderation/index.test.ts | 14 +++++------ .../moderation/moderators/MockModerator.ts | 2 +- .../moderation/moderators/OpenAiModerator.ts | 9 +++---- .../features/moderation/moderators/index.ts | 2 +- .../features/persistence/AilaPersistence.ts | 11 +++++---- .../persistence/adaptors/file/index.ts | 4 ++-- .../features/persistence/adaptors/kv/index.ts | 2 +- .../persistence/adaptors/prisma/index.ts | 19 ++++++++------- packages/aila/src/features/rag/AilaRag.ts | 6 ++--- .../snapshotStore/AilaSnapshotStore.ts | 9 +++---- .../threatDetection/AilaThreatDetection.ts | 4 ++-- packages/aila/src/features/types.ts | 14 +++++------ .../lib/openai/OpenAICompletionWithLogging.ts | 2 +- .../aila/src/protocol/jsonPatchProtocol.ts | 6 +++-- .../aila/src/protocol/parseMessageRow.test.ts | 2 +- .../aila/src/protocol/sectionToMarkdown.ts | 3 ++- .../src/utils/language/findAmericanisms.ts | 2 +- .../lessonPlan/compressedLessonPlanForRag.ts | 2 +- .../src/utils/lessonPlan/fetchLessonPlan.ts | 4 ++-- .../lessonPlan/fetchLessonPlanContentById.ts | 7 +++--- .../minifyLessonPlanForRelevantLessons.ts | 4 ++-- .../moderation/moderationErrorHandling.ts | 6 ++--- .../aila/src/utils/rag/fetchRagContent.ts | 4 ++-- packages/api/src/context.ts | 8 +++---- .../export/exportAdditionalMaterialsDoc.ts | 9 +++---- packages/api/src/export/exportLessonPlan.ts | 9 +++---- packages/api/src/export/exportLessonSlides.ts | 9 +++---- packages/api/src/export/exportQuizDoc.ts | 9 +++---- packages/api/src/export/exportWorksheets.ts | 9 +++---- packages/api/src/middleware/adminAuth.ts | 2 +- packages/api/src/middleware/apiKeyAuth.ts | 2 +- packages/api/src/middleware/auth.ts | 2 +- packages/api/src/middleware/rateLimiter.ts | 5 ++-- packages/api/src/router/admin.ts | 5 ++-- packages/api/src/router/appSessions.ts | 7 +++--- packages/api/src/router/exports.ts | 6 ++--- packages/api/src/router/generations.ts | 3 ++- packages/api/src/router/judgements.ts | 2 +- packages/api/src/router/lesson.ts | 3 ++- .../api/src/router/subjectsAndKeyStage.ts | 2 +- packages/core/src/functions/event-types.ts | 2 +- packages/core/src/llm/openai.ts | 16 ++++++++----- packages/core/src/models/apps.ts | 5 ++-- packages/core/src/models/demoUsers.ts | 2 +- packages/core/src/models/feedback.ts | 2 +- packages/core/src/models/generations.ts | 12 ++++++---- packages/core/src/models/lessonPlans.ts | 12 ++++++---- packages/core/src/models/lessonSnapshots.ts | 11 +++++---- packages/core/src/models/lessonSummaries.ts | 2 +- packages/core/src/models/lessons.ts | 11 +++++---- packages/core/src/models/moderations.ts | 7 +++--- packages/core/src/models/promptVariants.ts | 13 +++++----- packages/core/src/models/prompts.ts | 10 ++++---- packages/core/src/models/quizAnswers.ts | 2 +- packages/core/src/models/quizQuestions.ts | 2 +- packages/core/src/models/safetyViolations.ts | 7 +++--- packages/core/src/models/serializers.ts | 5 ++-- packages/core/src/models/snippets.ts | 5 ++-- packages/core/src/models/statistics.ts | 3 ++- packages/core/src/models/transcript.ts | 2 +- .../src/prompts/lesson-assistant/index.ts | 2 +- .../parts/americanToBritish.ts | 2 +- .../prompts/lesson-assistant/parts/basedOn.ts | 2 +- .../prompts/lesson-assistant/parts/body.ts | 2 +- .../prompts/lesson-assistant/parts/context.ts | 2 +- .../parts/currentLessonPlan.ts | 2 +- .../parts/interactingWithTheUser.ts | 4 ++-- .../lesson-assistant/parts/protocol.ts | 2 +- .../src/prompts/lesson-assistant/parts/rag.ts | 2 +- .../prompts/lesson-assistant/parts/schema.ts | 2 +- .../prompts/lesson-assistant/parts/signOff.ts | 2 +- .../prompts/lesson-assistant/parts/task.ts | 2 +- .../src/prompts/lesson-assistant/variants.ts | 5 ++-- .../extend-lesson-plan-quiz/index.ts | 2 +- .../generate-lesson-plan/index.ts | 2 +- .../regenerate-lesson-plan/index.ts | 2 +- .../index.ts | 2 +- .../generate-questions-rag/index.ts | 2 +- .../regenerate-all-distractors/index.ts | 2 +- .../regenerate-answer-rag/index.ts | 2 +- .../regenerate-distractor-rag/index.ts | 2 +- packages/core/src/prompts/types.ts | 2 +- packages/core/src/rag/index.ts | 24 +++++++------------ .../createSnippetsForNewLessons.ts | 2 +- .../import-new-lessons/importNewLessons.ts | 3 ++- packages/core/src/tracing/mockTracer.ts | 2 +- .../core/src/utils/ailaModeration/helpers.ts | 2 +- .../utils/ailaModeration/moderationSchema.ts | 2 +- .../core/src/utils/getCaptionsFromFile.ts | 3 ++- packages/core/src/utils/moderation.ts | 2 +- .../rateLimiting/userBasedRateLimiter.ts | 5 ++-- .../core/src/utils/sendQuizFeedbackEmail.ts | 2 +- packages/core/src/utils/slack.ts | 3 ++- .../workers/generations/requestGeneration.ts | 15 +++++++----- packages/eslint-config-custom/index.js | 2 +- .../src/dataHelpers/prepLessonForSlides.ts | 6 ++--- .../src/dataHelpers/prepLessonPlanForDocs.ts | 4 ++-- .../src/dataHelpers/prepQuizForDocs.ts | 2 +- .../src/dataHelpers/prepWorksheetForSlides.ts | 2 +- packages/exports/src/downloadDriveFile.ts | 2 +- .../exports/src/exportAdditionalMaterials.ts | 4 ++-- packages/exports/src/exportDocLessonPlan.ts | 4 ++-- packages/exports/src/exportDocQuiz.ts | 4 ++-- packages/exports/src/exportDocsWorksheet.ts | 4 ++-- packages/exports/src/exportGeneric.ts | 2 +- .../exports/src/exportQuizDesignerSlides.ts | 4 ++-- .../exports/src/exportSlidesFullLesson.ts | 7 +++--- .../exports/src/gSuite/docs/populateDoc.ts | 7 +++--- .../exports/src/gSuite/drive/addReader.ts | 4 ++-- .../exports/src/gSuite/drive/copyTemplate.ts | 4 ++-- .../src/gSuite/drive/getExportFileStream.ts | 4 ++-- packages/exports/src/gSuite/drive/getLink.ts | 4 ++-- .../exports/src/gSuite/slides/deleteSlides.ts | 4 ++-- .../src/gSuite/slides/populateSlides.ts | 7 +++--- packages/exports/src/schema/input.schema.ts | 2 +- packages/exports/src/templates.ts | 2 +- .../src/captions/getCaptionsByFileName.ts | 3 ++- .../ingest/src/chunking/getLessonPlanParts.ts | 2 +- packages/ingest/src/config/ingestConfig.ts | 2 +- .../src/db-helpers/createCaptionsRecord.ts | 4 ++-- .../src/db-helpers/createErrorRecord.ts | 4 ++-- .../src/db-helpers/createIngestRecord.ts | 4 ++-- .../ingest/src/db-helpers/getIngestById.ts | 2 +- .../src/db-helpers/getLatestIngestId.ts | 2 +- .../src/db-helpers/getLessonsByState.ts | 4 ++-- .../db-helpers/loadLessonsAndUpdateState.ts | 4 ++-- .../src/db-helpers/updateLessonsState.ts | 4 ++-- .../getPartEmbeddingBatchFileLine.ts | 2 +- .../embedding/handleEmbeddingBatchSuccess.ts | 2 +- .../ingest/src/embedding/startEmbedding.ts | 5 ++-- .../getLessonPlanBatchFileLine.ts | 9 +++---- .../generate-lesson-plans/getSystemPrompt.ts | 5 ++-- .../getUserPrompt.test.ts | 2 +- .../generate-lesson-plans/getUserPrompt.ts | 2 +- .../handleLessonPlanBatchSuccess.ts | 2 +- .../generate-lesson-plans/startGenerating.ts | 6 ++--- .../transformQuiz.test.ts | 2 +- .../generate-lesson-plans/transformQuiz.ts | 4 ++-- .../user-prompt-parts/exitQuiz.promptPart.ts | 2 +- .../keyLearningPoints.promptPart.ts | 2 +- .../user-prompt-parts/keywords.promptPart.ts | 2 +- .../learningOutcome.promptPart.ts | 2 +- .../misconceptions.promptPart.ts | 2 +- .../starterQuiz.promptPart.ts | 2 +- .../titleSubjectKeyStage.promptPart.ts | 2 +- .../transcript.promptPart.ts | 2 +- .../user-prompt-parts/year.promptPart.ts | 2 +- .../src/import-lessons/importLessons.ts | 3 ++- .../import-lessons/importLessonsFromCSV.ts | 3 ++- .../import-lessons/importLessonsFromOakDB.ts | 5 ++-- .../src/openai-batches/batchLineCompletion.ts | 2 +- .../handleOpenAiBatchErrorFile.ts | 7 +++--- .../ingest/src/openai-batches/startBatch.ts | 6 +++-- .../src/openai-batches/submitOpenAiBatch.ts | 2 +- packages/ingest/src/steps/0-start.ts | 6 ++--- packages/ingest/src/steps/1-captions.ts | 9 +++---- packages/ingest/src/steps/2-lp-batch-start.ts | 7 +++--- packages/ingest/src/steps/3-lp-batch-sync.ts | 4 ++-- packages/ingest/src/steps/4-lp-chunking.ts | 10 ++++---- .../src/steps/5-lp-parts-embed-start.ts | 7 +++--- .../ingest/src/steps/6-lp-parts-embed-sync.ts | 4 ++-- packages/ingest/src/types.ts | 2 +- packages/logger/index.ts | 3 ++- 191 files changed, 514 insertions(+), 438 deletions(-) diff --git a/packages/aila/src/constants.ts b/packages/aila/src/constants.ts index 2df3b7dbe..fbcc27ab5 100644 --- a/packages/aila/src/constants.ts +++ b/packages/aila/src/constants.ts @@ -1,4 +1,4 @@ -import OpenAI from "openai"; +import type OpenAI from "openai"; export const DEFAULT_MODEL: OpenAI.Chat.ChatModel = "gpt-4o-2024-08-06"; export const DEFAULT_MODERATION_MODEL: OpenAI.Chat.ChatModel = diff --git a/packages/aila/src/core/Aila.liveWithOpenAI.test.ts b/packages/aila/src/core/Aila.liveWithOpenAI.test.ts index ae38ffcfb..5b9eed543 100644 --- a/packages/aila/src/core/Aila.liveWithOpenAI.test.ts +++ b/packages/aila/src/core/Aila.liveWithOpenAI.test.ts @@ -1,4 +1,5 @@ -import { Aila, AilaInitializationOptions } from "."; +import type { AilaInitializationOptions } from "."; +import { Aila } from "."; import { MockCategoriser } from "../features/categorisation/categorisers/MockCategoriser"; import { checkLastMessage, expectPatch, expectText } from "./Aila.testHelpers"; diff --git a/packages/aila/src/core/Aila.testHelpers.ts b/packages/aila/src/core/Aila.testHelpers.ts index f7b7c4387..e2831e130 100644 --- a/packages/aila/src/core/Aila.testHelpers.ts +++ b/packages/aila/src/core/Aila.testHelpers.ts @@ -1,8 +1,8 @@ import { expect } from "@jest/globals"; import invariant from "tiny-invariant"; -import { Aila } from "."; -import { MessagePart, TextDocument } from "../protocol/jsonPatchProtocol"; +import type { Aila } from "."; +import type { MessagePart, TextDocument } from "../protocol/jsonPatchProtocol"; export function checkAssistantResponse(content: string) { // Check that the response is a string (not JSON) diff --git a/packages/aila/src/core/Aila.ts b/packages/aila/src/core/Aila.ts index 9b19b9266..b47aabd39 100644 --- a/packages/aila/src/core/Aila.ts +++ b/packages/aila/src/core/Aila.ts @@ -1,4 +1,5 @@ -import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; +import type { PrismaClientWithAccelerate} from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { @@ -7,8 +8,8 @@ import { DEFAULT_RAG_LESSON_PLANS, } from "../constants"; import { AilaCategorisation } from "../features/categorisation"; -import { AilaSnapshotStore } from "../features/snapshotStore"; -import { +import type { AilaSnapshotStore } from "../features/snapshotStore"; +import type { AilaAnalyticsFeature, AilaErrorReportingFeature, AilaModerationFeature, @@ -18,17 +19,18 @@ import { import { generateMessageId } from "../helpers/chat/generateMessageId"; import { AilaAuthenticationError, AilaGenerationError } from "./AilaError"; import { AilaFeatureFactory } from "./AilaFeatureFactory"; -import { +import type { AilaChatService, AilaLessonService, AilaServices, } from "./AilaServices"; -import { AilaChat, Message } from "./chat"; +import type { Message } from "./chat"; +import { AilaChat } from "./chat"; import { AilaLesson } from "./lesson"; -import { LLMService } from "./llm/LLMService"; +import type { LLMService } from "./llm/LLMService"; import { OpenAIService } from "./llm/OpenAIService"; -import { AilaPlugin } from "./plugins/types"; -import { +import type { AilaPlugin } from "./plugins/types"; +import type { AilaGenerateLessonPlanOptions, AilaOptions, AilaOptionsWithDefaultFallbackValues, diff --git a/packages/aila/src/core/AilaFeatureFactory.ts b/packages/aila/src/core/AilaFeatureFactory.ts index c98f9df46..3730830ab 100644 --- a/packages/aila/src/core/AilaFeatureFactory.ts +++ b/packages/aila/src/core/AilaFeatureFactory.ts @@ -6,22 +6,23 @@ import { import { AilaAnalytics } from "../features/analytics/AilaAnalytics"; import { SentryErrorReporter } from "../features/errorReporting/reporters/SentryErrorReporter"; import { AilaModeration } from "../features/moderation"; +import type { + OpenAILike} from "../features/moderation/moderators/OpenAiModerator"; import { - OpenAILike, OpenAiModerator, } from "../features/moderation/moderators/OpenAiModerator"; import { AilaPrismaPersistence } from "../features/persistence/adaptors/prisma"; import { AilaSnapshotStore } from "../features/snapshotStore"; import { AilaThreatDetection } from "../features/threatDetection"; -import { +import type { AilaAnalyticsFeature, AilaErrorReportingFeature, AilaModerationFeature, AilaPersistenceFeature, AilaThreatDetectionFeature, } from "../features/types"; -import { AilaServices } from "./AilaServices"; -import { AilaOptions } from "./types"; +import type { AilaServices } from "./AilaServices"; +import type { AilaOptions } from "./types"; export class AilaFeatureFactory { static createAnalytics( diff --git a/packages/aila/src/core/AilaServices.ts b/packages/aila/src/core/AilaServices.ts index 38614fe0d..2b0eb1968 100644 --- a/packages/aila/src/core/AilaServices.ts +++ b/packages/aila/src/core/AilaServices.ts @@ -1,21 +1,21 @@ -import { AilaAnalytics } from "../features/analytics/AilaAnalytics"; -import { AilaErrorReporter } from "../features/errorReporting"; -import { AilaSnapshotStore } from "../features/snapshotStore"; -import { +import type { AilaAnalytics } from "../features/analytics/AilaAnalytics"; +import type { AilaErrorReporter } from "../features/errorReporting"; +import type { AilaSnapshotStore } from "../features/snapshotStore"; +import type { AilaAnalyticsFeature, AilaModerationFeature, AilaPersistenceFeature, AilaThreatDetectionFeature, } from "../features/types"; -import { MessagePart } from "../protocol/jsonPatchProtocol"; -import { +import type { MessagePart } from "../protocol/jsonPatchProtocol"; +import type { AilaPersistedChat, AilaRagRelevantLesson, LooseLessonPlan, } from "../protocol/schema"; -import { Message } from "./chat"; -import { AilaOptionsWithDefaultFallbackValues } from "./index"; -import { AilaPlugin } from "./plugins"; +import type { Message } from "./chat"; +import type { AilaOptionsWithDefaultFallbackValues } from "./index"; +import type { AilaPlugin } from "./plugins"; // This provides a set of interfaces between the Aila core and the features that use it. // We can then mock these out in tests without needing to instantiate the entire Aila object. diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 9760b51ca..452b2a49e 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -5,29 +5,32 @@ import { } from "@oakai/core/src/utils/subjects"; import invariant from "tiny-invariant"; -import { AilaChatService, AilaError, AilaServices } from "../.."; +import type { AilaChatService, AilaServices } from "../.."; +import { AilaError } from "../.."; import { DEFAULT_MODEL, DEFAULT_TEMPERATURE } from "../../constants"; +import type { + AilaGenerationStatus} from "../../features/generation"; import { - AilaGeneration, - AilaGenerationStatus, + AilaGeneration } from "../../features/generation"; import { generateMessageId } from "../../helpers/chat/generateMessageId"; +import type { + JsonPatchDocumentOptional} from "../../protocol/jsonPatchProtocol"; import { - JsonPatchDocumentOptional, LLMMessageSchema, parseMessageParts, } from "../../protocol/jsonPatchProtocol"; -import { +import type { AilaPersistedChat, AilaRagRelevantLesson, } from "../../protocol/schema"; -import { LLMService } from "../llm/LLMService"; +import type { LLMService } from "../llm/LLMService"; import { OpenAIService } from "../llm/OpenAIService"; -import { AilaPromptBuilder } from "../prompt/AilaPromptBuilder"; +import type { AilaPromptBuilder } from "../prompt/AilaPromptBuilder"; import { AilaLessonPromptBuilder } from "../prompt/builders/AilaLessonPromptBuilder"; import { AilaStreamHandler } from "./AilaStreamHandler"; import { PatchEnqueuer } from "./PatchEnqueuer"; -import { Message } from "./types"; +import type { Message } from "./types"; export class AilaChat implements AilaChatService { private readonly _id: string; diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 3fc01419b..3245d1098 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -1,11 +1,11 @@ import { aiLogger } from "@oakai/logger"; -import { ReadableStreamDefaultController } from "stream/web"; +import type { ReadableStreamDefaultController } from "stream/web"; import invariant from "tiny-invariant"; import { AilaThreatDetectionError } from "../../features/threatDetection/types"; import { AilaChatError } from "../AilaError"; -import { AilaChat } from "./AilaChat"; -import { PatchEnqueuer } from "./PatchEnqueuer"; +import type { AilaChat } from "./AilaChat"; +import type { PatchEnqueuer } from "./PatchEnqueuer"; const log = aiLogger("aila:stream"); export class AilaStreamHandler { diff --git a/packages/aila/src/core/chat/PatchEnqueuer.ts b/packages/aila/src/core/chat/PatchEnqueuer.ts index 3978630ed..724e0c789 100644 --- a/packages/aila/src/core/chat/PatchEnqueuer.ts +++ b/packages/aila/src/core/chat/PatchEnqueuer.ts @@ -1,6 +1,6 @@ import { aiLogger } from "@oakai/logger"; -import { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; +import type { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; const log = aiLogger("aila:protocol"); diff --git a/packages/aila/src/core/lesson/AilaLesson.ts b/packages/aila/src/core/lesson/AilaLesson.ts index 4981c8b59..9810cdc8f 100644 --- a/packages/aila/src/core/lesson/AilaLesson.ts +++ b/packages/aila/src/core/lesson/AilaLesson.ts @@ -2,15 +2,16 @@ import { aiLogger } from "@oakai/logger"; import { deepClone } from "fast-json-patch"; import { AilaCategorisation } from "../../features/categorisation/categorisers/AilaCategorisation"; -import { AilaCategorisationFeature } from "../../features/types"; +import type { AilaCategorisationFeature } from "../../features/types"; +import type { + PatchDocument} from "../../protocol/jsonPatchProtocol"; import { - PatchDocument, applyLessonPlanPatch, extractPatches, } from "../../protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "../../protocol/schema"; -import { AilaLessonService, AilaServices } from "../AilaServices"; -import { Message } from "../chat"; +import type { LooseLessonPlan } from "../../protocol/schema"; +import type { AilaLessonService, AilaServices } from "../AilaServices"; +import type { Message } from "../chat"; const log = aiLogger("aila:lesson"); diff --git a/packages/aila/src/core/llm/LLMService.ts b/packages/aila/src/core/llm/LLMService.ts index 685850d28..9b549ec07 100644 --- a/packages/aila/src/core/llm/LLMService.ts +++ b/packages/aila/src/core/llm/LLMService.ts @@ -1,6 +1,6 @@ -import { ZodSchema } from "zod"; +import type { ZodSchema } from "zod"; -import { Message } from "../chat"; +import type { Message } from "../chat"; export interface LLMService { name: string; diff --git a/packages/aila/src/core/llm/MockLLMService.ts b/packages/aila/src/core/llm/MockLLMService.ts index 5a99b3ea0..034c94f60 100644 --- a/packages/aila/src/core/llm/MockLLMService.ts +++ b/packages/aila/src/core/llm/MockLLMService.ts @@ -1,4 +1,4 @@ -import { LLMService } from "./LLMService"; +import type { LLMService } from "./LLMService"; async function sleep(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/packages/aila/src/core/llm/OpenAIService.ts b/packages/aila/src/core/llm/OpenAIService.ts index af157bac7..6f3d0047f 100644 --- a/packages/aila/src/core/llm/OpenAIService.ts +++ b/packages/aila/src/core/llm/OpenAIService.ts @@ -1,12 +1,12 @@ -import { OpenAIProvider } from "@ai-sdk/openai"; -import { HeliconeChatMeta } from "@oakai/core/src/llm/helicone"; +import type { OpenAIProvider } from "@ai-sdk/openai"; +import type { HeliconeChatMeta } from "@oakai/core/src/llm/helicone"; import { createVercelOpenAIClient } from "@oakai/core/src/llm/openai"; import { aiLogger } from "@oakai/logger"; import { streamObject, streamText } from "ai"; -import { ZodSchema } from "zod"; +import type { ZodSchema } from "zod"; -import { Message } from "../chat"; -import { LLMService } from "./LLMService"; +import type { Message } from "../chat"; +import type { LLMService } from "./LLMService"; const log = aiLogger("aila:llm"); diff --git a/packages/aila/src/core/plugins/types.ts b/packages/aila/src/core/plugins/types.ts index fd609b52c..b7c277054 100644 --- a/packages/aila/src/core/plugins/types.ts +++ b/packages/aila/src/core/plugins/types.ts @@ -1,7 +1,7 @@ -import { Moderation } from "@prisma/client"; +import type { Moderation } from "@prisma/client"; -import { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; -import { AilaServices } from "../AilaServices"; +import type { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; +import type { AilaServices } from "../AilaServices"; export type AilaPluginContext = { aila: AilaServices; diff --git a/packages/aila/src/core/prompt/AilaPromptBuilder.ts b/packages/aila/src/core/prompt/AilaPromptBuilder.ts index 0ff27baf8..3e93decbc 100644 --- a/packages/aila/src/core/prompt/AilaPromptBuilder.ts +++ b/packages/aila/src/core/prompt/AilaPromptBuilder.ts @@ -1,7 +1,7 @@ import { jsonrepair } from "jsonrepair"; -import { Message } from ".."; -import { AilaServices } from "../.."; +import type { Message } from ".."; +import type { AilaServices } from "../.."; import { tryWithErrorReporting } from "../../helpers/errorReporting"; export abstract class AilaPromptBuilder { diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index e8b67461f..621c34613 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -1,5 +1,6 @@ +import type { + TemplateProps} from "@oakai/core/src/prompts/lesson-assistant"; import { - TemplateProps, template, } from "@oakai/core/src/prompts/lesson-assistant"; import { prisma as globalPrisma } from "@oakai/db"; @@ -8,18 +9,20 @@ import { aiLogger } from "@oakai/logger"; import { DEFAULT_RAG_LESSON_PLANS } from "../../../constants"; import { tryWithErrorReporting } from "../../../helpers/errorReporting"; import { LLMResponseJsonSchema } from "../../../protocol/jsonPatchProtocol"; +import type { + LooseLessonPlan} from "../../../protocol/schema"; import { - LessonPlanJsonSchema, - LooseLessonPlan, + LessonPlanJsonSchema } from "../../../protocol/schema"; import { findAmericanisms } from "../../../utils/language/findAmericanisms"; import { compressedLessonPlanForRag } from "../../../utils/lessonPlan/compressedLessonPlanForRag"; import { fetchLessonPlan } from "../../../utils/lessonPlan/fetchLessonPlan"; +import type { + RagLessonPlan} from "../../../utils/rag/fetchRagContent"; import { - fetchRagContent, - RagLessonPlan, + fetchRagContent } from "../../../utils/rag/fetchRagContent"; -import { AilaServices } from "../../AilaServices"; +import type { AilaServices } from "../../AilaServices"; import { AilaPromptBuilder } from "../AilaPromptBuilder"; const log = aiLogger("aila:prompt"); diff --git a/packages/aila/src/core/types.ts b/packages/aila/src/core/types.ts index 6a8d2623b..118382537 100644 --- a/packages/aila/src/core/types.ts +++ b/packages/aila/src/core/types.ts @@ -1,21 +1,21 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { AilaModerator } from "../features/moderation/moderators"; -import { OpenAILike } from "../features/moderation/moderators/OpenAiModerator"; -import { AilaPersistence } from "../features/persistence"; -import { AilaThreatDetector } from "../features/threatDetection"; -import { +import type { AilaModerator } from "../features/moderation/moderators"; +import type { OpenAILike } from "../features/moderation/moderators/OpenAiModerator"; +import type { AilaPersistence } from "../features/persistence"; +import type { AilaThreatDetector } from "../features/threatDetection"; +import type { AilaAnalyticsFeature, AilaCategorisationFeature, AilaErrorReportingFeature, AilaModerationFeature, AilaThreatDetectionFeature, } from "../features/types"; -import { LooseLessonPlan } from "../protocol/schema"; -import { Message } from "./chat"; -import { LLMService } from "./llm/LLMService"; -import { AilaPlugin } from "./plugins/types"; -import { AilaPromptBuilder } from "./prompt/AilaPromptBuilder"; +import type { LooseLessonPlan } from "../protocol/schema"; +import type { Message } from "./chat"; +import type { LLMService } from "./llm/LLMService"; +import type { AilaPlugin } from "./plugins/types"; +import type { AilaPromptBuilder } from "./prompt/AilaPromptBuilder"; export type AilaGenerateLessonPlanMode = "interactive" | "generate"; diff --git a/packages/aila/src/features/analytics/AilaAnalytics.ts b/packages/aila/src/features/analytics/AilaAnalytics.ts index 730777f45..778813432 100644 --- a/packages/aila/src/features/analytics/AilaAnalytics.ts +++ b/packages/aila/src/features/analytics/AilaAnalytics.ts @@ -1,5 +1,5 @@ -import { AilaServices } from "../../core"; -import { AnalyticsAdapter } from "./adapters/AnalyticsAdapter"; +import type { AilaServices } from "../../core"; +import type { AnalyticsAdapter } from "./adapters/AnalyticsAdapter"; export class AilaAnalytics { private _aila: AilaServices; diff --git a/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts b/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts index 1a2738a9e..6b8b677bd 100644 --- a/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts +++ b/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts @@ -1,4 +1,4 @@ -import { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core"; export abstract class AnalyticsAdapter { protected _aila: AilaServices; diff --git a/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts b/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts index 9949af3ef..57a632b20 100644 --- a/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts +++ b/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts @@ -2,7 +2,7 @@ import { getEncoding } from "js-tiktoken"; import { PostHog } from "posthog-node"; import invariant from "tiny-invariant"; -import { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core"; import { reportCompletionAnalyticsEvent } from "../../../lib/openai/OpenAICompletionWithLogging"; import { AnalyticsAdapter } from "./AnalyticsAdapter"; diff --git a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts index 4b098c2ce..869bcc3ac 100644 --- a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts +++ b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts @@ -4,9 +4,9 @@ import { prisma as globalPrisma, } from "@oakai/db"; -import { AilaServices, Message } from "../../../core"; -import { LooseLessonPlan } from "../../../protocol/schema"; -import { AilaCategorisationFeature } from "../../types"; +import type { AilaServices, Message } from "../../../core"; +import type { LooseLessonPlan } from "../../../protocol/schema"; +import type { AilaCategorisationFeature } from "../../types"; export class AilaCategorisation implements AilaCategorisationFeature { private _aila: AilaServices; diff --git a/packages/aila/src/features/categorisation/categorisers/MockCategoriser.ts b/packages/aila/src/features/categorisation/categorisers/MockCategoriser.ts index ab51ba369..f5a429dde 100644 --- a/packages/aila/src/features/categorisation/categorisers/MockCategoriser.ts +++ b/packages/aila/src/features/categorisation/categorisers/MockCategoriser.ts @@ -1,5 +1,5 @@ -import { LooseLessonPlan } from "../../../protocol/schema"; -import { AilaCategorisationFeature } from "../../types"; +import type { LooseLessonPlan } from "../../../protocol/schema"; +import type { AilaCategorisationFeature } from "../../types"; export class MockCategoriser implements AilaCategorisationFeature { private _mockedLessonPlan: LooseLessonPlan | undefined; diff --git a/packages/aila/src/features/errorReporting/reporters/AilaErrorReporter.ts b/packages/aila/src/features/errorReporting/reporters/AilaErrorReporter.ts index 584b98349..06a281d9c 100644 --- a/packages/aila/src/features/errorReporting/reporters/AilaErrorReporter.ts +++ b/packages/aila/src/features/errorReporting/reporters/AilaErrorReporter.ts @@ -1,5 +1,5 @@ -import { AilaErrorReportingFeature } from "../../types"; -import { AilaErrorBreadcrumb, AilaErrorSeverity } from "../types"; +import type { AilaErrorReportingFeature } from "../../types"; +import type { AilaErrorBreadcrumb, AilaErrorSeverity } from "../types"; export abstract class AilaErrorReporter implements AilaErrorReportingFeature { abstract captureException( diff --git a/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts b/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts index 048e35c25..704d3113e 100644 --- a/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts +++ b/packages/aila/src/features/errorReporting/reporters/SentryErrorReporter.ts @@ -1,7 +1,7 @@ import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; -import { AilaErrorSeverity, AilaErrorBreadcrumb } from "../types"; +import type { AilaErrorSeverity, AilaErrorBreadcrumb } from "../types"; import { AilaErrorReporter } from "./AilaErrorReporter"; const log = aiLogger("aila:errors"); diff --git a/packages/aila/src/features/generation/AilaGeneration.ts b/packages/aila/src/features/generation/AilaGeneration.ts index d96b154a0..9a41b76d7 100644 --- a/packages/aila/src/features/generation/AilaGeneration.ts +++ b/packages/aila/src/features/generation/AilaGeneration.ts @@ -3,14 +3,15 @@ import { ailaGenerate, generateAilaPromptVersionVariantSlug, } from "@oakai/core/src/prompts/lesson-assistant/variants"; -import { prisma, Prompt } from "@oakai/db"; +import type { Prompt } from "@oakai/db"; +import { prisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { kv } from "@vercel/kv"; import { getEncoding } from "js-tiktoken"; -import { AilaServices } from "../../core"; -import { AilaChat } from "../../core/chat"; -import { AilaGenerationStatus } from "./types"; +import type { AilaServices } from "../../core"; +import type { AilaChat } from "../../core/chat"; +import type { AilaGenerationStatus } from "./types"; const log = aiLogger("generation"); diff --git a/packages/aila/src/features/generation/index.test.ts b/packages/aila/src/features/generation/index.test.ts index e1270b103..ec36ca248 100644 --- a/packages/aila/src/features/generation/index.test.ts +++ b/packages/aila/src/features/generation/index.test.ts @@ -1,6 +1,8 @@ import { AilaGeneration } from "."; -import { Aila, AilaInitializationOptions } from "../.."; -import { AilaChat, Message } from "../../core/chat"; +import type { AilaInitializationOptions } from "../.."; +import { Aila } from "../.."; +import type { Message } from "../../core/chat"; +import { AilaChat } from "../../core/chat"; const ailaArgs: AilaInitializationOptions = { plugins: [], diff --git a/packages/aila/src/features/moderation/AilaModeration.ts b/packages/aila/src/features/moderation/AilaModeration.ts index 6aa716190..db4f6e684 100644 --- a/packages/aila/src/features/moderation/AilaModeration.ts +++ b/packages/aila/src/features/moderation/AilaModeration.ts @@ -5,23 +5,24 @@ import { getSafetyResult, isToxic, } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; -import { +import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { Moderation, - PrismaClientWithAccelerate, + PrismaClientWithAccelerate} from "@oakai/db"; +import { prisma as globalPrisma, } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import invariant from "tiny-invariant"; -import { AilaServices } from "../../core"; -import { Message } from "../../core/chat"; -import { AilaPluginContext } from "../../core/plugins/types"; +import type { AilaServices } from "../../core"; +import type { Message } from "../../core/chat"; +import type { AilaPluginContext } from "../../core/plugins/types"; import { getLastAssistantMessage } from "../../helpers/chat/getLastAssistantMessage"; -import { ModerationDocument } from "../../protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "../../protocol/schema"; -import { AilaModerationFeature } from "../types"; -import { AilaModerator } from "./moderators"; +import type { ModerationDocument } from "../../protocol/jsonPatchProtocol"; +import type { LooseLessonPlan } from "../../protocol/schema"; +import type { AilaModerationFeature } from "../types"; +import type { AilaModerator } from "./moderators"; import { OpenAiModerator } from "./moderators/OpenAiModerator"; const log = aiLogger("aila:moderation"); diff --git a/packages/aila/src/features/moderation/getSessionModerations.ts b/packages/aila/src/features/moderation/getSessionModerations.ts index 3a30b7cc7..522e6f8dd 100644 --- a/packages/aila/src/features/moderation/getSessionModerations.ts +++ b/packages/aila/src/features/moderation/getSessionModerations.ts @@ -1,5 +1,6 @@ import { Moderations } from "@oakai/core"; -import { Moderation, prisma } from "@oakai/db"; +import type { Moderation} from "@oakai/db"; +import { prisma } from "@oakai/db"; export async function getSessionModerations( appSessionId: string, diff --git a/packages/aila/src/features/moderation/index.test.ts b/packages/aila/src/features/moderation/index.test.ts index 91af17f01..58601196c 100644 --- a/packages/aila/src/features/moderation/index.test.ts +++ b/packages/aila/src/features/moderation/index.test.ts @@ -1,14 +1,14 @@ // Generated by CodiumAI -import { Moderations } from "@oakai/core"; -import { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { Moderations } from "@oakai/core"; +import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { AilaModeration } from "."; import { Aila } from "../.."; -import { AilaChatInitializationOptions, Message } from "../../core"; -import { AilaPlugin } from "../../core/plugins"; -import { LooseLessonPlan } from "../../protocol/schema"; -import { AilaModerator } from "./moderators"; +import type { AilaChatInitializationOptions, Message } from "../../core"; +import type { AilaPlugin } from "../../core/plugins"; +import type { LooseLessonPlan } from "../../protocol/schema"; +import type { AilaModerator } from "./moderators"; import { MockModerator } from "./moderators/MockModerator"; // Ensure that no tests start relying on prisma diff --git a/packages/aila/src/features/moderation/moderators/MockModerator.ts b/packages/aila/src/features/moderation/moderators/MockModerator.ts index a11bbb0dc..20c802cfc 100644 --- a/packages/aila/src/features/moderation/moderators/MockModerator.ts +++ b/packages/aila/src/features/moderation/moderators/MockModerator.ts @@ -1,4 +1,4 @@ -import { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; import { AilaModerator, AilaModerationError } from "."; diff --git a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts index 897dcaebf..21cd89273 100644 --- a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts +++ b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts @@ -1,12 +1,13 @@ import { createOpenAIClient } from "@oakai/core/src/llm/openai"; import { moderationPrompt } from "@oakai/core/src/utils/ailaModeration/moderationPrompt"; +import type { + ModerationResult} from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { - ModerationResult, moderationResponseSchema, } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; -import OpenAI from "openai"; -import { +import type OpenAI from "openai"; +import type { ChatCompletion, ChatCompletionCreateParamsNonStreaming, } from "openai/resources"; @@ -17,7 +18,7 @@ import { DEFAULT_MODERATION_MODEL, DEFAULT_MODERATION_TEMPERATURE, } from "../../../constants"; -import { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core"; const log = aiLogger("aila:moderation"); diff --git a/packages/aila/src/features/moderation/moderators/index.ts b/packages/aila/src/features/moderation/moderators/index.ts index b356cdaac..c749307dd 100644 --- a/packages/aila/src/features/moderation/moderators/index.ts +++ b/packages/aila/src/features/moderation/moderators/index.ts @@ -1,4 +1,4 @@ -import { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; export abstract class AilaModerator { private _userId: string | undefined; diff --git a/packages/aila/src/features/persistence/AilaPersistence.ts b/packages/aila/src/features/persistence/AilaPersistence.ts index 98b46c7f5..e031e3611 100644 --- a/packages/aila/src/features/persistence/AilaPersistence.ts +++ b/packages/aila/src/features/persistence/AilaPersistence.ts @@ -1,10 +1,11 @@ -import { GenerationStatus } from "@prisma/client"; +import type { GenerationStatus } from "@prisma/client"; import invariant from "tiny-invariant"; -import { AilaChatService, AilaError, AilaServices } from "../../core"; -import { AilaOptionsWithDefaultFallbackValues } from "../../core/types"; -import { AilaPersistedChat } from "../../protocol/schema"; -import { AilaGeneration } from "../generation"; +import type { AilaChatService, AilaServices } from "../../core"; +import { AilaError } from "../../core"; +import type { AilaOptionsWithDefaultFallbackValues } from "../../core/types"; +import type { AilaPersistedChat } from "../../protocol/schema"; +import type { AilaGeneration } from "../generation"; export abstract class AilaPersistence { protected _chat: AilaChatService; diff --git a/packages/aila/src/features/persistence/adaptors/file/index.ts b/packages/aila/src/features/persistence/adaptors/file/index.ts index 616cf889a..a63cecc1a 100644 --- a/packages/aila/src/features/persistence/adaptors/file/index.ts +++ b/packages/aila/src/features/persistence/adaptors/file/index.ts @@ -1,6 +1,6 @@ import { AilaPersistence } from "../.."; -import { AilaChatService, AilaServices } from "../../../../core"; -import { AilaGeneration } from "../../../generation"; +import type { AilaChatService, AilaServices } from "../../../../core"; +import type { AilaGeneration } from "../../../generation"; export class AilaFilePersistence extends AilaPersistence { constructor({ aila, chat }: { aila: AilaServices; chat: AilaChatService }) { diff --git a/packages/aila/src/features/persistence/adaptors/kv/index.ts b/packages/aila/src/features/persistence/adaptors/kv/index.ts index f7db56da4..a1cdf8d76 100644 --- a/packages/aila/src/features/persistence/adaptors/kv/index.ts +++ b/packages/aila/src/features/persistence/adaptors/kv/index.ts @@ -1,7 +1,7 @@ import { kv } from "@vercel/kv"; import { AilaPersistence } from "../.."; -import { AilaChatService, AilaServices } from "../../../../core"; +import type { AilaChatService, AilaServices } from "../../../../core"; export class AilaKVPersistence extends AilaPersistence { constructor({ chat, aila }: { chat: AilaChatService; aila: AilaServices }) { diff --git a/packages/aila/src/features/persistence/adaptors/prisma/index.ts b/packages/aila/src/features/persistence/adaptors/prisma/index.ts index 698b9cafe..8a1ecb0c0 100644 --- a/packages/aila/src/features/persistence/adaptors/prisma/index.ts +++ b/packages/aila/src/features/persistence/adaptors/prisma/index.ts @@ -1,22 +1,25 @@ -import { +import type { Prisma, - PrismaClientWithAccelerate, + PrismaClientWithAccelerate} from "@oakai/db"; +import { prisma as globalPrisma, } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { AilaPersistence } from "../.."; -import { - AilaAuthenticationError, +import type { AilaChatService, - AilaServices, -} from "../../../../core"; + AilaServices} from "../../../../core"; import { + AilaAuthenticationError +} from "../../../../core"; +import type { AilaPersistedChat, - LessonPlanKeys, + LessonPlanKeys} from "../../../../protocol/schema"; +import { chatSchema, } from "../../../../protocol/schema"; -import { AilaGeneration } from "../../../generation"; +import type { AilaGeneration } from "../../../generation"; const log = aiLogger("aila:persistence"); diff --git a/packages/aila/src/features/rag/AilaRag.ts b/packages/aila/src/features/rag/AilaRag.ts index 213bba4a7..d2b0de9e8 100644 --- a/packages/aila/src/features/rag/AilaRag.ts +++ b/packages/aila/src/features/rag/AilaRag.ts @@ -1,11 +1,11 @@ import { RAG } from "@oakai/core/src/rag"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import { AilaServices } from "../../core"; +import type { AilaServices } from "../../core"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; -import { LooseLessonPlan } from "../../protocol/schema"; +import type { LooseLessonPlan } from "../../protocol/schema"; import { minifyLessonPlanForRelevantLessons } from "../../utils/lessonPlan/minifyLessonPlanForRelevantLessons"; const log = aiLogger("aila:rag"); diff --git a/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts index f76585525..3089de3b4 100644 --- a/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts +++ b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts @@ -1,10 +1,11 @@ import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; -import { LessonSnapshotTrigger } from "@prisma/client"; +import type { PrismaClientWithAccelerate} from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db"; +import type { LessonSnapshotTrigger } from "@prisma/client"; import invariant from "tiny-invariant"; -import { AilaServices } from "../../core"; -import { LooseLessonPlan } from "../../protocol/schema"; +import type { AilaServices } from "../../core"; +import type { LooseLessonPlan } from "../../protocol/schema"; export class AilaSnapshotStore { protected _name: string; diff --git a/packages/aila/src/features/threatDetection/AilaThreatDetection.ts b/packages/aila/src/features/threatDetection/AilaThreatDetection.ts index 13780f538..04d2bc140 100644 --- a/packages/aila/src/features/threatDetection/AilaThreatDetection.ts +++ b/packages/aila/src/features/threatDetection/AilaThreatDetection.ts @@ -1,5 +1,5 @@ -import { AilaThreatDetectionFeature } from "../types"; -import { AilaThreatDetector } from "./detectors/AilaThreatDetector"; +import type { AilaThreatDetectionFeature } from "../types"; +import type { AilaThreatDetector } from "./detectors/AilaThreatDetector"; import { HeliconeThreatDetector } from "./detectors/HeliconeThreatDetector"; export class AilaThreatDetection implements AilaThreatDetectionFeature { diff --git a/packages/aila/src/features/types.ts b/packages/aila/src/features/types.ts index 5ea17970e..c44ca2ef5 100644 --- a/packages/aila/src/features/types.ts +++ b/packages/aila/src/features/types.ts @@ -1,10 +1,10 @@ -import { Message } from "../core/chat"; -import { AilaPluginContext } from "../core/plugins"; -import { ModerationDocument } from "../protocol/jsonPatchProtocol"; -import { AilaPersistedChat, LooseLessonPlan } from "../protocol/schema"; -import { AilaErrorBreadcrumb, AilaErrorSeverity } from "./errorReporting/types"; -import { AilaGeneration } from "./generation"; -import { AilaThreatDetector } from "./threatDetection"; +import type { Message } from "../core/chat"; +import type { AilaPluginContext } from "../core/plugins"; +import type { ModerationDocument } from "../protocol/jsonPatchProtocol"; +import type { AilaPersistedChat, LooseLessonPlan } from "../protocol/schema"; +import type { AilaErrorBreadcrumb, AilaErrorSeverity } from "./errorReporting/types"; +import type { AilaGeneration } from "./generation"; +import type { AilaThreatDetector } from "./threatDetection"; export interface AilaModerationFeature { moderate(options: { diff --git a/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts b/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts index 52dd9d339..9bc652951 100644 --- a/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts +++ b/packages/aila/src/lib/openai/OpenAICompletionWithLogging.ts @@ -3,7 +3,7 @@ import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; import { createOpenAIClient } from "@oakai/core/src/llm/openai"; import { aiLogger } from "@oakai/logger"; -import OpenAI from "openai"; +import type OpenAI from "openai"; import type { PostHog } from "posthog-node"; const log = aiLogger("aila:llm"); diff --git a/packages/aila/src/protocol/jsonPatchProtocol.ts b/packages/aila/src/protocol/jsonPatchProtocol.ts index 834092ec0..7bc3f2463 100644 --- a/packages/aila/src/protocol/jsonPatchProtocol.ts +++ b/packages/aila/src/protocol/jsonPatchProtocol.ts @@ -1,8 +1,9 @@ import { moderationCategoriesSchema } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +import type { + Operation} from "fast-json-patch"; import { - Operation, applyPatch, deepClone, JsonPatchError, @@ -11,6 +12,8 @@ import untruncateJson from "untruncate-json"; import { z } from "zod"; import zodToJsonSchema from "zod-to-json-schema"; +import type { + LooseLessonPlan} from "./schema"; import { BasedOnOptionalSchema, BasedOnSchema, @@ -21,7 +24,6 @@ import { KeywordsSchema, KeywordsSchemaWithoutLength, LessonPlanSchemaWhilstStreaming, - LooseLessonPlan, MisconceptionsOptionalSchema, MisconceptionsSchema, MisconceptionsSchemaWithoutLength, diff --git a/packages/aila/src/protocol/parseMessageRow.test.ts b/packages/aila/src/protocol/parseMessageRow.test.ts index c1d60a1c8..7057c7798 100644 --- a/packages/aila/src/protocol/parseMessageRow.test.ts +++ b/packages/aila/src/protocol/parseMessageRow.test.ts @@ -1,7 +1,7 @@ import invariant from "tiny-invariant"; import { PatchCycle, parseMessageRow } from "./jsonPatchProtocol"; -import { Cycle } from "./schema"; +import type { Cycle } from "./schema"; /* diff --git a/packages/aila/src/protocol/sectionToMarkdown.ts b/packages/aila/src/protocol/sectionToMarkdown.ts index 2ede1e2fb..a2d345aac 100644 --- a/packages/aila/src/protocol/sectionToMarkdown.ts +++ b/packages/aila/src/protocol/sectionToMarkdown.ts @@ -1,9 +1,10 @@ import { camelCaseToSentenceCase } from "@oakai/core/src/utils/camelCaseToSentenceCase"; import { isArray, isNumber, isObject, isString } from "remeda"; +import type { + QuizOptional} from "./schema"; import { CycleOptionalSchema, - QuizOptional, QuizOptionalSchema, } from "./schema"; diff --git a/packages/aila/src/utils/language/findAmericanisms.ts b/packages/aila/src/utils/language/findAmericanisms.ts index 40418f849..78694616b 100644 --- a/packages/aila/src/utils/language/findAmericanisms.ts +++ b/packages/aila/src/utils/language/findAmericanisms.ts @@ -3,7 +3,7 @@ import { textify } from "@oakai/core/src/models/lessonPlans"; import translator from "american-british-english-translator"; -import { LessonPlanKeys, LooseLessonPlan } from "../../protocol/schema"; +import type { LessonPlanKeys, LooseLessonPlan } from "../../protocol/schema"; export type AmericanismIssueBySection = { section: string; diff --git a/packages/aila/src/utils/lessonPlan/compressedLessonPlanForRag.ts b/packages/aila/src/utils/lessonPlan/compressedLessonPlanForRag.ts index 63dc9ac67..ba45194a6 100644 --- a/packages/aila/src/utils/lessonPlan/compressedLessonPlanForRag.ts +++ b/packages/aila/src/utils/lessonPlan/compressedLessonPlanForRag.ts @@ -1,4 +1,4 @@ -import { LooseLessonPlan } from "../../protocol/schema"; +import type { LooseLessonPlan } from "../../protocol/schema"; /** * Compresses a lesson plan to a stringified JSON object, excluding the exit and starter quizzes diff --git a/packages/aila/src/utils/lessonPlan/fetchLessonPlan.ts b/packages/aila/src/utils/lessonPlan/fetchLessonPlan.ts index d3e90e309..1415de620 100644 --- a/packages/aila/src/utils/lessonPlan/fetchLessonPlan.ts +++ b/packages/aila/src/utils/lessonPlan/fetchLessonPlan.ts @@ -1,7 +1,7 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { kv } from "@vercel/kv"; -import { LooseLessonPlan } from "../../protocol/schema"; +import type { LooseLessonPlan } from "../../protocol/schema"; import { fetchLessonPlanContentById } from "./fetchLessonPlanContentById"; export async function fetchLessonPlan({ diff --git a/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts b/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts index f706a0025..11f44d50c 100644 --- a/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts +++ b/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts @@ -1,9 +1,10 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; +import type { + LooseLessonPlan} from "../../protocol/schema"; import { - LessonPlanSchemaWhilstStreaming, - LooseLessonPlan, + LessonPlanSchemaWhilstStreaming } from "../../protocol/schema"; export async function fetchLessonPlanContentById( diff --git a/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts b/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts index deed91501..3bb1ecb8a 100644 --- a/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts +++ b/packages/aila/src/utils/lessonPlan/minifyLessonPlanForRelevantLessons.ts @@ -1,6 +1,6 @@ -import { LessonPlan as DatabaseLessonPlan } from "@oakai/db"; +import type { LessonPlan as DatabaseLessonPlan } from "@oakai/db"; -import { CompletedLessonPlan } from "../../protocol/schema"; +import type { CompletedLessonPlan } from "../../protocol/schema"; export function minifyLessonPlanForRelevantLessons( lessonPlan: DatabaseLessonPlan, diff --git a/packages/aila/src/utils/moderation/moderationErrorHandling.ts b/packages/aila/src/utils/moderation/moderationErrorHandling.ts index 82266b3b9..2a8428c89 100644 --- a/packages/aila/src/utils/moderation/moderationErrorHandling.ts +++ b/packages/aila/src/utils/moderation/moderationErrorHandling.ts @@ -1,9 +1,9 @@ import { SafetyViolations as defaultSafetyViolations } from "@oakai/core"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { AilaThreatDetectionError } from "../../features/threatDetection/types"; -import { +import type { AilaThreatDetectionError } from "../../features/threatDetection/types"; +import type { ActionDocument, ErrorDocument, } from "../../protocol/jsonPatchProtocol"; diff --git a/packages/aila/src/utils/rag/fetchRagContent.ts b/packages/aila/src/utils/rag/fetchRagContent.ts index f13be709b..3ffb709dc 100644 --- a/packages/aila/src/utils/rag/fetchRagContent.ts +++ b/packages/aila/src/utils/rag/fetchRagContent.ts @@ -1,8 +1,8 @@ import { RAG } from "@oakai/core/src/rag"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; -import { CompletedLessonPlan } from "../../protocol/schema"; +import type { CompletedLessonPlan } from "../../protocol/schema"; import { minifyLessonPlanForRelevantLessons } from "../lessonPlan/minifyLessonPlanForRelevantLessons"; export type RagLessonPlan = Omit< diff --git a/packages/api/src/context.ts b/packages/api/src/context.ts index fd5761114..31fcd29c7 100644 --- a/packages/api/src/context.ts +++ b/packages/api/src/context.ts @@ -1,14 +1,14 @@ -import { +import type { SignedInAuthObject, SignedOutAuthObject, } from "@clerk/backend/internal"; import { getAuth } from "@clerk/nextjs/server"; import { prisma } from "@oakai/db"; import { type inferAsyncReturnType } from "@trpc/server"; -import { NodeHTTPCreateContextFnOptions } from "@trpc/server/adapters/node-http"; -import { NextRequest, NextResponse } from "next/server"; +import type { NodeHTTPCreateContextFnOptions } from "@trpc/server/adapters/node-http"; +import type { NextRequest, NextResponse } from "next/server"; -import { RateLimitInfo } from "./types"; +import type { RateLimitInfo } from "./types"; type ClerkAuthSig = typeof getAuth; type ClerkAuthReturn = inferAsyncReturnType; diff --git a/packages/api/src/export/exportAdditionalMaterialsDoc.ts b/packages/api/src/export/exportAdditionalMaterialsDoc.ts index 2617bf7d6..826339ea4 100644 --- a/packages/api/src/export/exportAdditionalMaterialsDoc.ts +++ b/packages/api/src/export/exportAdditionalMaterialsDoc.ts @@ -1,16 +1,17 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportAdditionalMaterials } from "@oakai/exports"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +import type { + OutputSchema} from "../router/exports"; import { ailaGetExportBySnapshotId, ailaSaveExport, - OutputSchema, reportErrorResult, } from "../router/exports"; diff --git a/packages/api/src/export/exportLessonPlan.ts b/packages/api/src/export/exportLessonPlan.ts index 3847e38ad..705b197ae 100644 --- a/packages/api/src/export/exportLessonPlan.ts +++ b/packages/api/src/export/exportLessonPlan.ts @@ -1,16 +1,17 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocLessonPlan } from "@oakai/exports"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +import type { + OutputSchema} from "../router/exports"; import { ailaGetExportBySnapshotId, ailaSaveExport, - OutputSchema, reportErrorResult, } from "../router/exports"; diff --git a/packages/api/src/export/exportLessonSlides.ts b/packages/api/src/export/exportLessonSlides.ts index a129f1211..c941915e3 100644 --- a/packages/api/src/export/exportLessonSlides.ts +++ b/packages/api/src/export/exportLessonSlides.ts @@ -1,16 +1,17 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportSlidesFullLesson } from "@oakai/exports"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +import type { + OutputSchema} from "../router/exports"; import { ailaGetExportBySnapshotId, ailaSaveExport, - OutputSchema, reportErrorResult, } from "../router/exports"; diff --git a/packages/api/src/export/exportQuizDoc.ts b/packages/api/src/export/exportQuizDoc.ts index 0e6415e4f..640273a14 100644 --- a/packages/api/src/export/exportQuizDoc.ts +++ b/packages/api/src/export/exportQuizDoc.ts @@ -1,17 +1,18 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocQuiz } from "@oakai/exports"; -import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; +import type { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { z } from "zod"; +import type { + OutputSchema} from "../router/exports"; import { ailaGetExportBySnapshotId, ailaSaveExport, - OutputSchema, reportErrorResult, } from "../router/exports"; diff --git a/packages/api/src/export/exportWorksheets.ts b/packages/api/src/export/exportWorksheets.ts index 5a69da542..45e9785fa 100644 --- a/packages/api/src/export/exportWorksheets.ts +++ b/packages/api/src/export/exportWorksheets.ts @@ -1,16 +1,17 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocsWorksheet } from "@oakai/exports"; -import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; +import type { + OutputSchema} from "../router/exports"; import { ailaGetExportBySnapshotId, ailaSaveExport, - OutputSchema, reportErrorResult, } from "../router/exports"; diff --git a/packages/api/src/middleware/adminAuth.ts b/packages/api/src/middleware/adminAuth.ts index 0c9cba918..0c85c8b7c 100644 --- a/packages/api/src/middleware/adminAuth.ts +++ b/packages/api/src/middleware/adminAuth.ts @@ -1,4 +1,4 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; diff --git a/packages/api/src/middleware/apiKeyAuth.ts b/packages/api/src/middleware/apiKeyAuth.ts index 366ac49ee..3d00682ff 100644 --- a/packages/api/src/middleware/apiKeyAuth.ts +++ b/packages/api/src/middleware/apiKeyAuth.ts @@ -1,6 +1,6 @@ import { TRPCError } from "@trpc/server"; -import { APIKeyAuthObject } from "../context"; +import type { APIKeyAuthObject } from "../context"; import { t } from "../trpc"; const OAI_INTERNAL_API_KEY = process.env.OAI_INTERNAL_API_KEY; diff --git a/packages/api/src/middleware/auth.ts b/packages/api/src/middleware/auth.ts index 00129ea5e..c29814503 100644 --- a/packages/api/src/middleware/auth.ts +++ b/packages/api/src/middleware/auth.ts @@ -1,4 +1,4 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { aiLogger } from "@oakai/logger"; import { TRPCError } from "@trpc/server"; diff --git a/packages/api/src/middleware/rateLimiter.ts b/packages/api/src/middleware/rateLimiter.ts index f68120bb5..b12ae86a6 100644 --- a/packages/api/src/middleware/rateLimiter.ts +++ b/packages/api/src/middleware/rateLimiter.ts @@ -1,8 +1,9 @@ import { inngest } from "@oakai/core"; import { rateLimits } from "@oakai/core/src/utils/rateLimiting/rateLimit"; +import type { + RateLimiter} from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { - RateLimitExceededError, - RateLimiter, + RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { TRPCError } from "@trpc/server"; diff --git a/packages/api/src/router/admin.ts b/packages/api/src/router/admin.ts index f05ed17aa..619ba966b 100644 --- a/packages/api/src/router/admin.ts +++ b/packages/api/src/router/admin.ts @@ -1,11 +1,12 @@ import { Moderations, SafetyViolations } from "@oakai/core"; -import { Moderation } from "@oakai/db"; +import type { Moderation } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { z } from "zod"; import { getSessionModerations } from "../../../aila/src/features/moderation/getSessionModerations"; +import type { + AilaPersistedChat} from "../../../aila/src/protocol/schema"; import { - AilaPersistedChat, chatSchema, } from "../../../aila/src/protocol/schema"; import { adminProcedure } from "../middleware/adminAuth"; diff --git a/packages/api/src/router/appSessions.ts b/packages/api/src/router/appSessions.ts index dd64b451e..653c22a39 100644 --- a/packages/api/src/router/appSessions.ts +++ b/packages/api/src/router/appSessions.ts @@ -1,9 +1,9 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { demoUsers } from "@oakai/core"; import { rateLimits } from "@oakai/core/src/utils/rateLimiting/rateLimit"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; -import { Prisma, PrismaClientWithAccelerate } from "@oakai/db"; +import type { Prisma, PrismaClientWithAccelerate } from "@oakai/db"; import * as Sentry from "@sentry/nextjs"; import { TRPCError } from "@trpc/server"; import { isTruthy } from "remeda"; @@ -11,8 +11,9 @@ import { z } from "zod"; import { getSessionModerations } from "../../../aila/src/features/moderation/getSessionModerations"; import { generateChatId } from "../../../aila/src/helpers/chat/generateChatId"; +import type { + AilaPersistedChat} from "../../../aila/src/protocol/schema"; import { - AilaPersistedChat, chatSchema, } from "../../../aila/src/protocol/schema"; import { protectedProcedure } from "../middleware/auth"; diff --git a/packages/api/src/router/exports.ts b/packages/api/src/router/exports.ts index e81f44cc1..b711a6fa4 100644 --- a/packages/api/src/router/exports.ts +++ b/packages/api/src/router/exports.ts @@ -1,8 +1,8 @@ -import { SignedInAuthObject } from "@clerk/backend/internal"; +import type { SignedInAuthObject } from "@clerk/backend/internal"; import { clerkClient } from "@clerk/nextjs/server"; import { LessonSnapshots } from "@oakai/core"; import { sendEmail } from "@oakai/core/src/utils/sendEmail"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { exportDocLessonPlanSchema, exportDocQuizSchema, @@ -12,7 +12,7 @@ import { } from "@oakai/exports"; import { exportableQuizAppStateSchema } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; -import { LessonExportType } from "@prisma/client"; +import type { LessonExportType } from "@prisma/client"; import * as Sentry from "@sentry/nextjs"; import { kv } from "@vercel/kv"; import { z } from "zod"; diff --git a/packages/api/src/router/generations.ts b/packages/api/src/router/generations.ts index 906ec75b7..70fd77857 100644 --- a/packages/api/src/router/generations.ts +++ b/packages/api/src/router/generations.ts @@ -6,8 +6,9 @@ import { serializeGeneration, serializedGenerationSchema, } from "@oakai/core/src/models/serializers"; +import type { + GenerationPart} from "@oakai/core/src/types"; import { - GenerationPart, generationPartSchema, generationPartUserTweakedSchema, } from "@oakai/core/src/types"; diff --git a/packages/api/src/router/judgements.ts b/packages/api/src/router/judgements.ts index a76aedc1a..0c29cb4a9 100644 --- a/packages/api/src/router/judgements.ts +++ b/packages/api/src/router/judgements.ts @@ -1,4 +1,4 @@ -import { KeyStageName, SubjectName, subjectsAndKeyStages } from "@oakai/core"; +import type { KeyStageName, SubjectName, subjectsAndKeyStages } from "@oakai/core"; import { sendJudgementFeedbackEmail } from "@oakai/core/src/utils/sendJudgementFeedbackEmail"; import { structuredLogger as logger, aiLogger } from "@oakai/logger"; import { z } from "zod"; diff --git a/packages/api/src/router/lesson.ts b/packages/api/src/router/lesson.ts index 26c4154a8..06ae3154e 100644 --- a/packages/api/src/router/lesson.ts +++ b/packages/api/src/router/lesson.ts @@ -1,5 +1,6 @@ import { Lessons, inngest } from "@oakai/core"; -import { LessonSummary, LessonWithSnippets, Transcript } from "@oakai/db"; +import type { LessonSummary, Transcript } from "@oakai/db"; +import { LessonWithSnippets } from "@oakai/db"; import { TRPCError } from "@trpc/server"; import { z } from "zod"; diff --git a/packages/api/src/router/subjectsAndKeyStage.ts b/packages/api/src/router/subjectsAndKeyStage.ts index 8d1ab7f86..e4e5d3327 100644 --- a/packages/api/src/router/subjectsAndKeyStage.ts +++ b/packages/api/src/router/subjectsAndKeyStage.ts @@ -3,7 +3,7 @@ import { type KeyStageName, type SubjectName, } from "@oakai/core"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { z } from "zod"; import { protectedProcedure } from "../middleware/auth"; diff --git a/packages/core/src/functions/event-types.ts b/packages/core/src/functions/event-types.ts index c21295aed..7a83f3cd5 100644 --- a/packages/core/src/functions/event-types.ts +++ b/packages/core/src/functions/event-types.ts @@ -1,4 +1,4 @@ -import { ZodEventSchemas } from "inngest"; +import type { ZodEventSchemas } from "inngest"; import { z } from "zod"; import { populateDemoStatusesSchema } from "./demo/populateDemoStatuses.schema"; diff --git a/packages/core/src/llm/openai.ts b/packages/core/src/llm/openai.ts index 213e88b5a..7aa0210b4 100644 --- a/packages/core/src/llm/openai.ts +++ b/packages/core/src/llm/openai.ts @@ -1,14 +1,18 @@ -import { createOpenAI, OpenAIProvider } from "@ai-sdk/openai"; +import type { OpenAIProvider } from "@ai-sdk/openai"; +import { createOpenAI } from "@ai-sdk/openai"; import { ChatOpenAI as LangchainChatOpenAI } from "langchain/chat_models/openai"; -import { BaseLLMParams } from "langchain/llms/base"; -import { +import type { BaseLLMParams } from "langchain/llms/base"; +import type { AzureOpenAIInput, - OpenAIInput, + OpenAIInput} from "langchain/llms/openai"; +import { OpenAI as OpenAILangchain, } from "langchain/llms/openai"; -import OpenAI, { ClientOptions } from "openai"; +import type { ClientOptions } from "openai"; +import OpenAI from "openai"; -import { HeliconeChatMeta, heliconeHeaders } from "./helicone"; +import type { HeliconeChatMeta} from "./helicone"; +import { heliconeHeaders } from "./helicone"; export type CreateOpenAIClientProps = | { diff --git a/packages/core/src/models/apps.ts b/packages/core/src/models/apps.ts index 6b3f552e0..9e45c85e1 100644 --- a/packages/core/src/models/apps.ts +++ b/packages/core/src/models/apps.ts @@ -1,5 +1,6 @@ -import { Prisma, PrismaClientWithAccelerate, Prompt } from "@oakai/db"; -import { App } from "@prisma/client"; +import type { PrismaClientWithAccelerate, Prompt } from "@oakai/db"; +import { Prisma } from "@oakai/db"; +import type { App } from "@prisma/client"; import { Prompts } from "./prompts"; diff --git a/packages/core/src/models/demoUsers.ts b/packages/core/src/models/demoUsers.ts index e9a6c668e..db7ef64aa 100644 --- a/packages/core/src/models/demoUsers.ts +++ b/packages/core/src/models/demoUsers.ts @@ -1,4 +1,4 @@ -import { User } from "@clerk/nextjs/server"; +import type { User } from "@clerk/nextjs/server"; const DEVELOPMENT_USER_REGION = process.env.DEVELOPMENT_USER_REGION || null; if (process.env.NODE_ENV === "development" && !DEVELOPMENT_USER_REGION) { diff --git a/packages/core/src/models/feedback.ts b/packages/core/src/models/feedback.ts index 9dc368bd6..d797431a4 100644 --- a/packages/core/src/models/feedback.ts +++ b/packages/core/src/models/feedback.ts @@ -1,4 +1,4 @@ -import { +import type { GenerationUserFlag, PrismaClientWithAccelerate, ReGeneration, diff --git a/packages/core/src/models/generations.ts b/packages/core/src/models/generations.ts index 699cbca71..c256a015f 100644 --- a/packages/core/src/models/generations.ts +++ b/packages/core/src/models/generations.ts @@ -1,12 +1,14 @@ -import { +import type { Generation, - GenerationStatus, ModerationType, Prisma, - PrismaClientWithAccelerate, + PrismaClientWithAccelerate} from "@oakai/db"; +import { + GenerationStatus } from "@oakai/db"; -import { structuredLogger, StructuredLogger } from "@oakai/logger"; -import { Logger as InngestLogger } from "inngest/middleware/logger"; +import type { StructuredLogger } from "@oakai/logger"; +import { structuredLogger } from "@oakai/logger"; +import type { Logger as InngestLogger } from "inngest/middleware/logger"; import { omit } from "remeda"; import { Md5 } from "ts-md5"; diff --git a/packages/core/src/models/lessonPlans.ts b/packages/core/src/models/lessonPlans.ts index fb8b5b729..ef907ceeb 100644 --- a/packages/core/src/models/lessonPlans.ts +++ b/packages/core/src/models/lessonPlans.ts @@ -1,13 +1,14 @@ -import { +import type { KeyStage, Lesson, LessonPlan, LessonPlanPart, - LessonPlanPartStatus, - LessonPlanStatus, LessonSummary, PrismaClientWithAccelerate, - Subject, + Subject} from "@oakai/db"; +import { + LessonPlanPartStatus, + LessonPlanStatus } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import yaml from "yaml"; @@ -20,7 +21,8 @@ import { template } from "../prompts/lesson-assistant"; import { RAG } from "../rag"; import { camelCaseToSentenceCase } from "../utils/camelCaseToSentenceCase"; import { embedWithCache } from "../utils/embeddings"; -import { Caption, CaptionsSchema } from "./types/caption"; +import type { Caption} from "./types/caption"; +import { CaptionsSchema } from "./types/caption"; const log = aiLogger("lessons"); diff --git a/packages/core/src/models/lessonSnapshots.ts b/packages/core/src/models/lessonSnapshots.ts index c1314aae2..baf62b4d5 100644 --- a/packages/core/src/models/lessonSnapshots.ts +++ b/packages/core/src/models/lessonSnapshots.ts @@ -1,17 +1,18 @@ -import { +import type { LessonSnapshot, LessonSnapshotTrigger, PrismaClientWithAccelerate, } from "@oakai/db"; import crypto from "crypto"; +import type { + LooseLessonPlan} from "../../../aila/src/protocol/schema"; import { - LessonPlanJsonSchema, - LooseLessonPlan, + LessonPlanJsonSchema } from "../../../aila/src/protocol/schema"; // #TODO this import is reaching out of the package because it would otherwise be a circular dependency -import { DeepNullable } from "../utils/DeepNullable"; -import { DeepPartial } from "../utils/DeepPartial"; +import type { DeepNullable } from "../utils/DeepNullable"; +import type { DeepPartial } from "../utils/DeepPartial"; export type Snapshot = DeepPartial>; const JsonSchemaString = JSON.stringify(LessonPlanJsonSchema); diff --git a/packages/core/src/models/lessonSummaries.ts b/packages/core/src/models/lessonSummaries.ts index 104f7be50..111c935ec 100644 --- a/packages/core/src/models/lessonSummaries.ts +++ b/packages/core/src/models/lessonSummaries.ts @@ -1,4 +1,4 @@ -import { Lesson, LessonSummary, PrismaClientWithAccelerate } from "@oakai/db"; +import type { Lesson, LessonSummary, PrismaClientWithAccelerate } from "@oakai/db"; import { Prisma } from "@prisma/client"; import { OpenAIEmbeddings } from "langchain/embeddings/openai"; import { PrismaVectorStore } from "langchain/vectorstores/prisma"; diff --git a/packages/core/src/models/lessons.ts b/packages/core/src/models/lessons.ts index b76e51d52..945f174e7 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -1,11 +1,12 @@ -import { +import type { Lesson, LessonSummary, PrismaClientWithAccelerate, QuizQuestion, Snippet, SnippetVariant, - Transcript, + Transcript} from "@oakai/db"; +import { ZLesson, } from "@oakai/db"; import { ZNewLesson } from "@oakai/db/schemas/lesson"; @@ -17,8 +18,10 @@ import { z } from "zod"; import { inngest } from "../client"; import { createOpenAILangchainClient } from "../llm/openai"; -import { SnippetWithLesson, Snippets } from "./snippets"; -import { Caption, CaptionsSchema } from "./types/caption"; +import type { SnippetWithLesson} from "./snippets"; +import { Snippets } from "./snippets"; +import type { Caption} from "./types/caption"; +import { CaptionsSchema } from "./types/caption"; const log = aiLogger("lessons"); diff --git a/packages/core/src/models/moderations.ts b/packages/core/src/models/moderations.ts index 49cd046d3..7f7acb858 100644 --- a/packages/core/src/models/moderations.ts +++ b/packages/core/src/models/moderations.ts @@ -1,7 +1,8 @@ -import { Moderation, PrismaClientWithAccelerate } from "@oakai/db"; +import type { Moderation, PrismaClientWithAccelerate } from "@oakai/db"; -import { ModerationResult } from "../utils/ailaModeration/moderationSchema"; -import { LessonSnapshots, Snapshot } from "./lessonSnapshots"; +import type { ModerationResult } from "../utils/ailaModeration/moderationSchema"; +import type { Snapshot } from "./lessonSnapshots"; +import { LessonSnapshots } from "./lessonSnapshots"; /** * By default, only moderations which haven't been invalidated returned by this API diff --git a/packages/core/src/models/promptVariants.ts b/packages/core/src/models/promptVariants.ts index 19cd12c0a..0987f326d 100644 --- a/packages/core/src/models/promptVariants.ts +++ b/packages/core/src/models/promptVariants.ts @@ -1,11 +1,11 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import dedent from "ts-dedent"; import { Md5 } from "ts-md5"; import errorHandling from "../prompts/shared/error-handling"; import promptInjection from "../prompts/shared/prompt-injection"; -import { OakPromptDefinition, OakPromptVariant } from "../prompts/types"; +import type { OakPromptDefinition, OakPromptVariant } from "../prompts/types"; const log = aiLogger("prompts"); @@ -49,11 +49,10 @@ export class PromptVariants { const app = await this.prisma.app.findFirstOrThrow({ where: { slug: appSlug }, }); - const maxVersionRows = (await this.prisma - .$queryRaw`select max(version) as max_version from prompts where slug = ${slug}`) as { - max_version: number; - }[]; - const maxVersion = maxVersionRows?.[0]?.max_version ?? 0; + const maxVersionRow = await this.prisma.$queryRaw< + { max_version: number | null }[] + >`SELECT MAX(version) AS max_version FROM prompts WHERE slug = ${slug}`; + const maxVersion = maxVersionRow[0]?.max_version ?? 0; const version = maxVersion + 1; const created = await this.prisma.prompt.create({ data: { diff --git a/packages/core/src/models/prompts.ts b/packages/core/src/models/prompts.ts index 467855ca2..b2a6b633e 100644 --- a/packages/core/src/models/prompts.ts +++ b/packages/core/src/models/prompts.ts @@ -1,8 +1,10 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; -import { structuredLogger, StructuredLogger } from "@oakai/logger"; -import { Logger as InngestLogger } from "inngest/middleware/logger"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; +import type { StructuredLogger } from "@oakai/logger"; +import { structuredLogger } from "@oakai/logger"; +import type { Logger as InngestLogger } from "inngest/middleware/logger"; import { PromptTemplate } from "langchain/prompts"; -import { BaseMessage, SystemMessage } from "langchain/schema"; +import type { BaseMessage} from "langchain/schema"; +import { SystemMessage } from "langchain/schema"; import untruncateJson from "untruncate-json"; import { createOpenAILangchainChatClient } from "../llm/openai"; diff --git a/packages/core/src/models/quizAnswers.ts b/packages/core/src/models/quizAnswers.ts index 0616c2f69..1471c884e 100644 --- a/packages/core/src/models/quizAnswers.ts +++ b/packages/core/src/models/quizAnswers.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { inngest } from "../client"; import { embedWithCache } from "../utils/embeddings"; diff --git a/packages/core/src/models/quizQuestions.ts b/packages/core/src/models/quizQuestions.ts index 77b5966f7..ac3229b01 100644 --- a/packages/core/src/models/quizQuestions.ts +++ b/packages/core/src/models/quizQuestions.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { inngest } from "../client"; import { embedWithCache } from "../utils/embeddings"; diff --git a/packages/core/src/models/safetyViolations.ts b/packages/core/src/models/safetyViolations.ts index d2ceeec5e..f1b4e98ed 100644 --- a/packages/core/src/models/safetyViolations.ts +++ b/packages/core/src/models/safetyViolations.ts @@ -1,12 +1,13 @@ import { clerkClient } from "@clerk/nextjs/server"; -import { +import type { SafetyViolationAction, SafetyViolationRecordType, SafetyViolationSource, PrismaClientWithAccelerate, } from "@oakai/db"; -import { structuredLogger, StructuredLogger } from "@oakai/logger"; -import { Logger as InngestLogger } from "inngest/middleware/logger"; +import type { StructuredLogger } from "@oakai/logger"; +import { structuredLogger } from "@oakai/logger"; +import type { Logger as InngestLogger } from "inngest/middleware/logger"; import { posthogAiBetaServerClient } from "../analytics/posthogAiBetaServerClient"; import { inngest } from "../client"; diff --git a/packages/core/src/models/serializers.ts b/packages/core/src/models/serializers.ts index 37ce0cde7..7e6fce37f 100644 --- a/packages/core/src/models/serializers.ts +++ b/packages/core/src/models/serializers.ts @@ -13,10 +13,11 @@ * As these grow we will likely want to either extract them to their own files, or * make them part of the models */ -import { App, Generation, GenerationStatus, Prompt } from "@oakai/db"; +import type { App, Generation, Prompt } from "@oakai/db"; +import { GenerationStatus } from "@oakai/db"; import { z } from "zod"; -import { AppWithPrompt } from "./apps"; +import type { AppWithPrompt } from "./apps"; type SerializedApp = Pick; diff --git a/packages/core/src/models/snippets.ts b/packages/core/src/models/snippets.ts index 0dd5b0465..32661b9e8 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -1,7 +1,8 @@ +import type { + PrismaClientWithAccelerate, + Snippet} from "@oakai/db"; import { Prisma, - PrismaClientWithAccelerate, - Snippet, SnippetStatus, SnippetVariant, } from "@oakai/db"; diff --git a/packages/core/src/models/statistics.ts b/packages/core/src/models/statistics.ts index 4ed6d4892..3e05d685c 100644 --- a/packages/core/src/models/statistics.ts +++ b/packages/core/src/models/statistics.ts @@ -1,4 +1,5 @@ -import { Prisma, PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; +import { Prisma } from "@oakai/db"; export class Statistics { constructor(private readonly prisma: PrismaClientWithAccelerate) {} diff --git a/packages/core/src/models/transcript.ts b/packages/core/src/models/transcript.ts index b84d47cda..51808e70e 100644 --- a/packages/core/src/models/transcript.ts +++ b/packages/core/src/models/transcript.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { Document } from "langchain/document"; import { StructuredOutputParser } from "langchain/output_parsers"; import { PromptTemplate } from "langchain/prompts"; diff --git a/packages/core/src/prompts/lesson-assistant/index.ts b/packages/core/src/prompts/lesson-assistant/index.ts index 2a9ec853a..bab6e9dba 100644 --- a/packages/core/src/prompts/lesson-assistant/index.ts +++ b/packages/core/src/prompts/lesson-assistant/index.ts @@ -1,6 +1,6 @@ import crypto from "crypto"; -import { LooseLessonPlan } from "../../../../aila/src/protocol/schema"; +import type { LooseLessonPlan } from "../../../../aila/src/protocol/schema"; import { americanToBritish, basedOn, diff --git a/packages/core/src/prompts/lesson-assistant/parts/americanToBritish.ts b/packages/core/src/prompts/lesson-assistant/parts/americanToBritish.ts index 636b44724..20496b259 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/americanToBritish.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/americanToBritish.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; export const americanToBritish = ({ americanisms, diff --git a/packages/core/src/prompts/lesson-assistant/parts/basedOn.ts b/packages/core/src/prompts/lesson-assistant/parts/basedOn.ts index 886fff3f3..14be071fc 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/basedOn.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/basedOn.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; export const basedOn = ({ baseLessonPlan, diff --git a/packages/core/src/prompts/lesson-assistant/parts/body.ts b/packages/core/src/prompts/lesson-assistant/parts/body.ts index 961412731..24ffc180c 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/body.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/body.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; export const body = ({ lessonPlan, responseMode }: TemplateProps) => { const { keyStage } = lessonPlan ?? {}; diff --git a/packages/core/src/prompts/lesson-assistant/parts/context.ts b/packages/core/src/prompts/lesson-assistant/parts/context.ts index 3985a58e5..4ecebd096 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/context.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/context.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { 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.`; diff --git a/packages/core/src/prompts/lesson-assistant/parts/currentLessonPlan.ts b/packages/core/src/prompts/lesson-assistant/parts/currentLessonPlan.ts index 078568f13..d443be7e6 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/currentLessonPlan.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/currentLessonPlan.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; export const currentLessonPlan = ({ lessonPlan, diff --git a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts index 25d9b2616..4918bc804 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/interactingWithTheUser.ts @@ -1,8 +1,8 @@ import { aiLogger } from "@oakai/logger"; -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; import { allSectionsInOrder } from "../../../../../../apps/nextjs/src/lib/lessonPlan/sectionsInOrder"; -import { +import type { LessonPlanKeys, LooseLessonPlan, } from "../../../../../aila/src/protocol/schema"; diff --git a/packages/core/src/prompts/lesson-assistant/parts/protocol.ts b/packages/core/src/prompts/lesson-assistant/parts/protocol.ts index bc92bc84f..ada336e27 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/protocol.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/protocol.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; 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. diff --git a/packages/core/src/prompts/lesson-assistant/parts/rag.ts b/packages/core/src/prompts/lesson-assistant/parts/rag.ts index 29ab5f20e..363a904d5 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/rag.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/rag.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; export const rag = ({ relevantLessonPlans, diff --git a/packages/core/src/prompts/lesson-assistant/parts/schema.ts b/packages/core/src/prompts/lesson-assistant/parts/schema.ts index 9a156e947..6cdc3f5f7 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/schema.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/schema.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; const interactiveOnly = `This is a JSON object that should be generated through the patch instructions that you generate.`; diff --git a/packages/core/src/prompts/lesson-assistant/parts/signOff.ts b/packages/core/src/prompts/lesson-assistant/parts/signOff.ts index fb818b687..b71c9187c 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/signOff.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/signOff.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { 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. diff --git a/packages/core/src/prompts/lesson-assistant/parts/task.ts b/packages/core/src/prompts/lesson-assistant/parts/task.ts index a6e491394..53bf86e58 100644 --- a/packages/core/src/prompts/lesson-assistant/parts/task.ts +++ b/packages/core/src/prompts/lesson-assistant/parts/task.ts @@ -1,4 +1,4 @@ -import { TemplateProps } from ".."; +import type { TemplateProps } from ".."; const interactiveOnly = `Generate (or rewrite) sections 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. diff --git a/packages/core/src/prompts/lesson-assistant/variants.ts b/packages/core/src/prompts/lesson-assistant/variants.ts index 74632f8d1..beb06c26b 100644 --- a/packages/core/src/prompts/lesson-assistant/variants.ts +++ b/packages/core/src/prompts/lesson-assistant/variants.ts @@ -1,7 +1,8 @@ import z from "zod"; -import { TemplateProps, getPromptParts } from "."; -import { OakPromptDefinition, OakPromptVariant } from "../types"; +import type { TemplateProps} from "."; +import { getPromptParts } from "."; +import type { OakPromptDefinition, OakPromptVariant } from "../types"; export const inputSchema = z.object({}); diff --git a/packages/core/src/prompts/lesson-planner/extend-lesson-plan-quiz/index.ts b/packages/core/src/prompts/lesson-planner/extend-lesson-plan-quiz/index.ts index e78c2c72e..69110cc4a 100644 --- a/packages/core/src/prompts/lesson-planner/extend-lesson-plan-quiz/index.ts +++ b/packages/core/src/prompts/lesson-planner/extend-lesson-plan-quiz/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/lesson-planner/generate-lesson-plan/index.ts b/packages/core/src/prompts/lesson-planner/generate-lesson-plan/index.ts index c3c99db98..5ee057276 100644 --- a/packages/core/src/prompts/lesson-planner/generate-lesson-plan/index.ts +++ b/packages/core/src/prompts/lesson-planner/generate-lesson-plan/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import main from "./variants/main"; diff --git a/packages/core/src/prompts/lesson-planner/regenerate-lesson-plan/index.ts b/packages/core/src/prompts/lesson-planner/regenerate-lesson-plan/index.ts index 866dc8ee3..1cfffc2bc 100644 --- a/packages/core/src/prompts/lesson-planner/regenerate-lesson-plan/index.ts +++ b/packages/core/src/prompts/lesson-planner/regenerate-lesson-plan/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/quiz-generator/generate-answers-and-distractors-rag/index.ts b/packages/core/src/prompts/quiz-generator/generate-answers-and-distractors-rag/index.ts index 3e7f3b648..6d984a1d5 100644 --- a/packages/core/src/prompts/quiz-generator/generate-answers-and-distractors-rag/index.ts +++ b/packages/core/src/prompts/quiz-generator/generate-answers-and-distractors-rag/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/quiz-generator/generate-questions-rag/index.ts b/packages/core/src/prompts/quiz-generator/generate-questions-rag/index.ts index 744e10bfb..34ba82f81 100644 --- a/packages/core/src/prompts/quiz-generator/generate-questions-rag/index.ts +++ b/packages/core/src/prompts/quiz-generator/generate-questions-rag/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/quiz-generator/regenerate-all-distractors/index.ts b/packages/core/src/prompts/quiz-generator/regenerate-all-distractors/index.ts index 9e515c70f..7ee5d4555 100644 --- a/packages/core/src/prompts/quiz-generator/regenerate-all-distractors/index.ts +++ b/packages/core/src/prompts/quiz-generator/regenerate-all-distractors/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/quiz-generator/regenerate-answer-rag/index.ts b/packages/core/src/prompts/quiz-generator/regenerate-answer-rag/index.ts index 70f4f08a2..bde34ae5c 100644 --- a/packages/core/src/prompts/quiz-generator/regenerate-answer-rag/index.ts +++ b/packages/core/src/prompts/quiz-generator/regenerate-answer-rag/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/quiz-generator/regenerate-distractor-rag/index.ts b/packages/core/src/prompts/quiz-generator/regenerate-distractor-rag/index.ts index 6e367e735..aed380451 100644 --- a/packages/core/src/prompts/quiz-generator/regenerate-distractor-rag/index.ts +++ b/packages/core/src/prompts/quiz-generator/regenerate-distractor-rag/index.ts @@ -1,4 +1,4 @@ -import { OakPromptDefinition } from "../../types"; +import type { OakPromptDefinition } from "../../types"; import { inputSchema } from "./input.schema"; import { outputSchema } from "./output.schema"; import * as main from "./variants/main"; diff --git a/packages/core/src/prompts/types.ts b/packages/core/src/prompts/types.ts index 344ab23a3..dea74d9ed 100644 --- a/packages/core/src/prompts/types.ts +++ b/packages/core/src/prompts/types.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import type { z } from "zod"; export type OakPromptDefinition = { name: string; diff --git a/packages/core/src/rag/index.ts b/packages/core/src/rag/index.ts index 1967a3d3f..8ce25e86f 100644 --- a/packages/core/src/rag/index.ts +++ b/packages/core/src/rag/index.ts @@ -1,34 +1,27 @@ import { PrismaVectorStore } from "@langchain/community/vectorstores/prisma"; import { OpenAIEmbeddings } from "@langchain/openai"; -import { +import type { KeyStage, LessonPlanPart, PrismaClientWithAccelerate, Subject, } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import { - LessonPlan, - LessonSummary, - Prisma, - PrismaClient, - Snippet, -} from "@prisma/client"; +import type { LessonPlan, LessonSummary, Snippet } from "@prisma/client"; +import { Prisma, PrismaClient } from "@prisma/client"; import { withAccelerate } from "@prisma/extension-accelerate"; import * as Sentry from "@sentry/nextjs"; import { kv } from "@vercel/kv"; import { CohereClient } from "cohere-ai"; -import { RerankResponse } from "cohere-ai/api"; -import { ChatCompletionMessageParam } from "openai/resources/chat/completions"; +import type { RerankResponse } from "cohere-ai/api"; +import type { ChatCompletionMessageParam } from "openai/resources/chat/completions"; import { Md5 } from "ts-md5"; import z from "zod"; import { DEFAULT_CATEGORISE_MODEL } from "../../../aila/src/constants"; -import { - OpenAICompletionWithLogging, - OpenAICompletionWithLoggingOptions, -} from "../../../aila/src/lib/openai/OpenAICompletionWithLogging"; -import { JsonValue } from "../models/prompts"; +import type { OpenAICompletionWithLoggingOptions } from "../../../aila/src/lib/openai/OpenAICompletionWithLogging"; +import { OpenAICompletionWithLogging } from "../../../aila/src/lib/openai/OpenAICompletionWithLogging"; +import type { JsonValue } from "../models/prompts"; import { slugify } from "../utils/slugify"; import { keyStages, subjects } from "../utils/subjects"; @@ -48,6 +41,7 @@ export interface LessonPlanWithPartialLesson extends LessonPlan { } export type SimilarityResultWithScore = [ + // eslint-disable-next-line @typescript-eslint/consistent-type-imports import("@langchain/core/documents").DocumentInterface< // eslint-disable-next-line @typescript-eslint/no-explicit-any Record diff --git a/packages/core/src/scripts/import-new-lessons/createSnippetsForNewLessons.ts b/packages/core/src/scripts/import-new-lessons/createSnippetsForNewLessons.ts index 5ba4f6be5..469f38adf 100644 --- a/packages/core/src/scripts/import-new-lessons/createSnippetsForNewLessons.ts +++ b/packages/core/src/scripts/import-new-lessons/createSnippetsForNewLessons.ts @@ -1,4 +1,4 @@ -import { Snippet, SnippetVariant } from "@oakai/db"; +import type { Snippet, SnippetVariant } from "@oakai/db"; import { prisma } from "@oakai/db"; import { z } from "zod"; diff --git a/packages/core/src/scripts/import-new-lessons/importNewLessons.ts b/packages/core/src/scripts/import-new-lessons/importNewLessons.ts index 63a4a248d..64c78b9ea 100644 --- a/packages/core/src/scripts/import-new-lessons/importNewLessons.ts +++ b/packages/core/src/scripts/import-new-lessons/importNewLessons.ts @@ -1,4 +1,5 @@ -import { Quiz, prisma } from "@oakai/db"; +import type { Quiz} from "@oakai/db"; +import { prisma } from "@oakai/db"; import { GraphQLClient, gql } from "graphql-request"; const batchSize = parseInt(process.env.LESSON_QUERY_BATCH_SIZE || ""); diff --git a/packages/core/src/tracing/mockTracer.ts b/packages/core/src/tracing/mockTracer.ts index 9b645708d..b94a7ca9d 100644 --- a/packages/core/src/tracing/mockTracer.ts +++ b/packages/core/src/tracing/mockTracer.ts @@ -1,4 +1,4 @@ -import { TracingSpan } from "./serverTracing"; +import type { TracingSpan } from "./serverTracing"; class MockSpan implements TracingSpan { tags: Record = {}; diff --git a/packages/core/src/utils/ailaModeration/helpers.ts b/packages/core/src/utils/ailaModeration/helpers.ts index c5eb411ad..df54b56b1 100644 --- a/packages/core/src/utils/ailaModeration/helpers.ts +++ b/packages/core/src/utils/ailaModeration/helpers.ts @@ -1,5 +1,5 @@ import moderationCategories from "./moderationCategories.json"; -import { ModerationBase, ModerationResult } from "./moderationSchema"; +import type { ModerationBase, ModerationResult } from "./moderationSchema"; export function isToxic(result: ModerationBase): boolean { return result.categories.some((category) => diff --git a/packages/core/src/utils/ailaModeration/moderationSchema.ts b/packages/core/src/utils/ailaModeration/moderationSchema.ts index fafe5b7e6..eaf59b0e8 100644 --- a/packages/core/src/utils/ailaModeration/moderationSchema.ts +++ b/packages/core/src/utils/ailaModeration/moderationSchema.ts @@ -1,4 +1,4 @@ -import { JsonValue } from "@prisma/client/runtime/library"; +import type { JsonValue } from "@prisma/client/runtime/library"; import { z } from "zod"; export type ModerationBase = { diff --git a/packages/core/src/utils/getCaptionsFromFile.ts b/packages/core/src/utils/getCaptionsFromFile.ts index ca934a4ca..dc188493a 100644 --- a/packages/core/src/utils/getCaptionsFromFile.ts +++ b/packages/core/src/utils/getCaptionsFromFile.ts @@ -1,6 +1,7 @@ import { Storage } from "@google-cloud/storage"; import { aiLogger } from "@oakai/logger"; -import { Cue, WebVTTParser } from "webvtt-parser"; +import type { Cue} from "webvtt-parser"; +import { WebVTTParser } from "webvtt-parser"; const log = aiLogger("transcripts"); diff --git a/packages/core/src/utils/moderation.ts b/packages/core/src/utils/moderation.ts index 1ad35b08d..31616ea24 100644 --- a/packages/core/src/utils/moderation.ts +++ b/packages/core/src/utils/moderation.ts @@ -5,7 +5,7 @@ import { englishDataset, // eslint-disable-next-line @typescript-eslint/ban-ts-comment englishRecommendedTransformers, // @ts-ignore: Could not find declaration file } from "obscenity"; -import OpenAI from "openai"; +import type OpenAI from "openai"; export const moderationConfig: Record = { MODERATE_PROFANITY: false, diff --git a/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts b/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts index 9225774d9..1931aeefc 100644 --- a/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts +++ b/packages/core/src/utils/rateLimiting/userBasedRateLimiter.ts @@ -1,6 +1,7 @@ -import { clerkClient, User } from "@clerk/nextjs/server"; +import type { User } from "@clerk/nextjs/server"; +import { clerkClient } from "@clerk/nextjs/server"; import { aiLogger } from "@oakai/logger"; -import { Ratelimit } from "@upstash/ratelimit"; +import type { Ratelimit } from "@upstash/ratelimit"; import { waitUntil } from "@vercel/functions"; const log = aiLogger("rate-limiting"); diff --git a/packages/core/src/utils/sendQuizFeedbackEmail.ts b/packages/core/src/utils/sendQuizFeedbackEmail.ts index 867c64a9a..5410b18af 100644 --- a/packages/core/src/utils/sendQuizFeedbackEmail.ts +++ b/packages/core/src/utils/sendQuizFeedbackEmail.ts @@ -1,4 +1,4 @@ -import { GenerationPart } from "../types"; +import type { GenerationPart } from "../types"; import { sendEmail } from "./sendEmail"; const NEXT_PUBLIC_GLEAP_FEEDBACK_EMAIL_ADDR = diff --git a/packages/core/src/utils/slack.ts b/packages/core/src/utils/slack.ts index 858d79217..c489e0fbf 100644 --- a/packages/core/src/utils/slack.ts +++ b/packages/core/src/utils/slack.ts @@ -1,4 +1,5 @@ -import { ActionsBlock, SectionBlock, WebClient } from "@slack/web-api"; +import type { ActionsBlock, SectionBlock} from "@slack/web-api"; +import { WebClient } from "@slack/web-api"; import { uniqueNamesGenerator, adjectives, diff --git a/packages/core/src/workers/generations/requestGeneration.ts b/packages/core/src/workers/generations/requestGeneration.ts index da161792e..bea0b16dd 100644 --- a/packages/core/src/workers/generations/requestGeneration.ts +++ b/packages/core/src/workers/generations/requestGeneration.ts @@ -1,19 +1,22 @@ -import { GenerationStatus, ModerationType, Prisma, prisma } from "@oakai/db"; +import type { Prisma} from "@oakai/db"; +import { GenerationStatus, ModerationType, prisma } from "@oakai/db"; +import type { + StructuredLogger} from "@oakai/logger"; import { structuredLogger as baseLogger, - aiLogger, - StructuredLogger, + aiLogger } from "@oakai/logger"; import { Redis } from "@upstash/redis"; import { NonRetriableError } from "inngest"; -import { z } from "zod"; +import type { z } from "zod"; import { createOpenAIModerationsClient } from "../../llm/openai"; import { SafetyViolations } from "../../models"; import { Generations } from "../../models/generations"; -import { +import type { CompletionResult, - Json, + Json} from "../../models/prompts"; +import { LLMCompletionError, LLMRefusalError, Prompts, diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index 76d707d25..51e3a4511 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -29,7 +29,7 @@ module.exports = { "no-inner-declarations": "warn", "@typescript-eslint/no-unsafe-enum-comparison": "warn", "@typescript-eslint/no-unnecessary-type-assertion": "warn", - "@typescript-eslint/consistent-type-imports": "warn", + "@typescript-eslint/consistent-type-imports": "error", "@typescript-eslint/comma-dangle": "off", "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/require-await": "warn", diff --git a/packages/exports/src/dataHelpers/prepLessonForSlides.ts b/packages/exports/src/dataHelpers/prepLessonForSlides.ts index e3afe14fb..92dbd045f 100644 --- a/packages/exports/src/dataHelpers/prepLessonForSlides.ts +++ b/packages/exports/src/dataHelpers/prepLessonForSlides.ts @@ -1,9 +1,9 @@ -import { AdditionalMaterialsTemplateData } from "../schema/additionalMaterialsDoc.schema"; -import { +import type { AdditionalMaterialsTemplateData } from "../schema/additionalMaterialsDoc.schema"; +import type { ExportableQuizAppState, LessonSlidesInputData, } from "../schema/input.schema"; -import { LessonSlidesTemplateData } from "../schema/lessonSlidesTemplate.schema"; +import type { LessonSlidesTemplateData } from "../schema/lessonSlidesTemplate.schema"; import { camelCaseToTitleCase, processQuizAnswers } from "../utils"; const processQuizAnswersForSlides = ( diff --git a/packages/exports/src/dataHelpers/prepLessonPlanForDocs.ts b/packages/exports/src/dataHelpers/prepLessonPlanForDocs.ts index fa5913f12..69999fd7f 100644 --- a/packages/exports/src/dataHelpers/prepLessonPlanForDocs.ts +++ b/packages/exports/src/dataHelpers/prepLessonPlanForDocs.ts @@ -1,5 +1,5 @@ -import { LessonPlanDocInputData } from "../schema/input.schema"; -import { LessonPlanDocsTemplateData } from "../schema/lessonPlanDocsTemplate.schema"; +import type { LessonPlanDocInputData } from "../schema/input.schema"; +import type { LessonPlanDocsTemplateData } from "../schema/lessonPlanDocsTemplate.schema"; import { camelCaseToTitleCase, processQuizAnswers, diff --git a/packages/exports/src/dataHelpers/prepQuizForDocs.ts b/packages/exports/src/dataHelpers/prepQuizForDocs.ts index 511c5880f..cf837593c 100644 --- a/packages/exports/src/dataHelpers/prepQuizForDocs.ts +++ b/packages/exports/src/dataHelpers/prepQuizForDocs.ts @@ -1,4 +1,4 @@ -import { Quiz } from "../schema/input.schema"; +import type { Quiz } from "../schema/input.schema"; import { processQuizAnswers } from "../utils"; function processQuizAnswersForQuiz( diff --git a/packages/exports/src/dataHelpers/prepWorksheetForSlides.ts b/packages/exports/src/dataHelpers/prepWorksheetForSlides.ts index 826ef76d0..c820f94be 100644 --- a/packages/exports/src/dataHelpers/prepWorksheetForSlides.ts +++ b/packages/exports/src/dataHelpers/prepWorksheetForSlides.ts @@ -1,4 +1,4 @@ -import { WorksheetSlidesInputData } from "../schema/input.schema"; +import type { WorksheetSlidesInputData } from "../schema/input.schema"; export async function prepWorksheetForSlides( lessonPlan: Pick< diff --git a/packages/exports/src/downloadDriveFile.ts b/packages/exports/src/downloadDriveFile.ts index 88346d572..de9f88bfe 100644 --- a/packages/exports/src/downloadDriveFile.ts +++ b/packages/exports/src/downloadDriveFile.ts @@ -1,6 +1,6 @@ import { googleDrive } from "./gSuite/drive/client"; import { getExportFileStream } from "./gSuite/drive/getExportFileStream"; -import { Result } from "./types"; +import type { Result } from "./types"; type OutputData = { stream: NodeJS.ReadableStream; diff --git a/packages/exports/src/exportAdditionalMaterials.ts b/packages/exports/src/exportAdditionalMaterials.ts index 85f93bf1f..4f8d3fe32 100644 --- a/packages/exports/src/exportAdditionalMaterials.ts +++ b/packages/exports/src/exportAdditionalMaterials.ts @@ -4,9 +4,9 @@ import { prepLessonForAdditionalMaterialsDoc } from "./dataHelpers/prepLessonFor import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; import { populateDoc } from "./gSuite/docs/populateDoc"; -import { LessonSlidesInputData } from "./schema/input.schema"; +import type { LessonSlidesInputData } from "./schema/input.schema"; import { getSlidesTemplateIdAdditionalMaterials as getDocsTemplateIdAdditionalMaterials } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; const log = aiLogger("exports"); diff --git a/packages/exports/src/exportDocLessonPlan.ts b/packages/exports/src/exportDocLessonPlan.ts index a935f47b5..f329fdb7c 100644 --- a/packages/exports/src/exportDocLessonPlan.ts +++ b/packages/exports/src/exportDocLessonPlan.ts @@ -2,9 +2,9 @@ import { prepLessonPlanForDocs } from "./dataHelpers/prepLessonPlanForDocs"; import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; import { populateDoc } from "./gSuite/docs/populateDoc"; -import { LessonPlanDocInputData } from "./schema/input.schema"; +import type { LessonPlanDocInputData } from "./schema/input.schema"; import { getDocsTemplateIdLessonPlan } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; export const exportDocLessonPlan = async ({ snapshotId, diff --git a/packages/exports/src/exportDocQuiz.ts b/packages/exports/src/exportDocQuiz.ts index 9414026b6..bfa8a11c1 100644 --- a/packages/exports/src/exportDocQuiz.ts +++ b/packages/exports/src/exportDocQuiz.ts @@ -1,10 +1,10 @@ -import { ExportDocQuizData } from ".."; +import type { ExportDocQuizData } from ".."; import { prepQuizForDocs } from "./dataHelpers/prepQuizForDocs"; import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; import { populateDoc } from "./gSuite/docs/populateDoc"; import { getDocsTemplateIdQuiz } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; const QUIZ_TYPE_LABELS = { starter: "Starter quiz", diff --git a/packages/exports/src/exportDocsWorksheet.ts b/packages/exports/src/exportDocsWorksheet.ts index 84bfffc9e..90168b2d6 100644 --- a/packages/exports/src/exportDocsWorksheet.ts +++ b/packages/exports/src/exportDocsWorksheet.ts @@ -4,9 +4,9 @@ import { prepWorksheetForSlides } from "./dataHelpers/prepWorksheetForSlides"; import { exportGeneric } from "./exportGeneric"; import { getDocsClient } from "./gSuite/docs/client"; import { populateDoc } from "./gSuite/docs/populateDoc"; -import { WorksheetSlidesInputData } from "./schema/input.schema"; +import type { WorksheetSlidesInputData } from "./schema/input.schema"; import { getDocsTemplateIdWorksheet } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; const log = aiLogger("exports"); diff --git a/packages/exports/src/exportGeneric.ts b/packages/exports/src/exportGeneric.ts index 7f0fea661..2444f0757 100644 --- a/packages/exports/src/exportGeneric.ts +++ b/packages/exports/src/exportGeneric.ts @@ -2,7 +2,7 @@ import { addReader } from "./gSuite/drive/addReader"; import { googleDrive } from "./gSuite/drive/client"; import { copyTemplate } from "./gSuite/drive/copyTemplate"; import { getLink } from "./gSuite/drive/getLink"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; export const exportGeneric = async ({ newFileName, diff --git a/packages/exports/src/exportQuizDesignerSlides.ts b/packages/exports/src/exportQuizDesignerSlides.ts index 8a9219558..52c71626b 100644 --- a/packages/exports/src/exportQuizDesignerSlides.ts +++ b/packages/exports/src/exportQuizDesignerSlides.ts @@ -3,9 +3,9 @@ import { exportGeneric } from "./exportGeneric"; import { googleSlides } from "./gSuite/slides/client"; import { deleteSlides } from "./gSuite/slides/deleteSlides"; import { populateSlides } from "./gSuite/slides/populateSlides"; -import { ExportableQuizAppState } from "./schema/input.schema"; +import type { ExportableQuizAppState } from "./schema/input.schema"; import { getQuizDesignerSlidesTemplateIdWorksheet } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; type SpeakerNotesTag = string; diff --git a/packages/exports/src/exportSlidesFullLesson.ts b/packages/exports/src/exportSlidesFullLesson.ts index a440ee514..807124834 100644 --- a/packages/exports/src/exportSlidesFullLesson.ts +++ b/packages/exports/src/exportSlidesFullLesson.ts @@ -1,11 +1,12 @@ import { prepLessonForSlides } from "./dataHelpers/prepLessonForSlides"; import { exportGeneric } from "./exportGeneric"; import { googleSlides } from "./gSuite/slides/client"; -import { SpeakerNotesTag, deleteSlides } from "./gSuite/slides/deleteSlides"; +import type { SpeakerNotesTag} from "./gSuite/slides/deleteSlides"; +import { deleteSlides } from "./gSuite/slides/deleteSlides"; import { populateSlides } from "./gSuite/slides/populateSlides"; -import { LessonSlidesInputData } from "./schema/input.schema"; +import type { LessonSlidesInputData } from "./schema/input.schema"; import { getSlidesTemplateIdFullLesson } from "./templates"; -import { OutputData, Result, State } from "./types"; +import type { OutputData, Result, State } from "./types"; export const exportSlidesFullLesson = async ({ snapshotId, diff --git a/packages/exports/src/gSuite/docs/populateDoc.ts b/packages/exports/src/gSuite/docs/populateDoc.ts index 389fd44a2..2af3b893d 100644 --- a/packages/exports/src/gSuite/docs/populateDoc.ts +++ b/packages/exports/src/gSuite/docs/populateDoc.ts @@ -1,7 +1,8 @@ -import { docs_v1 } from "@googleapis/docs"; +import type { docs_v1 } from "@googleapis/docs"; -import { Result } from "../../types"; -import { ValueToString, defaultValueToString } from "../../utils"; +import type { Result } from "../../types"; +import type { ValueToString} from "../../utils"; +import { defaultValueToString } from "../../utils"; /** * @description Populates the template document with the given data. diff --git a/packages/exports/src/gSuite/drive/addReader.ts b/packages/exports/src/gSuite/drive/addReader.ts index c6ca454ab..053ee2742 100644 --- a/packages/exports/src/gSuite/drive/addReader.ts +++ b/packages/exports/src/gSuite/drive/addReader.ts @@ -1,6 +1,6 @@ -import { drive_v3 } from "@googleapis/drive"; +import type { drive_v3 } from "@googleapis/drive"; -import { Result } from "../../types"; +import type { Result } from "../../types"; /** * @description Adds the specified user as a reader to the file. diff --git a/packages/exports/src/gSuite/drive/copyTemplate.ts b/packages/exports/src/gSuite/drive/copyTemplate.ts index d75849c2a..3cd567040 100644 --- a/packages/exports/src/gSuite/drive/copyTemplate.ts +++ b/packages/exports/src/gSuite/drive/copyTemplate.ts @@ -1,6 +1,6 @@ -import { drive_v3 } from "@googleapis/drive"; +import type { drive_v3 } from "@googleapis/drive"; -import { Result } from "../../types"; +import type { Result } from "../../types"; const folderId = process.env.GOOGLE_DRIVE_OUTPUT_FOLDER_ID; diff --git a/packages/exports/src/gSuite/drive/getExportFileStream.ts b/packages/exports/src/gSuite/drive/getExportFileStream.ts index c4c51f612..e334a5a14 100644 --- a/packages/exports/src/gSuite/drive/getExportFileStream.ts +++ b/packages/exports/src/gSuite/drive/getExportFileStream.ts @@ -1,6 +1,6 @@ -import { drive_v3 } from "@googleapis/drive"; +import type { drive_v3 } from "@googleapis/drive"; -import { Result } from "../../types"; +import type { Result } from "../../types"; const MIME_TYPES = { pdf: "application/pdf", diff --git a/packages/exports/src/gSuite/drive/getLink.ts b/packages/exports/src/gSuite/drive/getLink.ts index f2cdf1d56..3612a8594 100644 --- a/packages/exports/src/gSuite/drive/getLink.ts +++ b/packages/exports/src/gSuite/drive/getLink.ts @@ -1,6 +1,6 @@ -import { drive_v3 } from "@googleapis/drive"; +import type { drive_v3 } from "@googleapis/drive"; -import { Result } from "../../types"; +import type { Result } from "../../types"; /** * @description Returns a link to the file. diff --git a/packages/exports/src/gSuite/slides/deleteSlides.ts b/packages/exports/src/gSuite/slides/deleteSlides.ts index e614b6a23..0e2370641 100644 --- a/packages/exports/src/gSuite/slides/deleteSlides.ts +++ b/packages/exports/src/gSuite/slides/deleteSlides.ts @@ -1,6 +1,6 @@ -import { slides_v1 } from "@googleapis/slides"; +import type { slides_v1 } from "@googleapis/slides"; -import { Result } from "../../types"; +import type { Result } from "../../types"; export type SpeakerNotesTag = "cycle1" | "cycle2" | "cycle3" | string; diff --git a/packages/exports/src/gSuite/slides/populateSlides.ts b/packages/exports/src/gSuite/slides/populateSlides.ts index d635554eb..bdbb6038b 100644 --- a/packages/exports/src/gSuite/slides/populateSlides.ts +++ b/packages/exports/src/gSuite/slides/populateSlides.ts @@ -1,7 +1,8 @@ -import { slides_v1 } from "@googleapis/slides"; +import type { slides_v1 } from "@googleapis/slides"; -import { Result } from "../../types"; -import { ValueToString, defaultValueToString } from "../../utils"; +import type { Result } from "../../types"; +import type { ValueToString} from "../../utils"; +import { defaultValueToString } from "../../utils"; /** * @description Populates the template presentation with the given data. diff --git a/packages/exports/src/schema/input.schema.ts b/packages/exports/src/schema/input.schema.ts index 0a5d9993e..ce7be09c7 100644 --- a/packages/exports/src/schema/input.schema.ts +++ b/packages/exports/src/schema/input.schema.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -import { DeepPartial } from "../types"; +import type { DeepPartial } from "../types"; export const quizQADSchema = z.object({ question: z.string(), diff --git a/packages/exports/src/templates.ts b/packages/exports/src/templates.ts index 2477d944d..97358af9f 100644 --- a/packages/exports/src/templates.ts +++ b/packages/exports/src/templates.ts @@ -1,4 +1,4 @@ -import { LessonPlanDocInputData } from "./schema/input.schema"; +import type { LessonPlanDocInputData } from "./schema/input.schema"; const GOOGLE_DOCS_LESSON_PLAN_TEMPLATE_ID = process.env.GOOGLE_DOCS_LESSON_PLAN_TEMPLATE_ID; diff --git a/packages/ingest/src/captions/getCaptionsByFileName.ts b/packages/ingest/src/captions/getCaptionsByFileName.ts index 8f3d45ba5..84a752fc7 100644 --- a/packages/ingest/src/captions/getCaptionsByFileName.ts +++ b/packages/ingest/src/captions/getCaptionsByFileName.ts @@ -1,6 +1,7 @@ import { Storage } from "@google-cloud/storage"; import { aiLogger } from "@oakai/logger"; -import { Cue, WebVTTParser } from "webvtt-parser"; +import type { Cue} from "webvtt-parser"; +import { WebVTTParser } from "webvtt-parser"; const log = aiLogger("ingest"); diff --git a/packages/ingest/src/chunking/getLessonPlanParts.ts b/packages/ingest/src/chunking/getLessonPlanParts.ts index 0ec5c67fc..eba97ad3c 100644 --- a/packages/ingest/src/chunking/getLessonPlanParts.ts +++ b/packages/ingest/src/chunking/getLessonPlanParts.ts @@ -1,4 +1,4 @@ -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import yaml from "yaml"; // Simplifies the input to a string for embedding diff --git a/packages/ingest/src/config/ingestConfig.ts b/packages/ingest/src/config/ingestConfig.ts index bbafead60..267d27ab8 100644 --- a/packages/ingest/src/config/ingestConfig.ts +++ b/packages/ingest/src/config/ingestConfig.ts @@ -1,6 +1,6 @@ import { z } from "zod"; -const DEFAULT_COMPLETION_TEMPERATURE = 0.7 as const; +const DEFAULT_COMPLETION_TEMPERATURE = 0.7; export const IngestConfigSchema = z.object({ completionModel: z.literal("gpt-4o-2024-08-06"), diff --git a/packages/ingest/src/db-helpers/createCaptionsRecord.ts b/packages/ingest/src/db-helpers/createCaptionsRecord.ts index 3d5d01530..5b7f6564a 100644 --- a/packages/ingest/src/db-helpers/createCaptionsRecord.ts +++ b/packages/ingest/src/db-helpers/createCaptionsRecord.ts @@ -1,7 +1,7 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { getDataHash } from "../utils/getDataHash"; -import { Captions } from "../zod-schema/zodSchema"; +import type { Captions } from "../zod-schema/zodSchema"; export async function createCaptionsRecord({ prisma, diff --git a/packages/ingest/src/db-helpers/createErrorRecord.ts b/packages/ingest/src/db-helpers/createErrorRecord.ts index 0c42adaf0..9022197ab 100644 --- a/packages/ingest/src/db-helpers/createErrorRecord.ts +++ b/packages/ingest/src/db-helpers/createErrorRecord.ts @@ -1,6 +1,6 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { Step } from "./step"; +import type { Step } from "./step"; export async function createErrorRecord({ prisma, diff --git a/packages/ingest/src/db-helpers/createIngestRecord.ts b/packages/ingest/src/db-helpers/createIngestRecord.ts index 101be8ca3..581d88fdc 100644 --- a/packages/ingest/src/db-helpers/createIngestRecord.ts +++ b/packages/ingest/src/db-helpers/createIngestRecord.ts @@ -1,6 +1,6 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { IngestConfig } from "../config/ingestConfig"; +import type { IngestConfig } from "../config/ingestConfig"; export async function createIngestRecord({ prisma, diff --git a/packages/ingest/src/db-helpers/getIngestById.ts b/packages/ingest/src/db-helpers/getIngestById.ts index f388482ad..2e922aa04 100644 --- a/packages/ingest/src/db-helpers/getIngestById.ts +++ b/packages/ingest/src/db-helpers/getIngestById.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { IngestError } from "../IngestError"; import { IngestConfigSchema } from "../config/ingestConfig"; diff --git a/packages/ingest/src/db-helpers/getLatestIngestId.ts b/packages/ingest/src/db-helpers/getLatestIngestId.ts index c0a653e53..e4e96ba33 100644 --- a/packages/ingest/src/db-helpers/getLatestIngestId.ts +++ b/packages/ingest/src/db-helpers/getLatestIngestId.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { IngestError } from "../IngestError"; diff --git a/packages/ingest/src/db-helpers/getLessonsByState.ts b/packages/ingest/src/db-helpers/getLessonsByState.ts index 6fd982a74..556f50bd4 100644 --- a/packages/ingest/src/db-helpers/getLessonsByState.ts +++ b/packages/ingest/src/db-helpers/getLessonsByState.ts @@ -1,7 +1,7 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { RawLessonSchema } from "../zod-schema/zodSchema"; -import { Step, StepStatus } from "./step"; +import type { Step, StepStatus } from "./step"; export async function getLessonsByState({ prisma, diff --git a/packages/ingest/src/db-helpers/loadLessonsAndUpdateState.ts b/packages/ingest/src/db-helpers/loadLessonsAndUpdateState.ts index e6111f914..c72ba1dd0 100644 --- a/packages/ingest/src/db-helpers/loadLessonsAndUpdateState.ts +++ b/packages/ingest/src/db-helpers/loadLessonsAndUpdateState.ts @@ -1,7 +1,7 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { getLessonsByState } from "./getLessonsByState"; -import { Step } from "./step"; +import type { Step } from "./step"; import { updateLessonsState } from "./updateLessonsState"; export async function loadLessonsAndUpdateState({ diff --git a/packages/ingest/src/db-helpers/updateLessonsState.ts b/packages/ingest/src/db-helpers/updateLessonsState.ts index 283f94385..caed07cc6 100644 --- a/packages/ingest/src/db-helpers/updateLessonsState.ts +++ b/packages/ingest/src/db-helpers/updateLessonsState.ts @@ -1,6 +1,6 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { Step, StepStatus } from "./step"; +import type { Step, StepStatus } from "./step"; export async function updateLessonsState({ prisma, diff --git a/packages/ingest/src/embedding/getPartEmbeddingBatchFileLine.ts b/packages/ingest/src/embedding/getPartEmbeddingBatchFileLine.ts index ff96533d4..d9f9fbf12 100644 --- a/packages/ingest/src/embedding/getPartEmbeddingBatchFileLine.ts +++ b/packages/ingest/src/embedding/getPartEmbeddingBatchFileLine.ts @@ -1,4 +1,4 @@ -import { PersistedIngest } from "../db-helpers/getIngestById"; +import type { PersistedIngest } from "../db-helpers/getIngestById"; import { batchLineEmbedding } from "../openai-batches/batchLineEmbedding"; import { createCustomId } from "../openai-batches/customId"; diff --git a/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts b/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts index 224a7910e..14a4b1246 100644 --- a/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts +++ b/packages/ingest/src/embedding/handleEmbeddingBatchSuccess.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; diff --git a/packages/ingest/src/embedding/startEmbedding.ts b/packages/ingest/src/embedding/startEmbedding.ts index 593decedf..0807f71c3 100644 --- a/packages/ingest/src/embedding/startEmbedding.ts +++ b/packages/ingest/src/embedding/startEmbedding.ts @@ -1,7 +1,8 @@ import { startBatch } from "../openai-batches/startBatch"; -import { OpenAiBatchSubmitCallback } from "../openai-batches/submitOpenAiBatch"; +import type { OpenAiBatchSubmitCallback } from "../openai-batches/submitOpenAiBatch"; +import type { + EmbeddingBatchLineProps} from "./getPartEmbeddingBatchFileLine"; import { - EmbeddingBatchLineProps, getPartEmbeddingBatchFileLine, } from "./getPartEmbeddingBatchFileLine"; diff --git a/packages/ingest/src/generate-lesson-plans/getLessonPlanBatchFileLine.ts b/packages/ingest/src/generate-lesson-plans/getLessonPlanBatchFileLine.ts index a486aa8f1..de0bc1c71 100644 --- a/packages/ingest/src/generate-lesson-plans/getLessonPlanBatchFileLine.ts +++ b/packages/ingest/src/generate-lesson-plans/getLessonPlanBatchFileLine.ts @@ -1,13 +1,14 @@ +import type { + LooseLessonPlan} from "@oakai/aila/src/protocol/schema"; import { - CompletedLessonPlanSchema, - LooseLessonPlan, + CompletedLessonPlanSchema } from "@oakai/aila/src/protocol/schema"; import { zodResponseFormat } from "openai/helpers/zod"; -import { PersistedIngest } from "../db-helpers/getIngestById"; +import type { PersistedIngest } from "../db-helpers/getIngestById"; import { batchLineCompletion } from "../openai-batches/batchLineCompletion"; import { createCustomId } from "../openai-batches/customId"; -import { Captions, RawLesson } from "../zod-schema/zodSchema"; +import type { Captions, RawLesson } from "../zod-schema/zodSchema"; import { getSystemPrompt } from "./getSystemPrompt"; import { getUserPrompt } from "./getUserPrompt"; diff --git a/packages/ingest/src/generate-lesson-plans/getSystemPrompt.ts b/packages/ingest/src/generate-lesson-plans/getSystemPrompt.ts index 10bdae7ba..397bf8000 100644 --- a/packages/ingest/src/generate-lesson-plans/getSystemPrompt.ts +++ b/packages/ingest/src/generate-lesson-plans/getSystemPrompt.ts @@ -1,10 +1,11 @@ +import type { + LooseLessonPlan} from "@oakai/aila/src/protocol/schema"; import { - LooseLessonPlan, LessonPlanJsonSchema, } from "@oakai/aila/src/protocol/schema"; import { template } from "@oakai/core/src/prompts/lesson-assistant"; -import { RawLesson } from "../zod-schema/zodSchema"; +import type { RawLesson } from "../zod-schema/zodSchema"; export function getSystemPrompt({ rawLesson }: { rawLesson: RawLesson }) { const { lessonTitle, keyStageSlug, subjectSlug } = rawLesson; diff --git a/packages/ingest/src/generate-lesson-plans/getUserPrompt.test.ts b/packages/ingest/src/generate-lesson-plans/getUserPrompt.test.ts index 21baaf22d..0587ab90c 100644 --- a/packages/ingest/src/generate-lesson-plans/getUserPrompt.test.ts +++ b/packages/ingest/src/generate-lesson-plans/getUserPrompt.test.ts @@ -1,7 +1,7 @@ import fs from "node:fs"; import rawLesson from "../fixtures/rawLesson.json"; -import { Captions } from "../zod-schema/zodSchema"; +import type { Captions } from "../zod-schema/zodSchema"; import { getUserPrompt } from "./getUserPrompt"; describe("getUserPrompt", () => { diff --git a/packages/ingest/src/generate-lesson-plans/getUserPrompt.ts b/packages/ingest/src/generate-lesson-plans/getUserPrompt.ts index d2abd5e36..38cc23ca5 100644 --- a/packages/ingest/src/generate-lesson-plans/getUserPrompt.ts +++ b/packages/ingest/src/generate-lesson-plans/getUserPrompt.ts @@ -1,7 +1,7 @@ import { isTruthy } from "remeda"; import { IngestError } from "../IngestError"; -import { RawLesson, Captions } from "../zod-schema/zodSchema"; +import type { RawLesson, Captions } from "../zod-schema/zodSchema"; import { exitQuizPromptPart } from "./user-prompt-parts/exitQuiz.promptPart"; import { keyLearningPointsPromptPart } from "./user-prompt-parts/keyLearningPoints.promptPart"; import { lessonKeywordsPromptPart } from "./user-prompt-parts/keywords.promptPart"; diff --git a/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts b/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts index c8edad819..5efb6e686 100644 --- a/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts +++ b/packages/ingest/src/generate-lesson-plans/handleLessonPlanBatchSuccess.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; diff --git a/packages/ingest/src/generate-lesson-plans/startGenerating.ts b/packages/ingest/src/generate-lesson-plans/startGenerating.ts index 42be207bf..b4bc0d87e 100644 --- a/packages/ingest/src/generate-lesson-plans/startGenerating.ts +++ b/packages/ingest/src/generate-lesson-plans/startGenerating.ts @@ -1,7 +1,7 @@ -import { PersistedIngest } from "../db-helpers/getIngestById"; -import { PersistedIngestLesson } from "../db-helpers/getLessonsByState"; +import type { PersistedIngest } from "../db-helpers/getIngestById"; +import type { PersistedIngestLesson } from "../db-helpers/getLessonsByState"; import { startBatch } from "../openai-batches/startBatch"; -import { OpenAiBatchSubmitCallback } from "../openai-batches/submitOpenAiBatch"; +import type { OpenAiBatchSubmitCallback } from "../openai-batches/submitOpenAiBatch"; import { CaptionsSchema } from "../zod-schema/zodSchema"; import { getLessonPlanBatchFileLine } from "./getLessonPlanBatchFileLine"; diff --git a/packages/ingest/src/generate-lesson-plans/transformQuiz.test.ts b/packages/ingest/src/generate-lesson-plans/transformQuiz.test.ts index 9f7428bfd..5e7a175ec 100644 --- a/packages/ingest/src/generate-lesson-plans/transformQuiz.test.ts +++ b/packages/ingest/src/generate-lesson-plans/transformQuiz.test.ts @@ -1,4 +1,4 @@ -import { OakLessonQuiz } from "../zod-schema/zodSchema"; +import type { OakLessonQuiz } from "../zod-schema/zodSchema"; import { transformQuiz } from "./transformQuiz"; describe("transformQuiz", () => { diff --git a/packages/ingest/src/generate-lesson-plans/transformQuiz.ts b/packages/ingest/src/generate-lesson-plans/transformQuiz.ts index 6ce2dd248..9b320a6d5 100644 --- a/packages/ingest/src/generate-lesson-plans/transformQuiz.ts +++ b/packages/ingest/src/generate-lesson-plans/transformQuiz.ts @@ -1,8 +1,8 @@ -import { Quiz } from "@oakai/aila/src/protocol/schema"; +import type { Quiz } from "@oakai/aila/src/protocol/schema"; import { isTruthy, partition } from "remeda"; import { IngestError } from "../IngestError"; -import { OakLessonQuiz } from "../zod-schema/zodSchema"; +import type { OakLessonQuiz } from "../zod-schema/zodSchema"; export function transformQuiz(oakQuiz: OakLessonQuiz): Quiz { const quiz: Quiz = []; diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/exitQuiz.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/exitQuiz.promptPart.ts index db834f537..25508ac94 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/exitQuiz.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/exitQuiz.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; import { transformQuiz } from "../transformQuiz"; export function exitQuizPromptPart(rawLesson: RawLesson) { diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keyLearningPoints.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keyLearningPoints.promptPart.ts index 7f9182725..9d0514e1d 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keyLearningPoints.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keyLearningPoints.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; import { toMarkdownList } from "./toMarkdownList"; export const keyLearningPointsPromptPart = ({ diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keywords.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keywords.promptPart.ts index 44e65328f..4a68728bb 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keywords.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/keywords.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; import { toMarkdownList } from "./toMarkdownList"; export const lessonKeywordsPromptPart = ({ lessonKeywords }: RawLesson) => diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/learningOutcome.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/learningOutcome.promptPart.ts index 5cbcbc1b8..a03199f25 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/learningOutcome.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/learningOutcome.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; export const learningOutcomePromptPart = ({ pupilLessonOutcome }: RawLesson) => pupilLessonOutcome diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/misconceptions.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/misconceptions.promptPart.ts index 8912fa3fa..1bd7ccabd 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/misconceptions.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/misconceptions.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; import { toMarkdownList } from "./toMarkdownList"; export const misconceptionsPromptPart = ({ diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/starterQuiz.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/starterQuiz.promptPart.ts index 927e51d93..be125e68d 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/starterQuiz.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/starterQuiz.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; import { transformQuiz } from "../transformQuiz"; export function starterQuizPromptPart(rawLesson: RawLesson) { diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/titleSubjectKeyStage.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/titleSubjectKeyStage.promptPart.ts index 19fdb4319..55bf54313 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/titleSubjectKeyStage.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/titleSubjectKeyStage.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; export const titleSubjectKeyStagePromptPart = ({ lessonTitle, diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/transcript.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/transcript.promptPart.ts index 37ea3b55b..6b035f888 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/transcript.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/transcript.promptPart.ts @@ -1,4 +1,4 @@ -import { Captions } from "../../zod-schema/zodSchema"; +import type { Captions } from "../../zod-schema/zodSchema"; export const transcriptPromptPart = ( captions: Captions, diff --git a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/year.promptPart.ts b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/year.promptPart.ts index 9425fa1aa..67e61958c 100644 --- a/packages/ingest/src/generate-lesson-plans/user-prompt-parts/year.promptPart.ts +++ b/packages/ingest/src/generate-lesson-plans/user-prompt-parts/year.promptPart.ts @@ -1,4 +1,4 @@ -import { RawLesson } from "../../zod-schema/zodSchema"; +import type { RawLesson } from "../../zod-schema/zodSchema"; export const yearPromptPart = ({ yearTitle }: RawLesson) => yearTitle ? `The lesson is intended for ${yearTitle}.` : null; diff --git a/packages/ingest/src/import-lessons/importLessons.ts b/packages/ingest/src/import-lessons/importLessons.ts index 85afffb6b..94e9ba87e 100644 --- a/packages/ingest/src/import-lessons/importLessons.ts +++ b/packages/ingest/src/import-lessons/importLessons.ts @@ -3,7 +3,8 @@ import { aiLogger } from "@oakai/logger"; import { IngestError } from "../IngestError"; import { getDataHash } from "../utils/getDataHash"; -import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; +import type { RawLesson} from "../zod-schema/zodSchema"; +import { RawLessonSchema } from "../zod-schema/zodSchema"; import { graphqlClient } from "./graphql/client"; import { query } from "./graphql/query"; diff --git a/packages/ingest/src/import-lessons/importLessonsFromCSV.ts b/packages/ingest/src/import-lessons/importLessonsFromCSV.ts index 0c674e0b0..b503db7a7 100644 --- a/packages/ingest/src/import-lessons/importLessonsFromCSV.ts +++ b/packages/ingest/src/import-lessons/importLessonsFromCSV.ts @@ -6,7 +6,8 @@ import fs from "node:fs"; import { IngestError } from "../IngestError"; import { chunkAndPromiseAll } from "../utils/chunkAndPromiseAll"; import { getDataHash } from "../utils/getDataHash"; -import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; +import type { RawLesson} from "../zod-schema/zodSchema"; +import { RawLessonSchema } from "../zod-schema/zodSchema"; const log = aiLogger("ingest"); diff --git a/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts b/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts index 1ba873b0c..03eebfb2b 100644 --- a/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts +++ b/packages/ingest/src/import-lessons/importLessonsFromOakDB.ts @@ -1,9 +1,10 @@ import { prisma } from "@oakai/db"; import { IngestError } from "../IngestError"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; import { getDataHash } from "../utils/getDataHash"; -import { RawLesson, RawLessonSchema } from "../zod-schema/zodSchema"; +import type { RawLesson} from "../zod-schema/zodSchema"; +import { RawLessonSchema } from "../zod-schema/zodSchema"; import { graphqlClient } from "./graphql/client"; import { query } from "./graphql/query"; diff --git a/packages/ingest/src/openai-batches/batchLineCompletion.ts b/packages/ingest/src/openai-batches/batchLineCompletion.ts index d71bdc7b0..cfc51634e 100644 --- a/packages/ingest/src/openai-batches/batchLineCompletion.ts +++ b/packages/ingest/src/openai-batches/batchLineCompletion.ts @@ -1,4 +1,4 @@ -import { zodResponseFormat } from "openai/helpers/zod"; +import type { zodResponseFormat } from "openai/helpers/zod"; export function batchLineCompletion({ customId, diff --git a/packages/ingest/src/openai-batches/handleOpenAiBatchErrorFile.ts b/packages/ingest/src/openai-batches/handleOpenAiBatchErrorFile.ts index e204a3429..2751653db 100644 --- a/packages/ingest/src/openai-batches/handleOpenAiBatchErrorFile.ts +++ b/packages/ingest/src/openai-batches/handleOpenAiBatchErrorFile.ts @@ -1,9 +1,10 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { Step } from "../db-helpers/step"; +import type { Step } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; import { jsonlToArray } from "../utils/jsonlToArray"; -import { BatchTask, getLessonIdFromCustomId } from "./customId"; +import type { BatchTask} from "./customId"; +import { getLessonIdFromCustomId } from "./customId"; import { downloadOpenAiFile } from "./downloadOpenAiFile"; function getStepFromTask(task: BatchTask): Step { diff --git a/packages/ingest/src/openai-batches/startBatch.ts b/packages/ingest/src/openai-batches/startBatch.ts index 4569dead4..1e737a46d 100644 --- a/packages/ingest/src/openai-batches/startBatch.ts +++ b/packages/ingest/src/openai-batches/startBatch.ts @@ -3,12 +3,14 @@ import { aiLogger } from "@oakai/logger"; import { splitJsonlByRowsOrSize } from "../utils/splitJsonlByRowsOrSize"; import { OPEN_AI_BATCH_MAX_ROWS, OPEN_AI_BATCH_MAX_SIZE_MB } from "./constants"; import { getCustomIdsFromJsonlFile } from "./getCustomIdsFromJsonlFile"; +import type { + OpenAiBatchSubmitCallback} from "./submitOpenAiBatch"; import { - OpenAiBatchSubmitCallback, submitOpenAiBatch, } from "./submitOpenAiBatch"; import { uploadOpenAiBatchFile } from "./uploadOpenAiBatchFile"; -import { GetBatchFileLine, writeBatchFile } from "./writeBatchFile"; +import type { GetBatchFileLine} from "./writeBatchFile"; +import { writeBatchFile } from "./writeBatchFile"; const log = aiLogger("ingest"); diff --git a/packages/ingest/src/openai-batches/submitOpenAiBatch.ts b/packages/ingest/src/openai-batches/submitOpenAiBatch.ts index deb863666..b8df4bdd6 100644 --- a/packages/ingest/src/openai-batches/submitOpenAiBatch.ts +++ b/packages/ingest/src/openai-batches/submitOpenAiBatch.ts @@ -1,4 +1,4 @@ -import { OpenAI } from "openai"; +import type { OpenAI } from "openai"; import { openai } from "./openai"; diff --git a/packages/ingest/src/steps/0-start.ts b/packages/ingest/src/steps/0-start.ts index aa7eccdb0..776a2f286 100644 --- a/packages/ingest/src/steps/0-start.ts +++ b/packages/ingest/src/steps/0-start.ts @@ -1,11 +1,11 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { IngestError } from "../IngestError"; -import { IngestConfig } from "../config/ingestConfig"; +import type { IngestConfig } from "../config/ingestConfig"; import { createIngestRecord } from "../db-helpers/createIngestRecord"; import { importLessonsFromCSV } from "../import-lessons/importLessonsFromCSV"; import { importLessonsFromOakDB } from "../import-lessons/importLessonsFromOakDB"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; const config: IngestConfig = { completionModel: "gpt-4o-2024-08-06", diff --git a/packages/ingest/src/steps/1-captions.ts b/packages/ingest/src/steps/1-captions.ts index 2c022e876..de3f13bd2 100644 --- a/packages/ingest/src/steps/1-captions.ts +++ b/packages/ingest/src/steps/1-captions.ts @@ -1,4 +1,4 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { IngestError } from "../IngestError"; import { getCaptionsByFileName } from "../captions/getCaptionsByFileName"; @@ -7,10 +7,11 @@ import { createCaptionsRecord } from "../db-helpers/createCaptionsRecord"; import { createErrorRecord } from "../db-helpers/createErrorRecord"; import { getIngestById } from "../db-helpers/getIngestById"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; -import { Step, getPrevStep } from "../db-helpers/step"; +import type { Step} from "../db-helpers/step"; +import { getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; -import { IngestLogger } from "../types"; -import { Captions } from "../zod-schema/zodSchema"; +import type { IngestLogger } from "../types"; +import type { Captions } from "../zod-schema/zodSchema"; const currentStep: Step = "captions_fetch"; const prevStep = getPrevStep(currentStep); diff --git a/packages/ingest/src/steps/2-lp-batch-start.ts b/packages/ingest/src/steps/2-lp-batch-start.ts index 2b3efe80f..2cb66eaa4 100644 --- a/packages/ingest/src/steps/2-lp-batch-start.ts +++ b/packages/ingest/src/steps/2-lp-batch-start.ts @@ -1,11 +1,12 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { getIngestById } from "../db-helpers/getIngestById"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; -import { Step, getPrevStep } from "../db-helpers/step"; +import type { Step} from "../db-helpers/step"; +import { getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; import { startGenerating } from "../generate-lesson-plans/startGenerating"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; const currentStep: Step = "lesson_plan_generation"; const prevStep = getPrevStep(currentStep); diff --git a/packages/ingest/src/steps/3-lp-batch-sync.ts b/packages/ingest/src/steps/3-lp-batch-sync.ts index 7bf8c9620..8b6db42a8 100644 --- a/packages/ingest/src/steps/3-lp-batch-sync.ts +++ b/packages/ingest/src/steps/3-lp-batch-sync.ts @@ -1,9 +1,9 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { handleLessonPlanBatchSuccess } from "../generate-lesson-plans/handleLessonPlanBatchSuccess"; import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; /** * Check status of lesson plan generation batches and action diff --git a/packages/ingest/src/steps/4-lp-chunking.ts b/packages/ingest/src/steps/4-lp-chunking.ts index 1718cf158..edc072e93 100644 --- a/packages/ingest/src/steps/4-lp-chunking.ts +++ b/packages/ingest/src/steps/4-lp-chunking.ts @@ -1,14 +1,16 @@ +import type { + CompletedLessonPlan} from "@oakai/aila/src/protocol/schema"; import { - CompletedLessonPlan, CompletedLessonPlanSchema, } from "@oakai/aila/src/protocol/schema"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { getLessonPlanParts } from "../chunking/getLessonPlanParts"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; -import { Step, getPrevStep } from "../db-helpers/step"; +import type { Step} from "../db-helpers/step"; +import { getPrevStep } from "../db-helpers/step"; import { updateLessonsState } from "../db-helpers/updateLessonsState"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; const currentStep: Step = "chunking"; const prevStep = getPrevStep(currentStep); diff --git a/packages/ingest/src/steps/5-lp-parts-embed-start.ts b/packages/ingest/src/steps/5-lp-parts-embed-start.ts index 8aea6fb4d..9b97b9b77 100644 --- a/packages/ingest/src/steps/5-lp-parts-embed-start.ts +++ b/packages/ingest/src/steps/5-lp-parts-embed-start.ts @@ -1,11 +1,12 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { getIngestById } from "../db-helpers/getIngestById"; import { loadLessonsAndUpdateState } from "../db-helpers/loadLessonsAndUpdateState"; -import { Step, getPrevStep } from "../db-helpers/step"; +import type { Step} from "../db-helpers/step"; +import { getPrevStep } from "../db-helpers/step"; import { startEmbedding } from "../embedding/startEmbedding"; import { parseCustomId } from "../openai-batches/customId"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; import { chunkAndPromiseAll } from "../utils/chunkAndPromiseAll"; const currentStep: Step = "embedding"; diff --git a/packages/ingest/src/steps/6-lp-parts-embed-sync.ts b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts index 69727ab22..8940465d8 100644 --- a/packages/ingest/src/steps/6-lp-parts-embed-sync.ts +++ b/packages/ingest/src/steps/6-lp-parts-embed-sync.ts @@ -1,9 +1,9 @@ -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { handleEmbeddingBatchSuccess } from "../embedding/handleEmbeddingBatchSuccess"; import { handleOpenAiBatchErrorFile } from "../openai-batches/handleOpenAiBatchErrorFile"; import { retrieveOpenAiBatch } from "../openai-batches/retrieveOpenAiBatch"; -import { IngestLogger } from "../types"; +import type { IngestLogger } from "../types"; /** * Check status of lesson plan generation batches and action diff --git a/packages/ingest/src/types.ts b/packages/ingest/src/types.ts index 66f91c873..469453d9f 100644 --- a/packages/ingest/src/types.ts +++ b/packages/ingest/src/types.ts @@ -1,4 +1,4 @@ -import { StructuredLogger } from "@oakai/logger"; +import type { StructuredLogger } from "@oakai/logger"; export type IngestLogger = { info: (...args: unknown[]) => void; diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 02e00c6ff..d0eadd815 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -1,7 +1,8 @@ import debug from "debug"; import browserLogger from "./browser"; -import structuredLogger, { StructuredLogger } from "./structuredLogger"; +import type { StructuredLogger } from "./structuredLogger"; +import structuredLogger from "./structuredLogger"; if (typeof window !== "undefined") { debug.enable("ai:*"); From 4e5e1f22a4ea6262114033e45ffe4817b483f379 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 12:43:18 +0000 Subject: [PATCH 041/127] feat: selectively include Americanisms, RAG, analytics when instantiating Aila (#287) --- apps/nextjs/src/app/api/chat/chatHandler.ts | 13 ++ packages/aila/src/core/Aila.ts | 33 +++- packages/aila/src/core/AilaFeatureFactory.ts | 18 +- packages/aila/src/core/AilaServices.ts | 6 +- packages/aila/src/core/lesson/AilaLesson.ts | 5 +- .../builders/AilaLessonPromptBuilder.ts | 24 +-- packages/aila/src/core/types.ts | 7 + .../features/americanisms/AilaAmericanisms.ts | 69 ++++++++ .../americanisms/NullAilaAmericanisms.ts | 7 + .../american-british-english-translator.d.ts | 0 .../aila/src/features/americanisms/index.ts | 16 ++ .../src/features/analytics/AilaAnalytics.ts | 2 +- .../analytics/adapters/AnalyticsAdapter.ts | 2 +- .../adapters/PosthogAnalyticsAdapter.ts | 2 +- .../categorisers/AilaCategorisation.ts | 160 ++++++++++++++---- packages/aila/src/features/rag/AilaRag.ts | 5 +- packages/aila/src/features/rag/NullAilaRag.ts | 7 + packages/aila/src/features/rag/index.ts | 9 +- .../src/utils/language/findAmericanisms.ts | 75 -------- .../lessonPlan/fetchLessonPlanContentById.ts | 7 +- .../moderation/moderationErrorHandling.ts | 2 +- packages/eslint-config-custom/index.js | 2 +- packages/logger/index.ts | 1 + 23 files changed, 308 insertions(+), 164 deletions(-) create mode 100644 packages/aila/src/features/americanisms/AilaAmericanisms.ts create mode 100644 packages/aila/src/features/americanisms/NullAilaAmericanisms.ts rename packages/aila/src/{utils/language => features/americanisms}/american-british-english-translator.d.ts (100%) create mode 100644 packages/aila/src/features/americanisms/index.ts create mode 100644 packages/aila/src/features/rag/NullAilaRag.ts delete mode 100644 packages/aila/src/utils/language/findAmericanisms.ts diff --git a/apps/nextjs/src/app/api/chat/chatHandler.ts b/apps/nextjs/src/app/api/chat/chatHandler.ts index 3283a993d..907246e72 100644 --- a/apps/nextjs/src/app/api/chat/chatHandler.ts +++ b/apps/nextjs/src/app/api/chat/chatHandler.ts @@ -3,8 +3,15 @@ import type { AilaInitializationOptions, AilaOptions, AilaPublicChatOptions, + AilaServices, Message, } from "@oakai/aila"; +import { AilaAmericanisms } from "@oakai/aila/src/features/americanisms/AilaAmericanisms"; +import { + DatadogAnalyticsAdapter, + PosthogAnalyticsAdapter, +} from "@oakai/aila/src/features/analytics"; +import { AilaRag } from "@oakai/aila/src/features/rag/AilaRag"; import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { TracingSpan, @@ -176,6 +183,12 @@ export async function handleChatPostRequest( services: { chatLlmService: llmService, moderationAiClient, + ragService: (aila: AilaServices) => new AilaRag({ aila }), + americanismsService: () => new AilaAmericanisms(), + analyticsAdapters: (aila: AilaServices) => [ + new PosthogAnalyticsAdapter(aila), + new DatadogAnalyticsAdapter(aila), + ], }, lessonPlan: lessonPlan ?? {}, }; diff --git a/packages/aila/src/core/Aila.ts b/packages/aila/src/core/Aila.ts index b47aabd39..41de60f53 100644 --- a/packages/aila/src/core/Aila.ts +++ b/packages/aila/src/core/Aila.ts @@ -1,4 +1,4 @@ -import type { PrismaClientWithAccelerate} from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; @@ -7,7 +7,11 @@ import { DEFAULT_TEMPERATURE, DEFAULT_RAG_LESSON_PLANS, } from "../constants"; +import type { AilaAmericanismsFeature } from "../features/americanisms"; +import { NullAilaAmericanisms } from "../features/americanisms/NullAilaAmericanisms"; import { AilaCategorisation } from "../features/categorisation"; +import type { AilaRagFeature } from "../features/rag"; +import { NullAilaRag } from "../features/rag/NullAilaRag"; import type { AilaSnapshotStore } from "../features/snapshotStore"; import type { AilaAnalyticsFeature, @@ -51,9 +55,11 @@ export class Aila implements AilaServices { private _persistence: AilaPersistenceFeature[] = []; private _threatDetection?: AilaThreatDetectionFeature; private _prisma: PrismaClientWithAccelerate; + private _rag: AilaRagFeature; private _plugins: AilaPlugin[]; private _userId!: string | undefined; private _chatId!: string; + private _americanisms: AilaAmericanismsFeature; constructor(options: AilaInitializationOptions) { this._userId = options.chat.userId; @@ -79,13 +85,14 @@ export class Aila implements AilaServices { options.services?.chatCategoriser ?? new AilaCategorisation({ aila: this, - prisma: this._prisma, - chatId: this._chatId, - userId: this._userId, }), }); - this._analytics = AilaFeatureFactory.createAnalytics(this, this._options); + this._analytics = AilaFeatureFactory.createAnalytics( + this, + this._options, + options.services?.analyticsAdapters?.(this), + ); this._moderation = AilaFeatureFactory.createModeration( this, this._options, @@ -104,6 +111,10 @@ export class Aila implements AilaServices { this, this._options, ); + this._rag = options.services?.ragService?.(this) ?? new NullAilaRag(); + this._americanisms = + options.services?.americanismsService?.(this) ?? + new NullAilaAmericanisms(); if (this._analytics) { this._analytics.initialiseAnalyticsContext(); @@ -212,6 +223,18 @@ export class Aila implements AilaServices { return this._chatLlmService; } + public get rag() { + return this._rag; + } + + public get americanisms() { + return this._americanisms; + } + + public get prisma() { + return this._prisma; + } + // Check methods public checkUserIdPresentIfPersisting() { if (!this._chat.userId && this._options.usePersistence) { diff --git a/packages/aila/src/core/AilaFeatureFactory.ts b/packages/aila/src/core/AilaFeatureFactory.ts index 3730830ab..e25d20069 100644 --- a/packages/aila/src/core/AilaFeatureFactory.ts +++ b/packages/aila/src/core/AilaFeatureFactory.ts @@ -1,16 +1,10 @@ // AilaFeatureFactory.ts -import { - DatadogAnalyticsAdapter, - PosthogAnalyticsAdapter, -} from "../features/analytics"; +import type { AnalyticsAdapter } from "../features/analytics"; import { AilaAnalytics } from "../features/analytics/AilaAnalytics"; import { SentryErrorReporter } from "../features/errorReporting/reporters/SentryErrorReporter"; import { AilaModeration } from "../features/moderation"; -import type { - OpenAILike} from "../features/moderation/moderators/OpenAiModerator"; -import { - OpenAiModerator, -} from "../features/moderation/moderators/OpenAiModerator"; +import type { OpenAILike } from "../features/moderation/moderators/OpenAiModerator"; +import { OpenAiModerator } from "../features/moderation/moderators/OpenAiModerator"; import { AilaPrismaPersistence } from "../features/persistence/adaptors/prisma"; import { AilaSnapshotStore } from "../features/snapshotStore"; import { AilaThreatDetection } from "../features/threatDetection"; @@ -28,14 +22,12 @@ export class AilaFeatureFactory { static createAnalytics( aila: AilaServices, options: AilaOptions, + adapters: AnalyticsAdapter[] = [], ): AilaAnalyticsFeature | undefined { if (options.useAnalytics) { return new AilaAnalytics({ aila, - adapters: [ - new PosthogAnalyticsAdapter(aila), - new DatadogAnalyticsAdapter(aila), - ], + adapters, }); } return undefined; diff --git a/packages/aila/src/core/AilaServices.ts b/packages/aila/src/core/AilaServices.ts index 2b0eb1968..816a4b09e 100644 --- a/packages/aila/src/core/AilaServices.ts +++ b/packages/aila/src/core/AilaServices.ts @@ -1,5 +1,7 @@ +import type { AilaAmericanismsFeature } from "../features/americanisms"; import type { AilaAnalytics } from "../features/analytics/AilaAnalytics"; import type { AilaErrorReporter } from "../features/errorReporting"; +import type { AilaRagFeature } from "../features/rag"; import type { AilaSnapshotStore } from "../features/snapshotStore"; import type { AilaAnalyticsFeature, @@ -14,8 +16,8 @@ import type { LooseLessonPlan, } from "../protocol/schema"; import type { Message } from "./chat"; -import type { AilaOptionsWithDefaultFallbackValues } from "./index"; import type { AilaPlugin } from "./plugins"; +import type { AilaOptionsWithDefaultFallbackValues } from "./types"; // This provides a set of interfaces between the Aila core and the features that use it. // We can then mock these out in tests without needing to instantiate the entire Aila object. @@ -64,4 +66,6 @@ export interface AilaServices { readonly persistence?: AilaPersistenceFeature[]; readonly moderation?: AilaModerationFeature; readonly plugins: AilaPlugin[]; + readonly rag: AilaRagFeature; + readonly americanisms: AilaAmericanismsFeature; } diff --git a/packages/aila/src/core/lesson/AilaLesson.ts b/packages/aila/src/core/lesson/AilaLesson.ts index 9810cdc8f..e2b5003e5 100644 --- a/packages/aila/src/core/lesson/AilaLesson.ts +++ b/packages/aila/src/core/lesson/AilaLesson.ts @@ -3,8 +3,7 @@ import { deepClone } from "fast-json-patch"; import { AilaCategorisation } from "../../features/categorisation/categorisers/AilaCategorisation"; import type { AilaCategorisationFeature } from "../../features/types"; -import type { - PatchDocument} from "../../protocol/jsonPatchProtocol"; +import type { PatchDocument } from "../../protocol/jsonPatchProtocol"; import { applyLessonPlanPatch, extractPatches, @@ -39,8 +38,6 @@ export class AilaLesson implements AilaLessonService { categoriser ?? new AilaCategorisation({ aila, - userId: aila.userId, - chatId: aila.chatId, }); } diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index 621c34613..b9afc0cc5 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -1,27 +1,17 @@ -import type { - TemplateProps} from "@oakai/core/src/prompts/lesson-assistant"; -import { - template, -} from "@oakai/core/src/prompts/lesson-assistant"; +import type { TemplateProps } from "@oakai/core/src/prompts/lesson-assistant"; +import { template } from "@oakai/core/src/prompts/lesson-assistant"; import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { DEFAULT_RAG_LESSON_PLANS } from "../../../constants"; import { tryWithErrorReporting } from "../../../helpers/errorReporting"; import { LLMResponseJsonSchema } from "../../../protocol/jsonPatchProtocol"; -import type { - LooseLessonPlan} from "../../../protocol/schema"; -import { - LessonPlanJsonSchema -} from "../../../protocol/schema"; -import { findAmericanisms } from "../../../utils/language/findAmericanisms"; +import type { LooseLessonPlan } from "../../../protocol/schema"; +import { LessonPlanJsonSchema } from "../../../protocol/schema"; import { compressedLessonPlanForRag } from "../../../utils/lessonPlan/compressedLessonPlanForRag"; import { fetchLessonPlan } from "../../../utils/lessonPlan/fetchLessonPlan"; -import type { - RagLessonPlan} from "../../../utils/rag/fetchRagContent"; -import { - fetchRagContent -} from "../../../utils/rag/fetchRagContent"; +import type { RagLessonPlan } from "../../../utils/rag/fetchRagContent"; +import { fetchRagContent } from "../../../utils/rag/fetchRagContent"; import type { AilaServices } from "../../AilaServices"; import { AilaPromptBuilder } from "../AilaPromptBuilder"; @@ -117,7 +107,7 @@ export class AilaLessonPromptBuilder extends AilaPromptBuilder { summaries: "None", responseMode: this._aila?.options.mode ?? "interactive", useRag: this._aila?.options.useRag ?? true, - americanisms: findAmericanisms(lessonPlan), + americanisms: this._aila.americanisms.findAmericanisms(lessonPlan), baseLessonPlan: baseLessonPlan ? compressedLessonPlanForRag(baseLessonPlan) : undefined, diff --git a/packages/aila/src/core/types.ts b/packages/aila/src/core/types.ts index 118382537..a98b8fa2a 100644 --- a/packages/aila/src/core/types.ts +++ b/packages/aila/src/core/types.ts @@ -1,8 +1,11 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; +import type { AilaAmericanismsFeature } from "../features/americanisms"; +import type { AnalyticsAdapter } from "../features/analytics"; import type { AilaModerator } from "../features/moderation/moderators"; import type { OpenAILike } from "../features/moderation/moderators/OpenAiModerator"; import type { AilaPersistence } from "../features/persistence"; +import type { AilaRagFeature } from "../features/rag"; import type { AilaThreatDetector } from "../features/threatDetection"; import type { AilaAnalyticsFeature, @@ -12,6 +15,7 @@ import type { AilaThreatDetectionFeature, } from "../features/types"; import type { LooseLessonPlan } from "../protocol/schema"; +import type { AilaServices } from "./AilaServices"; import type { Message } from "./chat"; import type { LLMService } from "./llm/LLMService"; import type { AilaPlugin } from "./plugins/types"; @@ -73,5 +77,8 @@ export type AilaInitializationOptions = { chatCategoriser?: AilaCategorisationFeature; chatLlmService?: LLMService; moderationAiClient?: OpenAILike; + ragService?: (aila: AilaServices) => AilaRagFeature; + americanismsService?: (aila: AilaServices) => AilaAmericanismsFeature; + analyticsAdapters?: (aila: AilaServices) => AnalyticsAdapter[]; }; }; diff --git a/packages/aila/src/features/americanisms/AilaAmericanisms.ts b/packages/aila/src/features/americanisms/AilaAmericanisms.ts new file mode 100644 index 000000000..b73444075 --- /dev/null +++ b/packages/aila/src/features/americanisms/AilaAmericanisms.ts @@ -0,0 +1,69 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// +import { textify } from "@oakai/core/src/models/lessonPlans"; +import translator from "american-british-english-translator"; + +import type { AilaAmericanismsFeature } from "."; +import type { + AmericanismIssue, + AmericanismIssueBySection, +} from "../../features/americanisms"; +import type { LessonPlanKeys, LooseLessonPlan } from "../../protocol/schema"; + +export type TranslationResult = Record< + string, + Array<{ [phrase: string]: { issue: string; details: string } }> +>; + +export class AilaAmericanisms implements AilaAmericanismsFeature { + private findSectionAmericanisms( + section: LessonPlanKeys, + lessonPlan: LooseLessonPlan, + ): AmericanismIssueBySection | undefined { + const filterOutPhrases = new Set([ + "practice", + "practices", + "gas", + "gases", + "period", + "periods", + "fall", + "falls", + ]); + + const sectionContent = lessonPlan[section]; + if (!sectionContent) return; + + const sectionText = textify(sectionContent); + const sectionAmericanismScan: TranslationResult = translator.translate( + sectionText, + { american: true }, + ); + + const issues: AmericanismIssue[] = []; + Object.values(sectionAmericanismScan).forEach((lineIssues) => { + lineIssues.forEach((lineIssue) => { + const [phrase, issueDefinition] = Object.entries(lineIssue)[0] ?? []; + if (phrase && issueDefinition && !filterOutPhrases.has(phrase)) { + if (!issues.some((issue) => issue.phrase === phrase)) { + issues.push({ phrase, ...issueDefinition }); + } + } + }); + }); + + return { section, issues }; + } + + public findAmericanisms(lessonPlan: LooseLessonPlan) { + return Object.keys(lessonPlan).flatMap((section) => { + const sectionIssues = this.findSectionAmericanisms( + section as LessonPlanKeys, + lessonPlan, + ); + return sectionIssues && sectionIssues.issues.length > 0 + ? [sectionIssues] + : []; + }); + } +} diff --git a/packages/aila/src/features/americanisms/NullAilaAmericanisms.ts b/packages/aila/src/features/americanisms/NullAilaAmericanisms.ts new file mode 100644 index 000000000..7b5ce8e9a --- /dev/null +++ b/packages/aila/src/features/americanisms/NullAilaAmericanisms.ts @@ -0,0 +1,7 @@ +import type { AilaAmericanismsFeature } from "."; + +export class NullAilaAmericanisms implements AilaAmericanismsFeature { + public findAmericanisms() { + return []; + } +} diff --git a/packages/aila/src/utils/language/american-british-english-translator.d.ts b/packages/aila/src/features/americanisms/american-british-english-translator.d.ts similarity index 100% rename from packages/aila/src/utils/language/american-british-english-translator.d.ts rename to packages/aila/src/features/americanisms/american-british-english-translator.d.ts diff --git a/packages/aila/src/features/americanisms/index.ts b/packages/aila/src/features/americanisms/index.ts new file mode 100644 index 000000000..41004974f --- /dev/null +++ b/packages/aila/src/features/americanisms/index.ts @@ -0,0 +1,16 @@ +import type { LooseLessonPlan } from "../../protocol/schema"; + +export type AmericanismIssueBySection = { + section: string; + issues: AmericanismIssue[]; +}; + +export type AmericanismIssue = { + phrase: string; + issue?: string; + details?: string; +}; + +export interface AilaAmericanismsFeature { + findAmericanisms(lessonPlan: LooseLessonPlan): AmericanismIssueBySection[]; +} diff --git a/packages/aila/src/features/analytics/AilaAnalytics.ts b/packages/aila/src/features/analytics/AilaAnalytics.ts index 778813432..0a28776eb 100644 --- a/packages/aila/src/features/analytics/AilaAnalytics.ts +++ b/packages/aila/src/features/analytics/AilaAnalytics.ts @@ -1,4 +1,4 @@ -import type { AilaServices } from "../../core"; +import type { AilaServices } from "../../core/AilaServices"; import type { AnalyticsAdapter } from "./adapters/AnalyticsAdapter"; export class AilaAnalytics { diff --git a/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts b/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts index 6b8b677bd..c99612c6b 100644 --- a/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts +++ b/packages/aila/src/features/analytics/adapters/AnalyticsAdapter.ts @@ -1,4 +1,4 @@ -import type { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core/AilaServices"; export abstract class AnalyticsAdapter { protected _aila: AilaServices; diff --git a/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts b/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts index 57a632b20..4b0edb998 100644 --- a/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts +++ b/packages/aila/src/features/analytics/adapters/PosthogAnalyticsAdapter.ts @@ -2,7 +2,7 @@ import { getEncoding } from "js-tiktoken"; import { PostHog } from "posthog-node"; import invariant from "tiny-invariant"; -import type { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core/AilaServices"; import { reportCompletionAnalyticsEvent } from "../../../lib/openai/OpenAICompletionWithLogging"; import { AnalyticsAdapter } from "./AnalyticsAdapter"; diff --git a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts index 869bcc3ac..4ff724c72 100644 --- a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts +++ b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts @@ -1,33 +1,23 @@ -import { RAG } from "@oakai/core/src/rag"; -import { - type PrismaClientWithAccelerate, - prisma as globalPrisma, -} from "@oakai/db"; +import { CategoriseKeyStageAndSubjectResponse } from "@oakai/core/src/rag"; +import { keyStages, subjects } from "@oakai/core/src/utils/subjects"; +import { aiLogger } from "@oakai/logger"; +import type { ChatCompletionMessageParam } from "openai/resources"; +import { Md5 } from "ts-md5"; -import type { AilaServices, Message } from "../../../core"; +import { DEFAULT_CATEGORISE_MODEL } from "../../../constants"; +import type { AilaServices } from "../../../core/AilaServices"; +import type { Message } from "../../../core/chat"; +import type { OpenAICompletionWithLoggingOptions } from "../../../lib/openai/OpenAICompletionWithLogging"; +import { OpenAICompletionWithLogging } from "../../../lib/openai/OpenAICompletionWithLogging"; import type { LooseLessonPlan } from "../../../protocol/schema"; import type { AilaCategorisationFeature } from "../../types"; +const log = aiLogger("aila:categorisation"); + export class AilaCategorisation implements AilaCategorisationFeature { private _aila: AilaServices; - private _prisma: PrismaClientWithAccelerate; - private _chatId: string; - private _userId: string | undefined; - constructor({ - aila, - prisma, - chatId, - userId, - }: { - aila: AilaServices; - prisma?: PrismaClientWithAccelerate; - chatId: string; - userId?: string; - }) { + constructor({ aila }: { aila: AilaServices }) { this._aila = aila; - this._prisma = prisma ?? globalPrisma; - this._chatId = chatId; - this._userId = userId; } public async categorise( messages: Message[], @@ -39,25 +29,123 @@ export class AilaCategorisation implements AilaCategorisationFeature { .filter((i) => i) .join(" "); - const result = await this.fetchCategorisedInput( - categorisationInput, - this._prisma, - ); + const result = await this.fetchCategorisedInput(categorisationInput); return result; } + private async categoriseKeyStageAndSubject( + input: string, + chatMeta: OpenAICompletionWithLoggingOptions, + ) { + log.info("Categorise input", JSON.stringify(input)); + //# TODO Duplicated for now until we refactor the RAG class + const systemMessage = `You are a classifier which can help me categorise the intent of the user's input for an application which helps a teacher build a lesson plan their students in a UK school. +You accept a string as input and return an object with the keys keyStage, subject, title and topic. + +USER INPUT +The user will likely be starting to make a new lesson plan and this may be their first interaction with the system. So it's likely that the text will include introductory information, such as "Hello, I would like you to make a lesson about {title} for {key stage} students in {subject}." or "I need a lesson plan for {title} for {subject} students in {key stage}." The user may also include a topic for the lesson plan, such as "I need a lesson plan for {title} for {subject} students in {key stage} about {topic}." +The input will be highly variable, so you should be able to handle a wide range of inputs, and extract the relevant information from the input. + +KEY STAGE SLUGS +The following are the key stages you can use to categorise the input. Each of these is slug which we use in the database to refer to them: +${keyStages.join("\n")} + +SUBJECT SLUGS +The following are the subjects you can use to categorise the input. Each of these is a slug which we use in the database to refer to them: +${subjects.join("\n")} + +LESSON TITLES +The title of the lesson plan is the title of the lesson plan that the user wants to create. This could be anything, but it will likely be a short phrase or sentence that describes the topic of the lesson plan. +Do not include "Lesson about" or "…Lesson" in the title. The title should be the standalone main topic of the lesson plan and not mention the word Lesson. It will be used as the title of the lesson plan in our database and displayed to the user in an overview document. + +RETURNED OBJECT +The object you return should have the following shape: +{ + reasoning: string, // Why you have chosen to categorise the input in the way that you have + keyStage: string, // The slug of the key stage that the input is relevant to + subject: string, // The slug of the subject that the input is relevant to + title: string, // The title of the lesson plan + topic: string // The topic of the lesson plan +} + +GUESSING AN APPROPRIATE KEY STAGE, SUBJECT OR TOPIC WHEN NOT SPECIFIED +Where not specified by the user, you should attempt to come up with a reasonable title, key stage, subject and topic based on the input from the user. +For instance, "Plate tectonics" is obviously something covered in Geography and based on your knowledge of the UK education system I'm sure you know that this is often taught in Key Stage 2. +Imagine that you are a teacher who is trying to categorise the input. +You should use your knowledge of the UK education system to make an educated guess about the key stage and subject that the input is relevant to. + +EXAMPLE ALIASES + +Often, teachers will use shorthand to refer to a key stage or subject. For example, "KS3" is often used to refer to "Key Stage 3". You should be able to handle these aliases and return the correct slug for the key stage or subject. +The teacher might also say "Year 10". You should be able to handle this and return the correct slug for the key stage based on the teaching years that are part of the Key Stages in the UK National Curriculum. +For subjects, you should also be able to handle the plural form of the subject. For example, "Maths" should be categorised as "maths" and "Mathematics" should be categorised as "maths". +"PSHE" is often used to refer to "Personal, Social, Health and Economic education" and maps to "psed" in our database. +"PE" is often used to refer to "Physical Education". +"DT" is often used to refer to "Design and Technology". +"RSHE" is often used as a synonym for "PSHE" and "PSED" and maps to "rshe-pshe" in our database. +You should be able to handle any of these aliases and return the correct slug for the subject. +For computing we have both "GCSE" and "non-GCSE". By default, assume that the input is relevant to the GCSE computing curriculum. If the input is relevant to the non-GCSE computing curriculum, the user will specify this in the input. + +PROVIDING REASONING +When key stage, subject and topic are not provided by the user, it may be helpful to write out your reasoning for why you think the input relates to a particular key stage, subject or topic. Start with this reasoning in your response and write out why you think that the input is relevant to the key stage, subject and topic that you have chosen. This will help us to understand your thought process and improve the system in the future. + +BRITISH ENGLISH +The audience for your categorisation is teachers in the UK, so you should use British English when responding to the user. For example, use "Maths" instead of "Math" and "Key Stage 3" instead of "Grade 3". +If the user has provided a title or topic that is in American English, you should still respond with the British English equivalent. For example, if the user has provided the subject "Math" you should respond with "Maths". Or if the user has provided the title "Globalization" you should respond with "Globalisation". + +RESPONDING TO THE USER +All keys are optional but always prefer sending back a keyStage or subject if you are able to take a good guess at what would be appropriate values. If you are *REALLY* not able to determine the key stage or subject, you can return null for these values, but only do so as a last resort! If you are not able to determine the title or topic, you can return null for these values. +Always respond with a valid JSON document. If you are not able to respond for some reason, respond with another valid JSON document with the keys set to null and an "error" key with the value specifying the reason for the error. +Never respond with slugs that are not in the list of slugs provided above. If you are not able to categorise the input, return null for the key stage and subject. This is very important! We use the slugs to categorise the input in our database, so if you return a slug that is not in the list above, we will not be able to categorise the input correctly. +Do not respond with any other output than the object described above. If you do, the system will not be able to understand your response and will not be able to categorise the input correctly. +Thank you and happy classifying!`; + + const promptVersion = Md5.hashStr(systemMessage); + const messages: ChatCompletionMessageParam[] = [ + { role: "system", content: systemMessage }, + { role: "user", content: input }, + ]; + + // #TODO This is the only place where we use this old OpenAICompletionWithLogging + // We should be using methods on the Aila instance instead + const { completion } = await OpenAICompletionWithLogging( + { + ...chatMeta, + promptVersion, + prompt: "categorise_key_stage_and_subject", + }, + { + model: DEFAULT_CATEGORISE_MODEL, + stream: false, + messages, + response_format: { type: "json_object" }, + }, + ); + + try { + const content = completion.choices?.[0]?.message.content; + if (!content) return { error: "No content in response" }; + + const parsedResponse = CategoriseKeyStageAndSubjectResponse.parse( + JSON.parse(content), + ); + log.info("Categorisation results", parsedResponse); + return parsedResponse; + } catch (e) { + return { error: "Error parsing response" }; + } + } + private async fetchCategorisedInput( input: string, - prisma: PrismaClientWithAccelerate, ): Promise { - const rag = new RAG(prisma, { - chatId: this._chatId, - userId: this._userId, - }); - const parsedCategorisation = await rag.categoriseKeyStageAndSubject(input, { - chatId: this._chatId, - userId: this._userId, - }); + const parsedCategorisation = await this.categoriseKeyStageAndSubject( + input, + { + chatId: this._aila.chatId, + userId: this._aila.userId, + }, + ); const { keyStage, subject, title, topic } = parsedCategorisation; const plan: LooseLessonPlan = { keyStage: keyStage ?? undefined, diff --git a/packages/aila/src/features/rag/AilaRag.ts b/packages/aila/src/features/rag/AilaRag.ts index d2b0de9e8..f1ba55851 100644 --- a/packages/aila/src/features/rag/AilaRag.ts +++ b/packages/aila/src/features/rag/AilaRag.ts @@ -3,14 +3,15 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import type { AilaServices } from "../../core"; +import type { AilaRagFeature } from "."; +import type { AilaServices } from "../../core/AilaServices"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; import type { LooseLessonPlan } from "../../protocol/schema"; import { minifyLessonPlanForRelevantLessons } from "../../utils/lessonPlan/minifyLessonPlanForRelevantLessons"; const log = aiLogger("aila:rag"); -export class AilaRag { +export class AilaRag implements AilaRagFeature { private _aila: AilaServices; private _rag: RAG; private _prisma: PrismaClientWithAccelerate; diff --git a/packages/aila/src/features/rag/NullAilaRag.ts b/packages/aila/src/features/rag/NullAilaRag.ts new file mode 100644 index 000000000..8d8606ea5 --- /dev/null +++ b/packages/aila/src/features/rag/NullAilaRag.ts @@ -0,0 +1,7 @@ +import type { AilaRagFeature } from "."; + +export class NullAilaRag implements AilaRagFeature { + public async fetchRagContent() { + return ""; + } +} diff --git a/packages/aila/src/features/rag/index.ts b/packages/aila/src/features/rag/index.ts index 0f6542d26..71cffadc6 100644 --- a/packages/aila/src/features/rag/index.ts +++ b/packages/aila/src/features/rag/index.ts @@ -1 +1,8 @@ -export { AilaRag } from "./AilaRag"; +import type { LooseLessonPlan } from "../../protocol/schema"; + +export interface AilaRagFeature { + fetchRagContent(params: { + numberOfLessonPlansInRag?: number; + lessonPlan?: LooseLessonPlan; + }): Promise; +} diff --git a/packages/aila/src/utils/language/findAmericanisms.ts b/packages/aila/src/utils/language/findAmericanisms.ts deleted file mode 100644 index 78694616b..000000000 --- a/packages/aila/src/utils/language/findAmericanisms.ts +++ /dev/null @@ -1,75 +0,0 @@ -// eslint-disable-next-line @typescript-eslint/triple-slash-reference -/// -import { textify } from "@oakai/core/src/models/lessonPlans"; -import translator from "american-british-english-translator"; - -import type { LessonPlanKeys, LooseLessonPlan } from "../../protocol/schema"; - -export type AmericanismIssueBySection = { - section: string; - issues: AmericanismIssue[]; -}; - -export type AmericanismIssue = { - phrase: string; - issue?: string; - details?: string; -}; - -export type TranslationResult = Record< - string, - Array<{ [phrase: string]: { issue: string; details: string } }> ->; - -const filterOutPhrases = new Set([ - "practice", - "practices", - "gas", - "gases", - "period", - "periods", - "fall", - "falls", -]); - -export function findSectionAmericanisms( - section: LessonPlanKeys, - lessonPlan: LooseLessonPlan, -): AmericanismIssueBySection | undefined { - const sectionContent = lessonPlan[section]; - if (!sectionContent) return; - - const sectionText = textify(sectionContent); - const sectionAmericanismScan: TranslationResult = translator.translate( - sectionText, - { american: true }, - ); - - const issues: AmericanismIssue[] = []; - Object.values(sectionAmericanismScan).forEach((lineIssues) => { - lineIssues.forEach((lineIssue) => { - const [phrase, issueDefinition] = Object.entries(lineIssue)[0] ?? []; - if (phrase && issueDefinition && !filterOutPhrases.has(phrase)) { - if (!issues.some((issue) => issue.phrase === phrase)) { - issues.push({ phrase, ...issueDefinition }); - } - } - }); - }); - - return { section, issues }; -} - -export function findAmericanisms( - lessonPlan: LooseLessonPlan, -): AmericanismIssueBySection[] { - return Object.keys(lessonPlan).flatMap((section) => { - const sectionIssues = findSectionAmericanisms( - section as LessonPlanKeys, - lessonPlan, - ); - return sectionIssues && sectionIssues.issues.length > 0 - ? [sectionIssues] - : []; - }); -} diff --git a/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts b/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts index 11f44d50c..6496d98ca 100644 --- a/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts +++ b/packages/aila/src/utils/lessonPlan/fetchLessonPlanContentById.ts @@ -1,11 +1,8 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; -import type { - LooseLessonPlan} from "../../protocol/schema"; -import { - LessonPlanSchemaWhilstStreaming -} from "../../protocol/schema"; +import type { LooseLessonPlan } from "../../protocol/schema"; +import { LessonPlanSchemaWhilstStreaming } from "../../protocol/schema"; export async function fetchLessonPlanContentById( id: string, diff --git a/packages/aila/src/utils/moderation/moderationErrorHandling.ts b/packages/aila/src/utils/moderation/moderationErrorHandling.ts index 2a8428c89..67feb2307 100644 --- a/packages/aila/src/utils/moderation/moderationErrorHandling.ts +++ b/packages/aila/src/utils/moderation/moderationErrorHandling.ts @@ -1,4 +1,4 @@ -import { SafetyViolations as defaultSafetyViolations } from "@oakai/core"; +import { SafetyViolations as defaultSafetyViolations } from "@oakai/core/src/models/safetyViolations"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; import type { PrismaClientWithAccelerate } from "@oakai/db"; diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js index 51e3a4511..76d707d25 100644 --- a/packages/eslint-config-custom/index.js +++ b/packages/eslint-config-custom/index.js @@ -29,7 +29,7 @@ module.exports = { "no-inner-declarations": "warn", "@typescript-eslint/no-unsafe-enum-comparison": "warn", "@typescript-eslint/no-unnecessary-type-assertion": "warn", - "@typescript-eslint/consistent-type-imports": "error", + "@typescript-eslint/consistent-type-imports": "warn", "@typescript-eslint/comma-dangle": "off", "@typescript-eslint/no-unused-vars": "warn", "@typescript-eslint/require-await": "warn", diff --git a/packages/logger/index.ts b/packages/logger/index.ts index d0eadd815..0b7b57563 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -16,6 +16,7 @@ type ChildKey = | "admin" | "aila" | "aila:analytics" + | "aila:categorisation" | "aila:errors" | "aila:lesson" | "aila:llm" From 8553fb09a70709aee9e8e66ff16d1d3ecd093a21 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Tue, 29 Oct 2024 14:00:59 +0100 Subject: [PATCH 042/127] chore: fix eslint import types on nextjs project (#288) --- .../ai-apps/common/parseLocalStorageData.ts | 2 +- .../src/ai-apps/common/state/create-parts.ts | 4 ++-- .../src/ai-apps/common/state/helpers.ts | 2 +- .../ai-apps/lesson-planner/state/actions.ts | 10 +++++----- .../src/ai-apps/quiz-designer/convertToCSV.ts | 2 +- .../quiz-designer/convertToGIFTFormat.ts | 2 +- .../ai-apps/quiz-designer/export-helpers.ts | 4 ++-- .../quiz-designer/extraQuizPromptInfo.ts | 2 +- .../quiz-designer/quizRequestGeneration.ts | 4 ++-- .../ai-apps/quiz-designer/state/actions.ts | 8 ++++---- .../ai-apps/quiz-designer/state/reducer.ts | 13 +++++------- apps/nextjs/src/app/actions.ts | 6 ++++-- .../app/aila/[id]/download/DownloadView.tsx | 2 +- .../app/aila/[id]/download/useDownloadView.ts | 4 ++-- apps/nextjs/src/app/aila/[id]/share/index.tsx | 4 ++-- apps/nextjs/src/app/aila/[id]/share/page.tsx | 5 +++-- .../src/app/api/aila-download-all/route.ts | 3 ++- .../nextjs/src/app/api/aila-download/route.ts | 3 ++- apps/nextjs/src/app/api/chat/chatHandler.ts | 17 ++++++++-------- apps/nextjs/src/app/api/chat/config.ts | 3 ++- .../src/app/api/chat/errorHandling.test.ts | 4 ++-- apps/nextjs/src/app/api/chat/errorHandling.ts | 6 +++--- .../chat/fixtures/FixtureRecordLLMService.ts | 6 +++--- .../fixtures/FixtureRecordOpenAiClient.ts | 6 +++--- .../fixtures/FixtureReplayOpenAiClient.ts | 4 ++-- apps/nextjs/src/app/api/chat/protocol.ts | 2 +- apps/nextjs/src/app/api/chat/route.test.ts | 5 +++-- apps/nextjs/src/app/api/chat/route.ts | 5 +++-- .../src/app/api/chat/webActionsPlugin.test.ts | 6 +++--- .../src/app/api/chat/webActionsPlugin.ts | 4 ++-- apps/nextjs/src/app/api/qd-download/route.ts | 3 ++- .../src/app/api/trpc/chat/[trpc]/route.ts | 2 +- .../src/app/api/trpc/main/[trpc]/route.ts | 2 +- .../app/api/trpc/test-support/[trpc]/route.ts | 2 +- apps/nextjs/src/app/home-page.tsx | 6 +++--- apps/nextjs/src/app/legal/[slug]/legal.tsx | 2 +- apps/nextjs/src/app/prompts/prompts.tsx | 2 +- .../app/quiz-designer/quiz-designer-page.tsx | 3 ++- .../nextjs/src/app/user/[[...index]]/page.tsx | 2 +- apps/nextjs/src/cms/data/fetchAiHomepage.ts | 2 +- .../src/cms/data/fetchPolicyDocument.ts | 2 +- .../Chat/Chat/hooks/useAilaStreamingStatus.ts | 2 +- .../Chat/hooks/useProgressForDownloads.ts | 4 ++-- .../Chat/chat-left-hand-side.tsx | 4 ++-- .../Chat/chat-lessonPlanDisplay.tsx | 2 +- .../Chat/chat-lessonPlanMapToMarkDown.tsx | 4 ++-- .../AppComponents/Chat/chat-list.tsx | 20 +++++++------------ .../AppComponents/Chat/chat-message/index.tsx | 15 +++++++------- .../Chat/chat-message/protocol.ts | 2 +- .../AppComponents/Chat/chat-quick-buttons.tsx | 2 +- .../Chat/chat-right-hand-side-lesson.tsx | 6 +++--- .../AppComponents/Chat/chat-share-dialog.tsx | 2 +- .../Chat/drop-down-section/flag-button.tsx | 3 ++- .../Chat/drop-down-section/modify-button.tsx | 3 ++- .../LessonPlanProgressDropdown.stories.tsx | 2 +- .../LessonPlanProgressDropdown.tsx | 2 +- .../AppComponents/Chat/guidance-required.tsx | 2 +- .../AppComponents/Chat/markdown.tsx | 6 ++++-- .../AppComponents/Chat/prompt-form.tsx | 4 ++-- .../AppComponents/Chat/sidebar-actions.tsx | 2 +- .../AppComponents/Chat/sidebar-item.tsx | 2 +- .../AppComponents/Chat/sidebar-items.tsx | 2 +- .../AppComponents/Chat/ui/button.tsx | 9 +++++---- .../AppComponents/Chat/ui/chat-button.tsx | 3 ++- .../AppComponents/Chat/ui/codeblock.tsx | 3 ++- .../ComparativeJudgement/JudgementContent.tsx | 5 +++-- .../KeyStageAndSubjectPicker.tsx | 6 +++--- .../ComparativeJudgement/PreviewContent.tsx | 4 ++-- .../ComparativeJudgement/QuestionButton.tsx | 4 ++-- .../ComparativeJudgement/index.tsx | 2 +- .../AppComponents/DialogContext/index.tsx | 2 +- .../FeedbackForms/ModerationFeedbackForm.tsx | 2 +- .../FeedbackForms/ModerationFeedbackModal.tsx | 6 ++---- .../QuizDesigner/DownloadGiftButton.tsx | 2 +- .../AppComponents/QuizDesigner/ErrorBox.tsx | 3 ++- .../AppComponents/QuizDesigner/ExportMenu.tsx | 2 +- .../QuizDesigner/GenerateAllButton.tsx | 2 +- .../AppComponents/QuizDesigner/Hero.tsx | 14 ++++++------- .../QuizDesigner/QuizContent.tsx | 8 +++++--- .../QuizDesigner/QuizDesignerPageContent.tsx | 11 +++++----- .../QuizDesigner/QuizQuestionRow/Answer.tsx | 11 +++++----- .../QuizDesigner/QuizQuestionRow/Answers.tsx | 7 ++++--- .../QuizQuestionRow/ControllerRow.tsx | 9 ++++----- .../QuizQuestionRow/Distractor.tsx | 14 ++++++------- .../QuizQuestionRow/Distractors.tsx | 6 +++--- .../QuizDesigner/QuizQuestionRow/Question.tsx | 13 ++++++------ .../QuizQuestionRow/RegenButtonGroup.tsx | 6 ++---- .../QuizDesigner/QuizQuestionRow/index.tsx | 10 ++++------ .../QuizDesigner/QuizRestoreDialog.tsx | 11 +++++----- .../SubjectKeyStageSection/Choose.tsx | 2 +- .../SubjectKeyStageSection/Confirmed.tsx | 2 +- .../SubjectKeyStageSection/index.tsx | 2 +- .../QuizDesigner/SuggestedQuestionCard.tsx | 11 +++++----- .../QuizDesigner/SuggestedQuestions.tsx | 8 ++++---- .../common/RateLimitNotification.tsx | 2 +- .../GenerationFeedbackDialog.tsx | 2 +- .../GenerationInputAndText.tsx | 2 +- .../SingleGeneration/GenerationWrapper.tsx | 2 +- .../AppComponents/common/SuggestedLessons.tsx | 4 ++-- .../download/DownloadAllButton.tsx | 2 +- .../AppComponents/download/DownloadButton.tsx | 8 +++----- .../SectionsNotCompleteDownloadNotice.tsx | 2 +- .../Border/BoxBorder/BoxBorderBottom.tsx | 2 +- .../Border/BoxBorder/BoxBorderLeft.tsx | 2 +- .../Border/BoxBorder/BoxBorderRight.tsx | 2 +- .../Border/BoxBorder/BoxBorderTop.tsx | 2 +- apps/nextjs/src/components/Button.tsx | 3 ++- apps/nextjs/src/components/ButtonCore.tsx | 3 ++- apps/nextjs/src/components/CheckBox.tsx | 2 +- .../ChatModerationContext.tsx | 3 ++- .../ContextProviders/ChatProvider.tsx | 17 ++++++++-------- .../CookieConsentProvider.tsx | 3 ++- .../ContextProviders/FontProvider.tsx | 9 ++------- .../ContextProviders/GleapProvider.tsx | 3 ++- .../ContentOptions/ReportContentDialog.tsx | 2 +- .../ContentOptions/ShareChatDialog.tsx | 4 ++-- .../DialogControl/DialogContents.tsx | 4 ++-- .../ExportsDialogs/exports.helpers.ts | 2 +- .../ExportsDialogs/exports.types.ts | 2 +- .../useExportAdditionalMaterials.ts | 6 +++--- .../useExportAllLessonAssets.ts | 10 ++++------ .../ExportsDialogs/useExportLessonPlanDoc.ts | 6 +++--- .../ExportsDialogs/useExportLessonSlides.ts | 6 +++--- .../useExportQuizDesignerSlides.ts | 8 +++----- .../ExportsDialogs/useExportQuizDoc.ts | 6 +++--- .../useExportWorksheetSlides.ts | 6 +++--- apps/nextjs/src/components/Feedback/index.tsx | 2 +- apps/nextjs/src/components/Footer.tsx | 2 +- .../nextjs/src/components/FullPageWarning.tsx | 3 ++- apps/nextjs/src/components/Icon/svgs.tsx | 2 +- .../components/ImageAltGenerator/ImageRow.tsx | 2 +- apps/nextjs/src/components/Input.tsx | 2 +- apps/nextjs/src/components/LoadingWheel.tsx | 3 ++- .../MainNavigation/MainNavigationMenu.tsx | 5 +++-- .../MainNavigation/MobileListItem.tsx | 2 +- .../src/components/MainNavigation/index.tsx | 3 ++- .../PortableText/portableTextComponents.tsx | 2 +- .../SubjectAndKeyStageSelect/index.tsx | 6 +++--- apps/nextjs/src/data/menus.tsx | 2 +- .../surveys/useModerationFeedbackSurvey.ts | 2 +- .../hooks/surveys/usePosthogFeedbackSurvey.ts | 2 +- apps/nextjs/src/hooks/useDemoLocking.ts | 4 ++-- apps/nextjs/src/hooks/useGeneration.ts | 9 +++++---- .../src/hooks/useGenerationCallbacks.ts | 6 ++++-- .../hooks/useMobileLessonPullOutControl.ts | 6 +++--- apps/nextjs/src/hooks/useQuizSession.ts | 12 +++++------ apps/nextjs/src/hooks/useRegenerateAnswers.ts | 11 +++++----- .../src/hooks/useRegenerateDistractors.ts | 11 +++++----- .../nextjs/src/hooks/useSuggestedQuestions.ts | 9 ++++----- ...seTemporaryLessonPlanWithStreamingEdits.ts | 4 ++-- apps/nextjs/src/lib/analytics/helpers.ts | 8 ++++---- .../lib/analytics/hubspot/HubspotClient.ts | 2 +- .../lib/analytics/hubspot/HubspotLoader.tsx | 5 +++-- .../analytics/lessonPlanTrackingContext.tsx | 8 ++++---- .../lib/analytics/trackLessonPlanRefined.ts | 9 +++++---- .../src/lib/analytics/useAnalyticsService.ts | 6 +++--- apps/nextjs/src/lib/avo/getAvoBridge.ts | 6 +++--- .../src/lib/lessonPlan/sectionsInOrder.ts | 2 +- apps/nextjs/src/lib/posthog/posthog.ts | 4 ++-- apps/nextjs/src/lib/sentry/sentrySetUser.ts | 2 +- apps/nextjs/src/lib/sentry/withSentry.ts | 2 +- apps/nextjs/src/middleware.test.ts | 11 ++++------ apps/nextjs/src/middleware.ts | 13 +++++------- .../nextjs/src/middlewares/auth.middleware.ts | 10 ++++------ apps/nextjs/src/middlewares/csp.test.ts | 3 ++- apps/nextjs/src/middlewares/csp.ts | 5 +++-- .../src/middlewares/middlewareErrorLogging.ts | 2 +- apps/nextjs/src/mocks/analytics/provider.tsx | 2 +- apps/nextjs/src/utils/alphabetiseArray.ts | 2 +- apps/nextjs/src/utils/scrollToRef.ts | 2 +- apps/nextjs/src/utils/trackDownload.ts | 6 +++--- apps/nextjs/src/utils/trpc.tsx | 5 +++-- 172 files changed, 413 insertions(+), 418 deletions(-) diff --git a/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts b/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts index 5bd6ce109..63a57753f 100644 --- a/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts +++ b/apps/nextjs/src/ai-apps/common/parseLocalStorageData.ts @@ -1,6 +1,6 @@ import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; -import { z } from "zod"; +import type { z } from "zod"; const log = aiLogger("ui"); diff --git a/apps/nextjs/src/ai-apps/common/state/create-parts.ts b/apps/nextjs/src/ai-apps/common/state/create-parts.ts index ada374416..ce6f27dd5 100644 --- a/apps/nextjs/src/ai-apps/common/state/create-parts.ts +++ b/apps/nextjs/src/ai-apps/common/state/create-parts.ts @@ -1,9 +1,9 @@ -import { +import type { GenerationPart, GenerationPartAIGenerated, - GenerationPartType, GenerationPartUserTweaked, } from "@oakai/core/src/types"; +import { GenerationPartType } from "@oakai/core/src/types"; export function createAIGeneratedPart( value: Value, diff --git a/apps/nextjs/src/ai-apps/common/state/helpers.ts b/apps/nextjs/src/ai-apps/common/state/helpers.ts index 33cea956f..d62726193 100644 --- a/apps/nextjs/src/ai-apps/common/state/helpers.ts +++ b/apps/nextjs/src/ai-apps/common/state/helpers.ts @@ -1,4 +1,4 @@ -import { GenerationPart } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; /** * Apply the `updater` function to the item diff --git a/apps/nextjs/src/ai-apps/lesson-planner/state/actions.ts b/apps/nextjs/src/ai-apps/lesson-planner/state/actions.ts index d237bb22e..16ef00a41 100644 --- a/apps/nextjs/src/ai-apps/lesson-planner/state/actions.ts +++ b/apps/nextjs/src/ai-apps/lesson-planner/state/actions.ts @@ -1,13 +1,13 @@ -import { RateLimitInfo } from "@oakai/api/src/types"; -import { KeyStageName, SubjectName } from "@oakai/core"; -import { +import type { RateLimitInfo } from "@oakai/api/src/types"; +import type { KeyStageName, SubjectName } from "@oakai/core"; +import type { QuizAppQuestion, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; -import { DeepPartial } from "@/utils/types/DeepPartial"; +import type { DeepPartial } from "@/utils/types/DeepPartial"; -import { +import type { LPKeyLearningPoint, LPKeyword, LPMisconception, diff --git a/apps/nextjs/src/ai-apps/quiz-designer/convertToCSV.ts b/apps/nextjs/src/ai-apps/quiz-designer/convertToCSV.ts index f4e45054e..6dec1fc91 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/convertToCSV.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/convertToCSV.ts @@ -1,4 +1,4 @@ -import { ExportableQuizAppState } from "@oakai/exports/src/schema/input.schema"; +import type { ExportableQuizAppState } from "@oakai/exports/src/schema/input.schema"; export function convertQuizToCSV(data: ExportableQuizAppState) { // Define the headers for your CSV diff --git a/apps/nextjs/src/ai-apps/quiz-designer/convertToGIFTFormat.ts b/apps/nextjs/src/ai-apps/quiz-designer/convertToGIFTFormat.ts index 900419109..d77ec7350 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/convertToGIFTFormat.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/convertToGIFTFormat.ts @@ -1,4 +1,4 @@ -import { +import type { ExportableQuizAppState, ExportableQuizQuestion, } from "@oakai/exports/src/schema/input.schema"; diff --git a/apps/nextjs/src/ai-apps/quiz-designer/export-helpers.ts b/apps/nextjs/src/ai-apps/quiz-designer/export-helpers.ts index 44ecd6ce9..e4a90e80b 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/export-helpers.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/export-helpers.ts @@ -1,4 +1,4 @@ -import { +import type { ExportableQuizAppState, ExportableQuizQuestion, } from "@oakai/exports/src/schema/input.schema"; @@ -6,7 +6,7 @@ import { getGenerationPartValue } from "ai-apps/common/state/helpers"; import { sortAlphabetically } from "@/utils/alphabetiseArray"; -import { +import type { QuizAppQuestion, QuizAppState, QuizAppStateQuestion, diff --git a/apps/nextjs/src/ai-apps/quiz-designer/extraQuizPromptInfo.ts b/apps/nextjs/src/ai-apps/quiz-designer/extraQuizPromptInfo.ts index 22b579306..d90b7a39b 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/extraQuizPromptInfo.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/extraQuizPromptInfo.ts @@ -1,4 +1,4 @@ -import { QuizAppState, QuizAppStateQuestion } from "./state/types"; +import type { QuizAppState, QuizAppStateQuestion } from "./state/types"; type OtherQuestionForPromptPropos = { state: QuizAppState; diff --git a/apps/nextjs/src/ai-apps/quiz-designer/quizRequestGeneration.ts b/apps/nextjs/src/ai-apps/quiz-designer/quizRequestGeneration.ts index a270b3942..5efddcb15 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/quizRequestGeneration.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/quizRequestGeneration.ts @@ -1,9 +1,9 @@ -import { GenerationPart } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; import { getAgesFromKeyStage } from "@/utils/getAgesFromKeyStage"; import { extraQuizPromptInfo } from "./extraQuizPromptInfo"; -import { QuizAppState, QuizAppStateQuestion } from "./state/types"; +import type { QuizAppState, QuizAppStateQuestion } from "./state/types"; type RequestionGenerationInputs = { lastGenerationId: string | null; diff --git a/apps/nextjs/src/ai-apps/quiz-designer/state/actions.ts b/apps/nextjs/src/ai-apps/quiz-designer/state/actions.ts index a5a55c020..9641f803e 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/state/actions.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/state/actions.ts @@ -1,8 +1,8 @@ -import { RateLimitInfo } from "@oakai/api/src/types"; -import { KeyStageName, SubjectName } from "@oakai/core"; -import { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; +import type { RateLimitInfo } from "@oakai/api/src/types"; +import type { KeyStageName, SubjectName } from "@oakai/core"; +import type { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; -import { QuizAppState, QuizQuestionType } from "./types"; +import type { QuizAppState, QuizQuestionType } from "./types"; /** * Our action types are declared as a const enum with string values diff --git a/apps/nextjs/src/ai-apps/quiz-designer/state/reducer.ts b/apps/nextjs/src/ai-apps/quiz-designer/state/reducer.ts index 0b2719325..82ca64f85 100644 --- a/apps/nextjs/src/ai-apps/quiz-designer/state/reducer.ts +++ b/apps/nextjs/src/ai-apps/quiz-designer/state/reducer.ts @@ -5,15 +5,12 @@ import { createTweakPart, } from "ai-apps/common/state/create-parts"; import { removeAtIndex, updateAtIndex } from "ai-apps/common/state/helpers"; -import { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; +import type { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; -import { QuizAppAction, QuizAppActions } from "./actions"; -import { - QuizAppState, - QuizAppStateQuestion, - QuizAppStatus, - QuizQuestionType, -} from "./types"; +import type { QuizAppAction } from "./actions"; +import { QuizAppActions } from "./actions"; +import type { QuizAppState, QuizAppStateQuestion } from "./types"; +import { QuizAppStatus, QuizQuestionType } from "./types"; export function quizAppReducer( state: QuizAppState, diff --git a/apps/nextjs/src/app/actions.ts b/apps/nextjs/src/app/actions.ts index d515d3330..6bd363009 100644 --- a/apps/nextjs/src/app/actions.ts +++ b/apps/nextjs/src/app/actions.ts @@ -1,7 +1,9 @@ "use server"; -import { AilaPersistedChat, chatSchema } from "@oakai/aila/src/protocol/schema"; -import { Prisma, prisma } from "@oakai/db"; +import type { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; +import { chatSchema } from "@oakai/aila/src/protocol/schema"; +import type { Prisma } from "@oakai/db"; +import { prisma } from "@oakai/db"; import * as Sentry from "@sentry/nextjs"; function parseChatAndReportError({ diff --git a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx index 30c02929f..a6fde2f92 100644 --- a/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx +++ b/apps/nextjs/src/app/aila/[id]/download/DownloadView.tsx @@ -1,6 +1,6 @@ "use client"; -import { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; +import type { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; import { Box, Flex, Grid } from "@radix-ui/themes"; import Layout from "@/components/AppComponents/Layout"; diff --git a/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts b/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts index cd0eef6ad..ab80452e2 100644 --- a/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts +++ b/apps/nextjs/src/app/aila/[id]/download/useDownloadView.ts @@ -1,8 +1,8 @@ import { getLastAssistantMessage } from "@oakai/aila/src/helpers/chat/getLastAssistantMessage"; -import { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; +import type { AilaPersistedChat } from "@oakai/aila/src/protocol/schema"; import { useProgressForDownloads } from "@/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads"; -import { ExportsHookProps } from "@/components/ExportsDialogs/exports.types"; +import type { ExportsHookProps } from "@/components/ExportsDialogs/exports.types"; import { useExportAdditionalMaterials } from "@/components/ExportsDialogs/useExportAdditionalMaterials"; import { useExportAllLessonAssets } from "@/components/ExportsDialogs/useExportAllLessonAssets"; import { useExportLessonPlanDoc } from "@/components/ExportsDialogs/useExportLessonPlanDoc"; diff --git a/apps/nextjs/src/app/aila/[id]/share/index.tsx b/apps/nextjs/src/app/aila/[id]/share/index.tsx index 833238906..e199c6023 100644 --- a/apps/nextjs/src/app/aila/[id]/share/index.tsx +++ b/apps/nextjs/src/app/aila/[id]/share/index.tsx @@ -2,8 +2,8 @@ import { useEffect, useState } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { OakSmallPrimaryButton } from "@oaknational/oak-components"; import Link from "next/link"; diff --git a/apps/nextjs/src/app/aila/[id]/share/page.tsx b/apps/nextjs/src/app/aila/[id]/share/page.tsx index be64ec031..bb9fb86b1 100644 --- a/apps/nextjs/src/app/aila/[id]/share/page.tsx +++ b/apps/nextjs/src/app/aila/[id]/share/page.tsx @@ -1,8 +1,9 @@ -import { User, clerkClient } from "@clerk/nextjs/server"; +import type { User } from "@clerk/nextjs/server"; +import { clerkClient } from "@clerk/nextjs/server"; import { getSessionModerations } from "@oakai/aila/src/features/moderation/getSessionModerations"; import { demoUsers } from "@oakai/core"; import { isToxic } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { type Metadata } from "next"; import { notFound } from "next/navigation"; diff --git a/apps/nextjs/src/app/api/aila-download-all/route.ts b/apps/nextjs/src/app/api/aila-download-all/route.ts index 169dc444b..7df412f2e 100644 --- a/apps/nextjs/src/app/api/aila-download-all/route.ts +++ b/apps/nextjs/src/app/api/aila-download-all/route.ts @@ -1,5 +1,6 @@ import { auth } from "@clerk/nextjs/server"; -import { LessonExportType, prisma } from "@oakai/db"; +import type { LessonExportType } from "@oakai/db"; +import { prisma } from "@oakai/db"; import { downloadDriveFile } from "@oakai/exports"; import * as Sentry from "@sentry/node"; import { kv } from "@vercel/kv"; diff --git a/apps/nextjs/src/app/api/aila-download/route.ts b/apps/nextjs/src/app/api/aila-download/route.ts index ada790560..52d4cf5b3 100644 --- a/apps/nextjs/src/app/api/aila-download/route.ts +++ b/apps/nextjs/src/app/api/aila-download/route.ts @@ -1,5 +1,6 @@ import { auth } from "@clerk/nextjs/server"; -import { LessonExportType, prisma } from "@oakai/db"; +import type { LessonExportType } from "@oakai/db"; +import { prisma } from "@oakai/db"; import { downloadDriveFile } from "@oakai/exports"; import * as Sentry from "@sentry/node"; diff --git a/apps/nextjs/src/app/api/chat/chatHandler.ts b/apps/nextjs/src/app/api/chat/chatHandler.ts index 907246e72..6e5218234 100644 --- a/apps/nextjs/src/app/api/chat/chatHandler.ts +++ b/apps/nextjs/src/app/api/chat/chatHandler.ts @@ -1,4 +1,4 @@ -import { Aila } from "@oakai/aila"; +import type { Aila } from "@oakai/aila"; import type { AilaInitializationOptions, AilaOptions, @@ -12,21 +12,20 @@ import { PosthogAnalyticsAdapter, } from "@oakai/aila/src/features/analytics"; import { AilaRag } from "@oakai/aila/src/features/rag/AilaRag"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; -import { - TracingSpan, - withTelemetry, -} from "@oakai/core/src/tracing/serverTracing"; -import { PrismaClientWithAccelerate, prisma as globalPrisma } from "@oakai/db"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; +import { withTelemetry } from "@oakai/core/src/tracing/serverTracing"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; // #TODO StreamingTextResponse is deprecated. If we choose to adopt the "ai" package // more fully, we should refactor to support its approach to streaming // but this could be a significant change given we have our record-separator approach import { StreamingTextResponse } from "ai"; -import { NextRequest } from "next/server"; +import type { NextRequest } from "next/server"; import invariant from "tiny-invariant"; -import { Config } from "./config"; +import type { Config } from "./config"; import { handleChatException } from "./errorHandling"; import { getFixtureLLMService, diff --git a/apps/nextjs/src/app/api/chat/config.ts b/apps/nextjs/src/app/api/chat/config.ts index 5c9b91f3f..645e827a0 100644 --- a/apps/nextjs/src/app/api/chat/config.ts +++ b/apps/nextjs/src/app/api/chat/config.ts @@ -1,4 +1,5 @@ -import { Aila, AilaInitializationOptions } from "@oakai/aila"; +import type { AilaInitializationOptions } from "@oakai/aila"; +import { Aila } from "@oakai/aila"; import { prisma as globalPrisma, type PrismaClientWithAccelerate, diff --git a/apps/nextjs/src/app/api/chat/errorHandling.test.ts b/apps/nextjs/src/app/api/chat/errorHandling.test.ts index 573c8550c..0a454dc8d 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.test.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.test.ts @@ -1,9 +1,9 @@ import { AilaAuthenticationError, AilaThreatDetectionError } from "@oakai/aila"; import * as moderationErrorHandling from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; -import { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; +import type { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import invariant from "tiny-invariant"; import { diff --git a/apps/nextjs/src/app/api/chat/errorHandling.ts b/apps/nextjs/src/app/api/chat/errorHandling.ts index be22e99d6..3bdb68096 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.ts @@ -1,13 +1,13 @@ import { AilaAuthenticationError, AilaThreatDetectionError } from "@oakai/aila"; -import { +import type { ActionDocument, ErrorDocument, } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; -import { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; +import type { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { streamingJSON } from "./protocol"; diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts index d4d9f5b8e..fa68531f1 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordLLMService.ts @@ -1,9 +1,9 @@ -import { Message } from "@oakai/aila"; -import { LLMService } from "@oakai/aila/src/core/llm/LLMService"; +import type { Message } from "@oakai/aila"; +import type { LLMService } from "@oakai/aila/src/core/llm/LLMService"; import { OpenAIService } from "@oakai/aila/src/core/llm/OpenAIService"; import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; -import { ZodSchema } from "zod"; +import type { ZodSchema } from "zod"; const log = aiLogger("fixtures"); diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts index b7b0e0b31..cb0d5d686 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureRecordOpenAiClient.ts @@ -1,9 +1,9 @@ -import { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; +import type { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; import { createOpenAIClient } from "@oakai/core/src/llm/openai"; import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; -import OpenAI from "openai"; -import { ChatCompletionCreateParamsNonStreaming } from "openai/resources"; +import type OpenAI from "openai"; +import type { ChatCompletionCreateParamsNonStreaming } from "openai/resources"; const log = aiLogger("fixtures"); diff --git a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts index 895a9ea33..bd7248313 100644 --- a/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts +++ b/apps/nextjs/src/app/api/chat/fixtures/FixtureReplayOpenAiClient.ts @@ -1,7 +1,7 @@ -import { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; +import type { OpenAILike } from "@oakai/aila/src/features/moderation/moderators/OpenAiModerator"; import { aiLogger } from "@oakai/logger"; import fs from "fs/promises"; -import OpenAI from "openai"; +import type OpenAI from "openai"; const log = aiLogger("fixtures"); diff --git a/apps/nextjs/src/app/api/chat/protocol.ts b/apps/nextjs/src/app/api/chat/protocol.ts index 322d43d6a..fc4fb0cb5 100644 --- a/apps/nextjs/src/app/api/chat/protocol.ts +++ b/apps/nextjs/src/app/api/chat/protocol.ts @@ -1,4 +1,4 @@ -import { +import type { ActionDocument, ErrorDocument, } from "@oakai/aila/src/protocol/jsonPatchProtocol"; diff --git a/apps/nextjs/src/app/api/chat/route.test.ts b/apps/nextjs/src/app/api/chat/route.test.ts index 7adfde59b..20d488d9f 100644 --- a/apps/nextjs/src/app/api/chat/route.test.ts +++ b/apps/nextjs/src/app/api/chat/route.test.ts @@ -1,4 +1,5 @@ -import { Aila, AilaInitializationOptions } from "@oakai/aila"; +import type { AilaInitializationOptions } from "@oakai/aila"; +import { Aila } from "@oakai/aila"; import { MockLLMService } from "@oakai/aila/src/core/llm/MockLLMService"; import { MockCategoriser } from "@oakai/aila/src/features/categorisation/categorisers/MockCategoriser"; import { mockTracer } from "@oakai/core/src/tracing/mockTracer"; @@ -6,7 +7,7 @@ import { NextRequest } from "next/server"; import { expectTracingSpan } from "../../../utils/testHelpers/tracing"; import { handleChatPostRequest } from "./chatHandler"; -import { Config } from "./config"; +import type { Config } from "./config"; const chatId = "test-chat-id"; const userId = "test-user-id"; diff --git a/apps/nextjs/src/app/api/chat/route.ts b/apps/nextjs/src/app/api/chat/route.ts index 365edd577..992bee59e 100644 --- a/apps/nextjs/src/app/api/chat/route.ts +++ b/apps/nextjs/src/app/api/chat/route.ts @@ -1,9 +1,10 @@ -import { NextRequest } from "next/server"; +import type { NextRequest } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; import { handleChatPostRequest } from "./chatHandler"; -import { Config, defaultConfig } from "./config"; +import type { Config } from "./config"; +import { defaultConfig } from "./config"; async function postHandler(req: NextRequest): Promise { const config: Config = defaultConfig; diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts index 289b5a6a3..76cdd2238 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -1,9 +1,9 @@ import { AilaThreatDetectionError } from "@oakai/aila"; -import { AilaPluginContext } from "@oakai/aila/src/core/plugins"; +import type { AilaPluginContext } from "@oakai/aila/src/core/plugins"; import { inngest } from "@oakai/core"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; -import { PrismaClientWithAccelerate } from "@oakai/db"; -import { Moderation } from "@prisma/client"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; +import type { Moderation } from "@prisma/client"; import { createWebActionsPlugin } from "./webActionsPlugin"; diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 2d24a3aa2..24f69be32 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -1,12 +1,12 @@ import { AilaThreatDetectionError } from "@oakai/aila"; -import { AilaPlugin } from "@oakai/aila/src/core/plugins"; +import type { AilaPlugin } from "@oakai/aila/src/core/plugins"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; import { SafetyViolations as defaultSafetyViolations, inngest, } from "@oakai/core"; import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; -import { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { waitUntil } from "@vercel/functions"; diff --git a/apps/nextjs/src/app/api/qd-download/route.ts b/apps/nextjs/src/app/api/qd-download/route.ts index bae256c49..d6dbbc838 100644 --- a/apps/nextjs/src/app/api/qd-download/route.ts +++ b/apps/nextjs/src/app/api/qd-download/route.ts @@ -1,5 +1,6 @@ import { auth } from "@clerk/nextjs/server"; -import { LessonExportType, prisma } from "@oakai/db"; +import type { LessonExportType } from "@oakai/db"; +import { prisma } from "@oakai/db"; import { downloadDriveFile } from "@oakai/exports"; import * as Sentry from "@sentry/node"; diff --git a/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts index d38a54bf4..27e3f609d 100644 --- a/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/chat/[trpc]/route.ts @@ -3,7 +3,7 @@ import { chatAppRouter } from "@oakai/api/src/router/chat"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; -import { NextRequest, NextResponse } from "next/server"; +import type { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; diff --git a/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts index 41b409442..97cbf0c94 100644 --- a/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/main/[trpc]/route.ts @@ -3,7 +3,7 @@ import { oakAppRouter } from "@oakai/api/src/router"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; -import { NextRequest, NextResponse } from "next/server"; +import type { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; diff --git a/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts b/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts index 9d1b4bfa1..15d0ccf6a 100644 --- a/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts +++ b/apps/nextjs/src/app/api/trpc/test-support/[trpc]/route.ts @@ -4,7 +4,7 @@ import { router } from "@oakai/api/src/trpc"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; -import { NextRequest, NextResponse } from "next/server"; +import type { NextRequest, NextResponse } from "next/server"; import { withSentry } from "@/lib/sentry/withSentry"; diff --git a/apps/nextjs/src/app/home-page.tsx b/apps/nextjs/src/app/home-page.tsx index e3c6cbbc6..1179832f0 100644 --- a/apps/nextjs/src/app/home-page.tsx +++ b/apps/nextjs/src/app/home-page.tsx @@ -2,18 +2,18 @@ import { useUser } from "@clerk/nextjs"; import MuxPlayer from "@mux/mux-player-react"; +import type { OakColorToken } from "@oaknational/oak-components"; import { OakBox, OakFlex, OakHeading, OakLink, OakP, - OakColorToken, oakColorTokens, OakPrimaryButton, } from "@oaknational/oak-components"; -import { HomePageQueryResult } from "cms/types/aiHomePageType"; -import { Metadata } from "next"; +import type { HomePageQueryResult } from "cms/types/aiHomePageType"; +import type { Metadata } from "next"; import Image from "next/image"; import Link from "next/link"; import styled from "styled-components"; diff --git a/apps/nextjs/src/app/legal/[slug]/legal.tsx b/apps/nextjs/src/app/legal/[slug]/legal.tsx index baff0b6aa..34ee39173 100644 --- a/apps/nextjs/src/app/legal/[slug]/legal.tsx +++ b/apps/nextjs/src/app/legal/[slug]/legal.tsx @@ -2,7 +2,7 @@ import { OakBox } from "@oaknational/oak-components"; import { PortableText } from "@portabletext/react"; -import { PolicyDocument } from "cms/types/policyDocument"; +import type { PolicyDocument } from "cms/types/policyDocument"; import Layout from "@/components/Layout"; import { portableTextComponents } from "@/components/PortableText/portableTextComponents"; diff --git a/apps/nextjs/src/app/prompts/prompts.tsx b/apps/nextjs/src/app/prompts/prompts.tsx index b20f03aa1..b66557ff4 100644 --- a/apps/nextjs/src/app/prompts/prompts.tsx +++ b/apps/nextjs/src/app/prompts/prompts.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo } from "react"; -import { SerializedAppWithPrompt } from "@oakai/core/src/models/serializers"; +import type { SerializedAppWithPrompt } from "@oakai/core/src/models/serializers"; import { OakBox, OakHeading, diff --git a/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx b/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx index 466f8671d..06b02df09 100644 --- a/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx +++ b/apps/nextjs/src/app/quiz-designer/quiz-designer-page.tsx @@ -5,7 +5,8 @@ import { memo, useCallback, useEffect, useReducer, useState } from "react"; import { useUser } from "@clerk/nextjs"; import { aiLogger } from "@oakai/logger"; import { quizAppReducer } from "ai-apps/quiz-designer/state/reducer"; -import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import { useQuizSession } from "hooks/useQuizSession"; import { useRouter } from "next/navigation"; import { equals } from "remeda"; diff --git a/apps/nextjs/src/app/user/[[...index]]/page.tsx b/apps/nextjs/src/app/user/[[...index]]/page.tsx index 804f85b9d..340bc036c 100644 --- a/apps/nextjs/src/app/user/[[...index]]/page.tsx +++ b/apps/nextjs/src/app/user/[[...index]]/page.tsx @@ -1,4 +1,4 @@ -import { UserProfile } from '@clerk/nextjs' +import { UserProfile } from "@clerk/nextjs"; export default function Page() { return ( diff --git a/apps/nextjs/src/cms/data/fetchAiHomepage.ts b/apps/nextjs/src/cms/data/fetchAiHomepage.ts index 955419f7b..39622c269 100644 --- a/apps/nextjs/src/cms/data/fetchAiHomepage.ts +++ b/apps/nextjs/src/cms/data/fetchAiHomepage.ts @@ -1,6 +1,6 @@ import { homePageQuery } from "cms/queries/homePageQuery"; import { sanityClient } from "cms/sanityClient"; -import { HomePageQueryResult } from "cms/types/aiHomePageType"; +import type { HomePageQueryResult } from "cms/types/aiHomePageType"; export async function fetchAiHomepage(): Promise { const query = homePageQuery; diff --git a/apps/nextjs/src/cms/data/fetchPolicyDocument.ts b/apps/nextjs/src/cms/data/fetchPolicyDocument.ts index 4abe871e6..e2e6d23e4 100644 --- a/apps/nextjs/src/cms/data/fetchPolicyDocument.ts +++ b/apps/nextjs/src/cms/data/fetchPolicyDocument.ts @@ -1,5 +1,5 @@ import { sanityClient } from "cms/sanityClient"; -import { PolicyDocument } from "cms/types/policyDocument"; +import type { PolicyDocument } from "cms/types/policyDocument"; export async function fetchPolicyDocument({ slug, diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts index 429271400..50168c43e 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts @@ -1,7 +1,7 @@ import { useMemo, useEffect } from "react"; import { aiLogger } from "@oakai/logger"; -import { Message } from "ai"; +import type { Message } from "ai"; const log = aiLogger("chat"); diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts index 3ae82ffe5..a3503b2c0 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useProgressForDownloads.ts @@ -1,8 +1,8 @@ import { useMemo } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { lessonPlanSectionsSchema } from "@oakai/exports/src/schema/input.schema"; -import { ZodIssue } from "zod"; +import type { ZodIssue } from "zod"; /** * For a given list of Zod issues and lessonPlan fields, checks that none of diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-left-hand-side.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-left-hand-side.tsx index c7737763b..b36ff5a7f 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-left-hand-side.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-left-hand-side.tsx @@ -1,9 +1,9 @@ import React from "react"; import { Flex } from "@radix-ui/themes"; -import { Message } from "ai"; +import type { Message } from "ai"; -import { DemoContextProps } from "@/components/ContextProviders/Demo"; +import type { DemoContextProps } from "@/components/ContextProviders/Demo"; import ChatLhsHeader from "./chat-lhs-header"; import { ChatList } from "./chat-list"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx index 632ba018f..81a8637c0 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanDisplay.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { BasedOnOptional } from "@oakai/aila/src/protocol/schema"; +import type { BasedOnOptional } from "@oakai/aila/src/protocol/schema"; import { Flex, Text } from "@radix-ui/themes"; import { cva } from "class-variance-authority"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanMapToMarkDown.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanMapToMarkDown.tsx index ee8656e89..f9b405ee0 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanMapToMarkDown.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lessonPlanMapToMarkDown.tsx @@ -1,6 +1,6 @@ import { useRef } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { sectionToMarkdown } from "@oakai/aila/src/protocol/sectionToMarkdown"; import { lessonSectionTitlesAndMiniDescriptions } from "data/lessonSectionTitlesAndMiniDescriptions"; @@ -64,7 +64,7 @@ export default LessonPlanMapToMarkDown; const ChatSection = ({ sectionRefs, objectKey, value }) => { const sectionRef = useRef(null); - if (!!sectionRefs) sectionRefs[objectKey] = sectionRef; + if (sectionRefs) sectionRefs[objectKey] = sectionRef; return (
diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx index c6794c2ff..fa0c5c7f6 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx @@ -1,27 +1,21 @@ "use client"; -import { - Dispatch, - SetStateAction, - useCallback, - useEffect, - useRef, - useState, -} from "react"; +import type { Dispatch, SetStateAction } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { OakBox, OakFlex, OakIcon, OakSpan } from "@oaknational/oak-components"; -import { Message } from "ai"; +import type { Message } from "ai"; import Link from "next/link"; import { ChatMessage } from "@/components/AppComponents/Chat/chat-message"; import { useLessonChat } from "@/components/ContextProviders/ChatProvider"; -import { DemoContextProps } from "@/components/ContextProviders/Demo"; +import type { DemoContextProps } from "@/components/ContextProviders/Demo"; import { useDialog } from "../DialogContext"; -import { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; +import type { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; import { useProgressForDownloads } from "./Chat/hooks/useProgressForDownloads"; -import { DialogTypes } from "./Chat/types"; +import type { DialogTypes } from "./Chat/types"; export interface ChatListProps { isDemoLocked: boolean; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx index dec946012..184bc6707 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-message/index.tsx @@ -1,8 +1,9 @@ // Inspired by Chatbot-UI and modified to fit the needs of this project // @see https://github.com/mckaywrigley/chatbot-ui/blob/main/components/Chat/ChatMessage.tsx -import { ReactNode, useState } from "react"; +import type { ReactNode } from "react"; +import { useState } from "react"; -import { +import type { ActionDocument, BadDocument, CommentDocument, @@ -14,20 +15,20 @@ import { StateDocument, TextDocument, UnknownDocument, - parseMessageParts, } from "@oakai/aila/src/protocol/jsonPatchProtocol"; +import { parseMessageParts } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { isSafe } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; -import { Message } from "ai"; +import type { Message } from "ai"; import { MemoizedReactMarkdownWithStyles } from "@/components/AppComponents/Chat/markdown"; import { useChatModeration } from "@/components/ContextProviders/ChatModerationContext"; import { Icon } from "@/components/Icon"; import { cn } from "@/lib/utils"; -import { ModerationModalHelpers } from "../../FeedbackForms/ModerationFeedbackModal"; -import { AilaStreamingStatus } from "../Chat/hooks/useAilaStreamingStatus"; +import type { ModerationModalHelpers } from "../../FeedbackForms/ModerationFeedbackModal"; +import type { AilaStreamingStatus } from "../Chat/hooks/useAilaStreamingStatus"; import { isModeration } from "./protocol"; const log = aiLogger("chat"); diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-message/protocol.ts b/apps/nextjs/src/components/AppComponents/Chat/chat-message/protocol.ts index dfcf5bfa0..5ed65611b 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-message/protocol.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-message/protocol.ts @@ -1,4 +1,4 @@ -import { +import type { ActionDocument, MessagePartDocument, ModerationDocument, diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx index 3b8a35b52..3226fc83d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-quick-buttons.tsx @@ -8,7 +8,7 @@ import { useLessonPlanTracking } from "@/lib/analytics/lessonPlanTrackingContext import useAnalytics from "@/lib/analytics/useAnalytics"; import { useDialog } from "../DialogContext"; -import { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; +import type { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; import ChatButton from "./ui/chat-button"; import { IconRefresh, IconStop } from "./ui/icons"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx index bd0d63026..21685515c 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx @@ -1,12 +1,12 @@ import React, { useRef, useState } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { OakIcon, OakSmallSecondaryButton } from "@oaknational/oak-components"; -import { Message } from "ai"; +import type { Message } from "ai"; import Link from "next/link"; import AiIcon from "../../AiIcon"; -import { DemoContextProps } from "../../ContextProviders/Demo"; +import type { DemoContextProps } from "../../ContextProviders/Demo"; import { useDialog } from "../DialogContext"; import LessonPlanDisplay from "./chat-lessonPlanDisplay"; import ExportButtons from "./export-buttons"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx index bd47903b4..08392232d 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx @@ -17,7 +17,7 @@ import { } from "@/components/AppComponents/Chat/ui/dialog"; import { IconSpinner } from "@/components/AppComponents/Chat/ui/icons"; import { useCopyToClipboard } from "@/lib/hooks/use-copy-to-clipboard"; -import { SideBarChatItem } from "@/lib/types"; +import type { SideBarChatItem } from "@/lib/types"; import { trpc } from "@/utils/trpc"; import { constructSharePath } from "./Chat/utils"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx index 523bd0ffd..48a17ecd5 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/flag-button.tsx @@ -9,7 +9,8 @@ import { useLessonChat } from "@/components/ContextProviders/ChatProvider"; import { trpc } from "@/utils/trpc"; import ActionButton from "./action-button"; -import { DropDownFormWrapper, FeedbackOption } from "./drop-down-form-wrapper"; +import type { FeedbackOption } from "./drop-down-form-wrapper"; +import { DropDownFormWrapper } from "./drop-down-form-wrapper"; import { SmallRadioButton } from "./small-radio-button"; const flagOptions = [ diff --git a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx index 0b6b4890d..ee4a17e30 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/drop-down-section/modify-button.tsx @@ -10,7 +10,8 @@ import { useLessonChat } from "@/components/ContextProviders/ChatProvider"; import { trpc } from "@/utils/trpc"; import ActionButton from "./action-button"; -import { DropDownFormWrapper, FeedbackOption } from "./drop-down-form-wrapper"; +import type { FeedbackOption } from "./drop-down-form-wrapper"; +import { DropDownFormWrapper } from "./drop-down-form-wrapper"; import { SmallRadioButton } from "./small-radio-button"; const log = aiLogger("chat"); diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx index cbac20242..f32699fa2 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.stories.tsx @@ -1,4 +1,4 @@ -import { +import type { Cycle, Keyword, Misconception, diff --git a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx index 72b1aa62a..98f414d2e 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/export-buttons/LessonPlanProgressDropdown.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { Flex } from "@radix-ui/themes"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/guidance-required.tsx b/apps/nextjs/src/components/AppComponents/Chat/guidance-required.tsx index 1ef467d58..14cecca3b 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/guidance-required.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/guidance-required.tsx @@ -2,7 +2,7 @@ import { isSafe, moderationSlugToDescription, } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { Icon } from "@/components/Icon"; import { cn } from "@/lib/utils"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx b/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx index bdc9dc5d6..1fe02df05 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/markdown.tsx @@ -1,5 +1,7 @@ -import React, { FC, memo } from "react"; -import ReactMarkdown, { Options } from "react-markdown"; +import type { FC } from "react"; +import React, { memo } from "react"; +import type { Options } from "react-markdown"; +import ReactMarkdown from "react-markdown"; import * as Tooltip from "@radix-ui/react-tooltip"; import { Box, Flex } from "@radix-ui/themes"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx b/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx index 16b8de455..41d453d46 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useRef } from "react"; -import { UseChatHelpers } from "ai/react"; +import type { UseChatHelpers } from "ai/react"; import { Tooltip, @@ -11,7 +11,7 @@ import { useLessonPlanTracking } from "@/lib/analytics/lessonPlanTrackingContext import { useEnterSubmit } from "@/lib/hooks/use-enter-submit"; import { useSidebar } from "@/lib/hooks/use-sidebar"; -import { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; +import type { AilaStreamingStatus } from "./Chat/hooks/useAilaStreamingStatus"; export interface PromptFormProps extends Pick { diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx index af053e9a1..90d157eaa 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-actions.tsx @@ -27,7 +27,7 @@ import { TooltipContent, TooltipTrigger, } from "@/components/AppComponents/Chat/ui/tooltip"; -import { SideBarChatItem } from "@/lib/types"; +import type { SideBarChatItem } from "@/lib/types"; import { trpc } from "@/utils/trpc"; type SidebarActionsProps = Readonly<{ diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx index 65926df69..71dec1e16 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-item.tsx @@ -14,7 +14,7 @@ import { TooltipContent, TooltipTrigger, } from "@/components/AppComponents/Chat/ui/tooltip"; -import { SideBarChatItem } from "@/lib/types"; +import type { SideBarChatItem } from "@/lib/types"; import { cn } from "@/lib/utils"; import { constructChatPath } from "./Chat/utils"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx index d396f6d6c..ff3503223 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-items.tsx @@ -3,7 +3,7 @@ import { AnimatePresence, motion } from "framer-motion"; import { SidebarItem } from "@/components/AppComponents/Chat/sidebar-item"; -import { SideBarChatItem } from "@/lib/types"; +import type { SideBarChatItem } from "@/lib/types"; interface SidebarItemsProps { chats: SideBarChatItem[]; diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx index ccc1923a2..131a0d415 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx @@ -1,8 +1,9 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { cva, type VariantProps } from 'class-variance-authority' +import * as React from "react"; -import { cn } from '@/lib/utils' +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; const buttonVariants = cva( 'inline-flex items-center justify-center rounded-md text-sm font-medium shadow ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/chat-button.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/chat-button.tsx index 1c482e484..379b1d74a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/chat-button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/chat-button.tsx @@ -1,6 +1,7 @@ import { cva } from "class-variance-authority"; -import ButtonCore, { ButtonVariant } from "@/components/ButtonCore"; +import type { ButtonVariant } from "@/components/ButtonCore"; +import ButtonCore from "@/components/ButtonCore"; import type { IconName } from "@/components/Icon"; interface ButtonProps { diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/codeblock.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/codeblock.tsx index f9daf291d..1633f7d0c 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/codeblock.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/codeblock.tsx @@ -1,6 +1,7 @@ "use client"; -import { FC, memo } from "react"; +import type { FC } from "react"; +import { memo } from "react"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementContent.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementContent.tsx index 3fd2f2427..f2c750455 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementContent.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/JudgementContent.tsx @@ -1,9 +1,10 @@ -import { Dispatch, SetStateAction, useMemo } from "react"; +import type { Dispatch, SetStateAction } from "react"; +import { useMemo } from "react"; import { Box, Grid, Text } from "@radix-ui/themes"; import { cva } from "class-variance-authority"; -import { Option } from "../../../ai-apps/comparative-judgement/state/types"; +import type { Option } from "../../../ai-apps/comparative-judgement/state/types"; import JudgementHeading from "./JudgementHeading"; import QuestionButton from "./QuestionButton"; import ReasonForChoosing from "./ReasonForChoosing"; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/KeyStageAndSubjectPicker.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/KeyStageAndSubjectPicker.tsx index 93224cdea..b5a039e53 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/KeyStageAndSubjectPicker.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/KeyStageAndSubjectPicker.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo } from "react"; -import { +import type { KeyStageName, SubjectName, - subjectsAndKeyStages, } from "@oakai/core/src/data/subjectsAndKeyStages"; +import { subjectsAndKeyStages } from "@oakai/core/src/data/subjectsAndKeyStages"; import { Flex } from "@radix-ui/themes"; import Input from "@/components/Input"; @@ -80,7 +80,7 @@ const KeyStageAndSubjectPicker = ({ type="dropdown" label="Subject" name="subject" - options={allowedSubjects as string[]} + options={allowedSubjects} onChange={(e) => setSelectedSubject(e.target.value as SubjectName)} value={selectedSubject} /> diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx index f0f961a18..1277cf06f 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx @@ -1,5 +1,5 @@ import { Box, Flex, Text } from "@radix-ui/themes"; -import { +import type { AnswerAndDistractor, OptionWithPrompt, } from "ai-apps/comparative-judgement/state/types"; @@ -17,7 +17,7 @@ type PreviewContentProps = { const PreviewContent = ({ option, question }: PreviewContentProps) => { if (!option?.answerAndDistractor) return null; const answersAndDistractors = - option?.answerAndDistractor as AnswerAndDistractor; + option?.answerAndDistractor; const { answers, distractors } = answersAndDistractors; const answerAndDistractorArray = [...answers, ...distractors]; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/QuestionButton.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/QuestionButton.tsx index 9ea001607..aa08fba0b 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/QuestionButton.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/QuestionButton.tsx @@ -1,4 +1,4 @@ -import { Dispatch } from "react"; +import type { Dispatch } from "react"; import { Flex } from "@radix-ui/themes"; import { cva } from "class-variance-authority"; @@ -6,7 +6,7 @@ import { cva } from "class-variance-authority"; import Button from "@/components/Button"; import { sortAlphabetically } from "@/utils/alphabetiseArray"; -import { +import type { AnswerAndDistractor, Option, } from "../../../ai-apps/comparative-judgement/state/types"; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/index.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/index.tsx index 02d38a225..681a47072 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/index.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useState } from "react"; -import { KeyStageName, SubjectName } from "@oakai/core"; +import type { KeyStageName, SubjectName } from "@oakai/core"; import { Flex } from "@radix-ui/themes"; import useQuestionsForJudgement from "hooks/useQuestionsForJudgement"; diff --git a/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx b/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx index dd3448c0e..d59b8ae55 100644 --- a/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx +++ b/apps/nextjs/src/components/AppComponents/DialogContext/index.tsx @@ -2,7 +2,7 @@ import React, { createContext, useState, useContext, useMemo } from "react"; -import { DialogTypes } from "../Chat/Chat/types"; +import type { DialogTypes } from "../Chat/Chat/types"; interface DialogContextType { dialogWindow: DialogTypes; diff --git a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx index b218669ff..a4407a22c 100644 --- a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx +++ b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackForm.tsx @@ -1,7 +1,7 @@ import { useEffect } from "react"; import { moderationSlugToDescription } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { Dialog } from "@radix-ui/themes"; import { useModerationFeedbackSurvey } from "hooks/surveys/useModerationFeedbackSurvey"; diff --git a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackModal.tsx b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackModal.tsx index 561676fbe..f0a79d180 100644 --- a/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackModal.tsx +++ b/apps/nextjs/src/components/AppComponents/FeedbackForms/ModerationFeedbackModal.tsx @@ -3,10 +3,8 @@ import { useState } from "react"; import * as Dialog from "@radix-ui/react-dialog"; import * as Sentry from "@sentry/react"; -import { - ModerationFeedbackForm, - ModerationFeedbackFormProps, -} from "@/components/AppComponents/FeedbackForms/ModerationFeedbackForm"; +import type { ModerationFeedbackFormProps } from "@/components/AppComponents/FeedbackForms/ModerationFeedbackForm"; +import { ModerationFeedbackForm } from "@/components/AppComponents/FeedbackForms/ModerationFeedbackForm"; import { dialogOverlay } from "../../DialogControl/dialogStyles"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/DownloadGiftButton.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/DownloadGiftButton.tsx index 9d078dc29..732da1b79 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/DownloadGiftButton.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/DownloadGiftButton.tsx @@ -1,4 +1,4 @@ -import { ExportableQuizAppState } from "@oakai/exports/src/schema/input.schema"; +import type { ExportableQuizAppState } from "@oakai/exports/src/schema/input.schema"; import { convertToGIFTFormat } from "ai-apps/quiz-designer/convertToGIFTFormat"; import useAnalytics from "@/lib/analytics/useAnalytics"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/ErrorBox.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/ErrorBox.tsx index 9014511f0..3b3c7869c 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/ErrorBox.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/ErrorBox.tsx @@ -1,4 +1,5 @@ -import { Box, Responsive } from "@radix-ui/themes"; +import type { Responsive } from "@radix-ui/themes"; +import { Box } from "@radix-ui/themes"; type ErrorBoxProps = { message: string | JSX.Element; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx index 4e8c1fe7c..23968628d 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx @@ -3,7 +3,7 @@ import { useMemo } from "react"; import { Box, Flex, Heading, Text } from "@radix-ui/themes"; import { convertQuizToCSV } from "ai-apps/quiz-designer/convertToCSV"; import { makeExportable } from "ai-apps/quiz-designer/export-helpers"; -import { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; import useShareContent from "hooks/useShareContent"; import Button from "@/components/Button"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/GenerateAllButton.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/GenerateAllButton.tsx index 646f56d4a..a5cea6259 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/GenerateAllButton.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/GenerateAllButton.tsx @@ -1,6 +1,6 @@ import { Box, Flex } from "@radix-ui/themes"; -import { IconName } from "../../Icon"; +import type { IconName } from "../../Icon"; import ChatButton from "../Chat/ui/chat-button"; import Generating from "../common/Generating"; import PromptExplainerButton from "../common/PromptExplainerButton"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/Hero.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/Hero.tsx index b18bb246b..22661d048 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/Hero.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/Hero.tsx @@ -1,12 +1,12 @@ -import { Dispatch, useCallback } from "react"; +import type { Dispatch } from "react"; +import { useCallback } from "react"; -import { KeyStageName, SubjectName } from "@oakai/core"; +import type { KeyStageName, SubjectName } from "@oakai/core"; import { Box, Flex, Heading, Text } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import Image from "next/image"; import jigsaw from "@/assets/svg/illustration/jigsaw.svg"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizContent.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizContent.tsx index 4c0fba348..23a5ef2bc 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizContent.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizContent.tsx @@ -1,8 +1,10 @@ -import { Dispatch, useRef } from "react"; +import type { Dispatch } from "react"; +import { useRef } from "react"; import { Box, Container } from "@radix-ui/themes"; -import { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; -import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import useShareContent from "hooks/useShareContent"; import useSuggestedQuestions from "hooks/useSuggestedQuestions"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx index 56f090582..8b920cee7 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizDesignerPageContent.tsx @@ -1,4 +1,5 @@ -import { Dispatch, useRef } from "react"; +import type { Dispatch } from "react"; +import { useRef } from "react"; import { Box, Container } from "@radix-ui/themes"; import useSuggestedQuestions from "hooks/useSuggestedQuestions"; @@ -10,11 +11,9 @@ import ControllerRow from "@/components/AppComponents/QuizDesigner/QuizQuestionR import RateLimitNotification from "@/components/AppComponents/common/RateLimitNotification"; import Layout from "@/components/Layout"; -import { QuizAppAction } from "../../../ai-apps/quiz-designer/state/actions"; -import { - QuizAppState, - QuizAppStatus, -} from "../../../ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "../../../ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "../../../ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "../../../ai-apps/quiz-designer/state/types"; import SuggestedQuestions from "./SuggestedQuestions"; type Props = { diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answer.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answer.tsx index b1db590bd..0447dd562 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answer.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answer.tsx @@ -1,13 +1,12 @@ import { useCallback } from "react"; -import { GenerationPart, GenerationPartType } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; +import { GenerationPartType } from "@oakai/core/src/types"; import browserLogger from "@oakai/logger/browser"; import { Flex } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppAnswer, QuizAppState, QuizAppStateQuestion, diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answers.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answers.tsx index b70c96cff..cd739f83d 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answers.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Answers.tsx @@ -1,8 +1,9 @@ -import { Dispatch, useState } from "react"; +import type { Dispatch } from "react"; +import { useState } from "react"; import { Box, Flex } from "@radix-ui/themes"; -import { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx index 12adff9e6..85dacbf45 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx @@ -1,10 +1,9 @@ -import { Dispatch, useState } from "react"; +import type { Dispatch } from "react"; +import { useState } from "react"; import { Flex, Heading } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; import useAnalytics from "@/lib/analytics/useAnalytics"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractor.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractor.tsx index 38d070217..9fc978685 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractor.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractor.tsx @@ -1,13 +1,13 @@ -import { Dispatch, useCallback, useState } from "react"; +import type { Dispatch } from "react"; +import { useCallback, useState } from "react"; -import { GenerationPart, GenerationPartType } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; +import { GenerationPartType } from "@oakai/core/src/types"; import browserLogger from "@oakai/logger/browser"; import { Flex } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppDistractor, QuizAppState, QuizAppStateQuestion, diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractors.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractors.tsx index dc75c74b4..7c7ec3774 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractors.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Distractors.tsx @@ -1,8 +1,8 @@ -import { Dispatch } from "react"; +import type { Dispatch } from "react"; import { Box, Flex } from "@radix-ui/themes"; -import { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Question.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Question.tsx index d5b6f2498..13b0272ef 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Question.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/Question.tsx @@ -1,17 +1,16 @@ -import { Dispatch, useCallback } from "react"; +import type { Dispatch } from "react"; +import { useCallback } from "react"; import { Box, Flex } from "@radix-ui/themes"; import { quizRequestGeneration } from "ai-apps/quiz-designer/quizRequestGeneration"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; +import type { GenerationWithResponse } from "hooks/useGeneration"; import { - GenerationWithResponse, UseGenerationStatus, isGenerationHookLoading, } from "hooks/useGeneration"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx index 89b05da04..420fede29 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/RegenButtonGroup.tsx @@ -1,8 +1,6 @@ import { Flex } from "@radix-ui/themes"; -import { - UseGenerationStatus, - isGenerationHookLoading, -} from "hooks/useGeneration"; +import type { UseGenerationStatus } from "hooks/useGeneration"; +import { isGenerationHookLoading } from "hooks/useGeneration"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/index.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/index.tsx index 3f2912fda..fbe90ec90 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/index.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/index.tsx @@ -1,11 +1,9 @@ -import { Dispatch } from "react"; +import type { Dispatch } from "react"; import { Box, Flex, Heading, Text } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizRestoreDialog.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizRestoreDialog.tsx index cdbad249d..f61bfc39e 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizRestoreDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizRestoreDialog.tsx @@ -1,11 +1,10 @@ -import { Dispatch, useCallback } from "react"; +import type { Dispatch } from "react"; +import { useCallback } from "react"; import * as Sentry from "@sentry/nextjs"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Choose.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Choose.tsx index f317d172e..81d91e568 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Choose.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Choose.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { +import type { KeyStageName, SubjectName, } from "@oakai/core/src/data/subjectsAndKeyStages"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Confirmed.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Confirmed.tsx index ec2e0321d..690797399 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Confirmed.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/Confirmed.tsx @@ -1,4 +1,4 @@ -import { KeyStageName, SubjectName } from "@oakai/core"; +import type { KeyStageName, SubjectName } from "@oakai/core"; import { Flex, Heading, Text } from "@radix-ui/themes"; type ConfirmedProps = { diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/index.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/index.tsx index 3e95c1b16..fd34ee4ec 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/index.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SubjectKeyStageSection/index.tsx @@ -1,4 +1,4 @@ -import { +import type { KeyStageName, SubjectName, } from "@oakai/core/src/data/subjectsAndKeyStages"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx index 9fdd2f725..5c907490a 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestionCard.tsx @@ -1,11 +1,10 @@ -import { Dispatch, useState } from "react"; +import type { Dispatch } from "react"; +import { useState } from "react"; import { Flex, Text } from "@radix-ui/themes"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; import { Icon } from "@/components/Icon"; diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestions.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestions.tsx index 2145a3097..c1113bd8e 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestions.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/SuggestedQuestions.tsx @@ -1,9 +1,9 @@ -import { Dispatch } from "react"; +import type { Dispatch } from "react"; import { Box, Flex, Grid, Text } from "@radix-ui/themes"; -import { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; -import { UseGenerationError } from "hooks/useGeneration"; -import { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import type { UseGenerationError } from "hooks/useGeneration"; +import type { PotentialQuestionsType } from "hooks/useSuggestedQuestions"; import LoadingWheel from "@/components/LoadingWheel"; diff --git a/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx b/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx index ea0b8d5e8..7e8e376cc 100644 --- a/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx +++ b/apps/nextjs/src/components/AppComponents/common/RateLimitNotification.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useUser } from "@clerk/nextjs"; -import { RateLimitInfo } from "@oakai/api/src/types"; +import type { RateLimitInfo } from "@oakai/api/src/types"; import { Box, Flex, Text } from "@radix-ui/themes"; import { cva } from "class-variance-authority"; diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx index 58d886f53..444eb2d5b 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationFeedbackDialog.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { useUser } from "@clerk/nextjs"; -import { GenerationPart } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; import { structuredLogger as logger } from "@oakai/logger"; import * as Dialog from "@radix-ui/react-dialog"; diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx index 9f7902b31..b524bfcb8 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { +import type { GenerationPart, GenerationPartPlaceholder, } from "@oakai/core/src/types"; diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationWrapper.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationWrapper.tsx index 7ba0fe6cc..d837683f7 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationWrapper.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationWrapper.tsx @@ -1,6 +1,6 @@ import { useCallback, useState } from "react"; -import { GenerationPart } from "@oakai/core/src/types"; +import type { GenerationPart } from "@oakai/core/src/types"; import { Box } from "@radix-ui/themes"; import GenerationFeedbackDialog, { diff --git a/apps/nextjs/src/components/AppComponents/common/SuggestedLessons.tsx b/apps/nextjs/src/components/AppComponents/common/SuggestedLessons.tsx index c91f50e15..5fba0caaf 100644 --- a/apps/nextjs/src/components/AppComponents/common/SuggestedLessons.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SuggestedLessons.tsx @@ -1,6 +1,6 @@ import { Flex, Grid, Heading } from "@radix-ui/themes"; -import { LessonPlannerAppState } from "ai-apps/lesson-planner/state/types"; -import { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import type { LessonPlannerAppState } from "ai-apps/lesson-planner/state/types"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; import Link from "next/link"; import { Icon } from "@/components/Icon"; diff --git a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx index 50aa10624..7b3f0081b 100644 --- a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx +++ b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx @@ -1,6 +1,6 @@ import { useState } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { aiLogger } from "@oakai/logger"; import { Box } from "@radix-ui/themes"; import * as Sentry from "@sentry/nextjs"; diff --git a/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx b/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx index c84385113..cfe0a4d87 100644 --- a/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx +++ b/apps/nextjs/src/components/AppComponents/download/DownloadButton.tsx @@ -1,4 +1,4 @@ -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { Box } from "@radix-ui/themes"; import Link from "next/link"; @@ -6,10 +6,8 @@ import useAnalytics from "@/lib/analytics/useAnalytics"; import { trackDownload } from "@/utils/trackDownload"; import { trpc } from "@/utils/trpc"; -import { - ExportsType, - getExportsConfig, -} from "../../ExportsDialogs/exports.helpers"; +import type { ExportsType } from "../../ExportsDialogs/exports.helpers"; +import { getExportsConfig } from "../../ExportsDialogs/exports.helpers"; import { Icon } from "../../Icon"; import LoadingWheel from "../../LoadingWheel"; import LessonIcon from "../../SVGParts/LessonIcon"; diff --git a/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx b/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx index 5ef0328aa..a0932aa1c 100644 --- a/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx +++ b/apps/nextjs/src/components/AppComponents/download/SectionsNotCompleteDownloadNotice.tsx @@ -12,7 +12,7 @@ import { import AiIcon from "@/components/SVGParts/AiIcon"; -import { ProgressSections } from "../Chat/Chat/hooks/useProgressForDownloads"; +import type { ProgressSections } from "../Chat/Chat/hooks/useProgressForDownloads"; const SectionsNotCompleteDownloadNotice = ({ sections, diff --git a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderBottom.tsx b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderBottom.tsx index 02772ba40..49a34c953 100644 --- a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderBottom.tsx +++ b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderBottom.tsx @@ -1,4 +1,4 @@ -import { BorderProps } from "../"; +import type { BorderProps } from "../"; function BoxBorderBottom({ className = "" }: Readonly) { return ( diff --git a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderLeft.tsx b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderLeft.tsx index 2349219b2..b8ff0c702 100644 --- a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderLeft.tsx +++ b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderLeft.tsx @@ -1,4 +1,4 @@ -import { BorderProps } from "../"; +import type { BorderProps } from "../"; function BoxBorderLeft({ className = "" }: Readonly) { return ( diff --git a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderRight.tsx b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderRight.tsx index 97a412d6c..34a08db5b 100644 --- a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderRight.tsx +++ b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderRight.tsx @@ -1,4 +1,4 @@ -import { BorderProps } from "../"; +import type { BorderProps } from "../"; function BoxBorderRight({ className = "" }: Readonly) { return ( diff --git a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderTop.tsx b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderTop.tsx index 9cc4cf690..ec7a6f9aa 100644 --- a/apps/nextjs/src/components/Border/BoxBorder/BoxBorderTop.tsx +++ b/apps/nextjs/src/components/Border/BoxBorder/BoxBorderTop.tsx @@ -1,4 +1,4 @@ -import { BorderProps } from "../"; +import type { BorderProps } from "../"; function BoxBorderTop({ className = "" }: Readonly) { return ( diff --git a/apps/nextjs/src/components/Button.tsx b/apps/nextjs/src/components/Button.tsx index 8af6b7efd..58dd9ecee 100644 --- a/apps/nextjs/src/components/Button.tsx +++ b/apps/nextjs/src/components/Button.tsx @@ -2,7 +2,8 @@ import { cva } from "class-variance-authority"; import type { IconName } from "@/components/Icon"; -import ButtonCore, { ButtonVariant } from "./ButtonCore"; +import type { ButtonVariant } from "./ButtonCore"; +import ButtonCore from "./ButtonCore"; interface ButtonProps { children?: React.ReactNode; diff --git a/apps/nextjs/src/components/ButtonCore.tsx b/apps/nextjs/src/components/ButtonCore.tsx index 9abedb3b1..d9e5d1601 100644 --- a/apps/nextjs/src/components/ButtonCore.tsx +++ b/apps/nextjs/src/components/ButtonCore.tsx @@ -5,7 +5,8 @@ import Link from "next/link"; import splodge from "@/assets/svg/splodge.svg"; -import { Icon, IconName } from "./Icon"; +import type { IconName } from "./Icon"; +import { Icon } from "./Icon"; type ContainerClassNameFn = (options: { disabled?: boolean; diff --git a/apps/nextjs/src/components/CheckBox.tsx b/apps/nextjs/src/components/CheckBox.tsx index 8baa6c367..8473dd3db 100644 --- a/apps/nextjs/src/components/CheckBox.tsx +++ b/apps/nextjs/src/components/CheckBox.tsx @@ -1,4 +1,4 @@ -import { PropsWithChildren } from "react"; +import type { PropsWithChildren } from "react"; import * as Checkbox from "@radix-ui/react-checkbox"; import { CheckIcon } from "@radix-ui/react-icons"; diff --git a/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx b/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx index 488b7f927..2e90dfe4a 100644 --- a/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx +++ b/apps/nextjs/src/components/ContextProviders/ChatModerationContext.tsx @@ -1,4 +1,5 @@ -import { useMemo, createContext, useContext, ReactNode } from "react"; +import type { ReactNode } from "react"; +import { useMemo, createContext, useContext } from "react"; import { useModerationModal } from "../AppComponents/FeedbackForms/ModerationFeedbackModal"; diff --git a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx index 60458bc66..de9fac36b 100644 --- a/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/ChatProvider.tsx @@ -11,17 +11,18 @@ import { toast } from "react-hot-toast"; import { generateMessageId } from "@oakai/aila/src/helpers/chat/generateMessageId"; import { parseMessageParts } from "@oakai/aila/src/protocol/jsonPatchProtocol"; -import { +import type { AilaPersistedChat, LooseLessonPlan, } from "@oakai/aila/src/protocol/schema"; import { isToxic } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; -import { Moderation } from "@oakai/db"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { Moderation } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; -import { Message, nanoid } from "ai"; -import { ChatRequestOptions, CreateMessage } from "ai"; +import type { Message } from "ai"; +import { nanoid } from "ai"; +import type { ChatRequestOptions, CreateMessage } from "ai"; import { useChat } from "ai/react"; import { useTemporaryLessonPlanWithStreamingEdits } from "hooks/useTemporaryLessonPlanWithStreamingEdits"; import { redirect, usePathname, useRouter } from "next/navigation"; @@ -30,10 +31,8 @@ import { useLessonPlanTracking } from "@/lib/analytics/lessonPlanTrackingContext import useAnalytics from "@/lib/analytics/useAnalytics"; import { trpc } from "@/utils/trpc"; -import { - AilaStreamingStatus, - useAilaStreamingStatus, -} from "../AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; +import type { AilaStreamingStatus } from "../AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; +import { useAilaStreamingStatus } from "../AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; import { findMessageIdFromContent } from "../AppComponents/Chat/Chat/utils"; import { isAccountLocked, diff --git a/apps/nextjs/src/components/ContextProviders/CookieConsentProvider.tsx b/apps/nextjs/src/components/ContextProviders/CookieConsentProvider.tsx index ed448debc..e4c4f5234 100644 --- a/apps/nextjs/src/components/ContextProviders/CookieConsentProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/CookieConsentProvider.tsx @@ -1,6 +1,7 @@ "use client"; -import { PropsWithChildren, useEffect } from "react"; +import type { PropsWithChildren } from "react"; +import { useEffect } from "react"; import { OakCookieConsentProvider, diff --git a/apps/nextjs/src/components/ContextProviders/FontProvider.tsx b/apps/nextjs/src/components/ContextProviders/FontProvider.tsx index 32d861d9a..e8008ba58 100644 --- a/apps/nextjs/src/components/ContextProviders/FontProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/FontProvider.tsx @@ -1,12 +1,7 @@ "use client"; -import React, { - Dispatch, - createContext, - useContext, - useMemo, - useState, -} from "react"; +import type { Dispatch } from "react"; +import React, { createContext, useContext, useMemo, useState } from "react"; interface FontContextProps { fontClass: string; diff --git a/apps/nextjs/src/components/ContextProviders/GleapProvider.tsx b/apps/nextjs/src/components/ContextProviders/GleapProvider.tsx index 9f45fa4ec..4ff4f790d 100644 --- a/apps/nextjs/src/components/ContextProviders/GleapProvider.tsx +++ b/apps/nextjs/src/components/ContextProviders/GleapProvider.tsx @@ -1,6 +1,7 @@ "use client"; -import { PropsWithChildren, useEffect } from "react"; +import type { PropsWithChildren } from "react"; +import { useEffect } from "react"; import { useAuth, useUser } from "@clerk/nextjs"; import { useOakConsent } from "@oaknational/oak-consent-client"; diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx index 586d65226..9e9106560 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ReportContentDialog.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { aiLogger } from "@oakai/logger"; import { Flex } from "@radix-ui/themes"; -import { Message } from "ai"; +import type { Message } from "ai"; import { usePosthogFeedbackSurvey } from "hooks/surveys/usePosthogFeedbackSurvey"; import ChatButton from "@/components/AppComponents/Chat/ui/chat-button"; diff --git a/apps/nextjs/src/components/DialogControl/ContentOptions/ShareChatDialog.tsx b/apps/nextjs/src/components/DialogControl/ContentOptions/ShareChatDialog.tsx index e38f080a7..bf12bd9c0 100644 --- a/apps/nextjs/src/components/DialogControl/ContentOptions/ShareChatDialog.tsx +++ b/apps/nextjs/src/components/DialogControl/ContentOptions/ShareChatDialog.tsx @@ -1,10 +1,10 @@ import { useCallback } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { Flex } from "@radix-ui/themes"; import * as Sentry from "@sentry/react"; -import { DialogTypes } from "@/components/AppComponents/Chat/Chat/types"; +import type { DialogTypes } from "@/components/AppComponents/Chat/Chat/types"; import ChatButton from "@/components/AppComponents/Chat/ui/chat-button"; import LoadingWheel from "@/components/LoadingWheel"; import { getLessonTrackingProps } from "@/lib/analytics/helpers"; diff --git a/apps/nextjs/src/components/DialogControl/DialogContents.tsx b/apps/nextjs/src/components/DialogControl/DialogContents.tsx index 8f148b249..c26db0b2d 100644 --- a/apps/nextjs/src/components/DialogControl/DialogContents.tsx +++ b/apps/nextjs/src/components/DialogControl/DialogContents.tsx @@ -1,7 +1,7 @@ -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import * as Dialog from "@radix-ui/react-dialog"; import { Box, Flex } from "@radix-ui/themes"; -import { Message } from "ai"; +import type { Message } from "ai"; import { useDialog } from "../AppComponents/DialogContext"; import { Icon } from "../Icon"; diff --git a/apps/nextjs/src/components/ExportsDialogs/exports.helpers.ts b/apps/nextjs/src/components/ExportsDialogs/exports.helpers.ts index feab9db88..3c5c42159 100644 --- a/apps/nextjs/src/components/ExportsDialogs/exports.helpers.ts +++ b/apps/nextjs/src/components/ExportsDialogs/exports.helpers.ts @@ -1,4 +1,4 @@ -import { ResourceTypeValueType } from "@/lib/avo/Avo"; +import type { ResourceTypeValueType } from "@/lib/avo/Avo"; export type ExportsType = | "lessonSlides" diff --git a/apps/nextjs/src/components/ExportsDialogs/exports.types.ts b/apps/nextjs/src/components/ExportsDialogs/exports.types.ts index a0ed0b0d8..b3ba95042 100644 --- a/apps/nextjs/src/components/ExportsDialogs/exports.types.ts +++ b/apps/nextjs/src/components/ExportsDialogs/exports.types.ts @@ -1,4 +1,4 @@ -import { LessonDeepPartial } from "@oakai/exports"; +import type { LessonDeepPartial } from "@oakai/exports"; export type ExportsHookProps = T & { onStart: () => void; diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts index 6affbc9d9..37aed51c6 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportAdditionalMaterials.ts @@ -1,14 +1,14 @@ import { useEffect, useMemo, useState, useCallback } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; -import { ExportsHookProps } from "./exports.types"; +import type { ExportsHookProps } from "./exports.types"; import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportAdditionalMaterials({ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts b/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts index e52c96c2f..7005f37b1 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportAllLessonAssets.ts @@ -1,14 +1,12 @@ import { useCallback, useEffect, useMemo, useState } from "react"; -import { - LessonDeepPartial, - exportSlidesFullLessonSchema, -} from "@oakai/exports/browser"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonDeepPartial } from "@oakai/exports/browser"; +import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts index 8d3e49a2d..d8ae4ad9c 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonPlanDoc.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocLessonPlanSchema } from "@oakai/exports/browser"; -import { LessonPlanDocInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonPlanDocInputData } from "@oakai/exports/src/schema/input.schema"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; -import { ExportsHookProps } from "./exports.types"; +import type { ExportsHookProps } from "./exports.types"; import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportLessonPlanDoc({ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts index f75b10ae8..e43e1d588 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportLessonSlides.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesFullLessonSchema } from "@oakai/exports/browser"; -import { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { LessonSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; -import { ExportsHookProps } from "./exports.types"; +import type { ExportsHookProps } from "./exports.types"; import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportLessonSlides({ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts index 2d9783519..c78b0eae4 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDesignerSlides.ts @@ -1,13 +1,11 @@ import { useEffect, useState } from "react"; -import { - ExportableQuizAppState, - exportableQuizAppStateSchema, -} from "@oakai/exports/src/schema/input.schema"; +import type { ExportableQuizAppState } from "@oakai/exports/src/schema/input.schema"; +import { exportableQuizAppStateSchema } from "@oakai/exports/src/schema/input.schema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts index c867e4131..28d2e103b 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportQuizDoc.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportDocQuizSchema } from "@oakai/exports/browser"; -import { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; +import type { QuizDocInputData } from "@oakai/exports/src/schema/input.schema"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; -import { ExportsHookProps } from "./exports.types"; +import type { ExportsHookProps } from "./exports.types"; import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportQuizDoc({ diff --git a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts index ce83a7d45..1a1ce8056 100644 --- a/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts +++ b/apps/nextjs/src/components/ExportsDialogs/useExportWorksheetSlides.ts @@ -1,14 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from "react"; import { exportSlidesWorksheetSchema } from "@oakai/exports/browser"; -import { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; +import type { WorksheetSlidesInputData } from "@oakai/exports/src/schema/input.schema"; import * as Sentry from "@sentry/nextjs"; import { useDebounce } from "@uidotdev/usehooks"; -import { ZodError } from "zod"; +import type { ZodError } from "zod"; import { trpc } from "@/utils/trpc"; -import { ExportsHookProps } from "./exports.types"; +import type { ExportsHookProps } from "./exports.types"; import { useExportsExistenceCheck } from "./useExportsExistenceCheck"; export function useExportWorksheetSlides({ diff --git a/apps/nextjs/src/components/Feedback/index.tsx b/apps/nextjs/src/components/Feedback/index.tsx index 3ed474a71..61be0aec2 100644 --- a/apps/nextjs/src/components/Feedback/index.tsx +++ b/apps/nextjs/src/components/Feedback/index.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Flex } from "@radix-ui/themes"; -import { Survey } from "posthog-js"; +import type { Survey } from "posthog-js"; import ChatButton from "../AppComponents/Chat/ui/chat-button"; import { Icon } from "../Icon"; diff --git a/apps/nextjs/src/components/Footer.tsx b/apps/nextjs/src/components/Footer.tsx index 6736a84be..3533f3cd7 100644 --- a/apps/nextjs/src/components/Footer.tsx +++ b/apps/nextjs/src/components/Footer.tsx @@ -1,6 +1,7 @@ "use client"; import { useAuth } from "@clerk/nextjs"; +import type { OakIconName } from "@oaknational/oak-components"; import { OakBox, OakFlex, @@ -8,7 +9,6 @@ import { OakMaxWidth, OakSpan, useCookieConsent, - OakIconName, OakP, OakUL, OakLI, diff --git a/apps/nextjs/src/components/FullPageWarning.tsx b/apps/nextjs/src/components/FullPageWarning.tsx index b1acb918b..3bea5a398 100644 --- a/apps/nextjs/src/components/FullPageWarning.tsx +++ b/apps/nextjs/src/components/FullPageWarning.tsx @@ -1,7 +1,8 @@ import React from "react"; import Button from "./Button"; -import { Icon, IconName, IconSize } from "./Icon"; +import type { IconName, IconSize } from "./Icon"; +import { Icon } from "./Icon"; interface FullPageWarningProps { children: React.ReactNode; diff --git a/apps/nextjs/src/components/Icon/svgs.tsx b/apps/nextjs/src/components/Icon/svgs.tsx index ffdcc9399..cc17d4485 100644 --- a/apps/nextjs/src/components/Icon/svgs.tsx +++ b/apps/nextjs/src/components/Icon/svgs.tsx @@ -71,7 +71,7 @@ import reload from "@/assets/svg/reload.svg"; import uploadWhite from "@/assets/svg/upload-white.svg"; import upload from "@/assets/svg/upload.svg"; -import { IconName } from "./types"; +import type { IconName } from "./types"; export const svgs: Record = { "arrow-left": arrowLeft, diff --git a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx index fa0dcfa1b..c1da6d492 100644 --- a/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx +++ b/apps/nextjs/src/components/ImageAltGenerator/ImageRow.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react"; import { aiLogger } from "@oakai/logger"; import { Box, Flex } from "@radix-ui/themes"; -import { Resource } from "ai-apps/image-alt-generation/types"; +import type { Resource } from "ai-apps/image-alt-generation/types"; import encode from "base64url"; import Image from "next/image"; import { useRouter } from "next/navigation"; diff --git a/apps/nextjs/src/components/Input.tsx b/apps/nextjs/src/components/Input.tsx index 1e060a39a..254d25e35 100644 --- a/apps/nextjs/src/components/Input.tsx +++ b/apps/nextjs/src/components/Input.tsx @@ -77,7 +77,7 @@ const Input: React.FC = ({ >, ) => { // Update the type - if (!!onChange) onChange(e); + if (onChange) onChange(e); }; return ( diff --git a/apps/nextjs/src/components/LoadingWheel.tsx b/apps/nextjs/src/components/LoadingWheel.tsx index 2f44c1bf0..0ce8e90f7 100644 --- a/apps/nextjs/src/components/LoadingWheel.tsx +++ b/apps/nextjs/src/components/LoadingWheel.tsx @@ -1,6 +1,7 @@ import React from "react"; -import { Icon, IconName, IconSize } from "./Icon"; +import type { IconName, IconSize } from "./Icon"; +import { Icon } from "./Icon"; const LoadingWheel = React.forwardRef< HTMLDivElement, diff --git a/apps/nextjs/src/components/MainNavigation/MainNavigationMenu.tsx b/apps/nextjs/src/components/MainNavigation/MainNavigationMenu.tsx index 680cb62da..2d657e627 100644 --- a/apps/nextjs/src/components/MainNavigation/MainNavigationMenu.tsx +++ b/apps/nextjs/src/components/MainNavigation/MainNavigationMenu.tsx @@ -1,4 +1,5 @@ -import { Ref, forwardRef } from "react"; +import type { Ref } from "react"; +import { forwardRef } from "react"; import { useAuth } from "@clerk/nextjs"; import { Box, Flex } from "@radix-ui/themes"; @@ -10,7 +11,7 @@ import { usePathname } from "next/navigation"; import loopingLine from "@/assets/svg/looping-line-1.svg"; import useAnalytics from "@/lib/analytics/useAnalytics"; -import { MainNavigationProps } from "."; +import type { MainNavigationProps } from "."; import Button from "../Button"; import { Icon } from "../Icon"; import { MobileListItem } from "./MobileListItem"; diff --git a/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx b/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx index c19c0f4fd..07587bcf7 100644 --- a/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx +++ b/apps/nextjs/src/components/MainNavigation/MobileListItem.tsx @@ -1,4 +1,4 @@ -import { MenuItem } from "data/menus"; +import type { MenuItem } from "data/menus"; import { usePathname } from "next/navigation"; import Button from "../Button"; diff --git a/apps/nextjs/src/components/MainNavigation/index.tsx b/apps/nextjs/src/components/MainNavigation/index.tsx index ed8553745..ff342ef85 100644 --- a/apps/nextjs/src/components/MainNavigation/index.tsx +++ b/apps/nextjs/src/components/MainNavigation/index.tsx @@ -1,4 +1,5 @@ -import { Dispatch, Fragment } from "react"; +import type { Dispatch } from "react"; +import { Fragment } from "react"; import { Dialog, Transition } from "@headlessui/react"; diff --git a/apps/nextjs/src/components/PortableText/portableTextComponents.tsx b/apps/nextjs/src/components/PortableText/portableTextComponents.tsx index 2eba67a80..f758cd385 100644 --- a/apps/nextjs/src/components/PortableText/portableTextComponents.tsx +++ b/apps/nextjs/src/components/PortableText/portableTextComponents.tsx @@ -1,7 +1,7 @@ "use client"; import { OakHeading, OakLink } from "@oaknational/oak-components"; -import { PortableTextComponents } from "@portabletext/react"; +import type { PortableTextComponents } from "@portabletext/react"; import Link from "next/link"; export const portableTextComponents: PortableTextComponents = { diff --git a/apps/nextjs/src/components/SubjectAndKeyStageSelect/index.tsx b/apps/nextjs/src/components/SubjectAndKeyStageSelect/index.tsx index 7d5146426..4c90a4093 100644 --- a/apps/nextjs/src/components/SubjectAndKeyStageSelect/index.tsx +++ b/apps/nextjs/src/components/SubjectAndKeyStageSelect/index.tsx @@ -1,10 +1,10 @@ import { useEffect, useMemo } from "react"; -import { +import type { KeyStageName, SubjectName, - subjectsAndKeyStages, } from "@oakai/core/src/data/subjectsAndKeyStages"; +import { subjectsAndKeyStages } from "@oakai/core/src/data/subjectsAndKeyStages"; import { Flex } from "@radix-ui/themes"; import Input from "../Input"; @@ -114,7 +114,7 @@ const SubjectSelect = ({ type="dropdown" label="Subject" name="subject" - options={allowedSubjects as string[]} + options={allowedSubjects} onChange={(e) => setSelectedSubject(e.target.value as SubjectName)} value={selectedSubject} /> diff --git a/apps/nextjs/src/data/menus.tsx b/apps/nextjs/src/data/menus.tsx index ac8bebbdf..786df5f08 100644 --- a/apps/nextjs/src/data/menus.tsx +++ b/apps/nextjs/src/data/menus.tsx @@ -1,4 +1,4 @@ -import { IconName } from "@/components/Icon"; +import type { IconName } from "@/components/Icon"; interface SocialItem { icon: IconName; diff --git a/apps/nextjs/src/hooks/surveys/useModerationFeedbackSurvey.ts b/apps/nextjs/src/hooks/surveys/useModerationFeedbackSurvey.ts index 9b16eb7e6..4319a560e 100644 --- a/apps/nextjs/src/hooks/surveys/useModerationFeedbackSurvey.ts +++ b/apps/nextjs/src/hooks/surveys/useModerationFeedbackSurvey.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { getSafetyResult } from "@oakai/core/src/utils/ailaModeration/helpers"; -import { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts b/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts index 279a9bee8..8f6a04eb3 100644 --- a/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts +++ b/apps/nextjs/src/hooks/surveys/usePosthogFeedbackSurvey.ts @@ -2,7 +2,7 @@ import { useEffect, useState, useCallback } from "react"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/react"; -import { Survey } from "posthog-js"; +import type { Survey } from "posthog-js"; import useAnalytics from "@/lib/analytics/useAnalytics"; diff --git a/apps/nextjs/src/hooks/useDemoLocking.ts b/apps/nextjs/src/hooks/useDemoLocking.ts index 263723eaa..d0d68c40e 100644 --- a/apps/nextjs/src/hooks/useDemoLocking.ts +++ b/apps/nextjs/src/hooks/useDemoLocking.ts @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; -import { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; -import { Message } from "ai"; +import type { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; +import type { Message } from "ai"; import { useDemoUser } from "@/components/ContextProviders/Demo"; diff --git a/apps/nextjs/src/hooks/useGeneration.ts b/apps/nextjs/src/hooks/useGeneration.ts index 604354c86..b316a82fc 100644 --- a/apps/nextjs/src/hooks/useGeneration.ts +++ b/apps/nextjs/src/hooks/useGeneration.ts @@ -1,6 +1,6 @@ import { useCallback, useEffect, useReducer } from "react"; -import { RateLimitInfo } from "@oakai/api/src/types"; +import type { RateLimitInfo } from "@oakai/api/src/types"; import type { SerializedGeneration } from "@oakai/core/src/models/serializers"; import { GenerationStatus } from "@oakai/db/prisma/client"; import { aiLogger } from "@oakai/logger"; @@ -9,11 +9,12 @@ import { default as logger, } from "@oakai/logger/browser"; import { TRPCClientError } from "@trpc/client"; -import { z } from "zod"; +import type { z } from "zod"; -import { DeepPartial } from "@/utils/types/DeepPartial"; +import type { DeepPartial } from "@/utils/types/DeepPartial"; -import { RouterInputs, trpc } from "../utils/trpc"; +import type { RouterInputs } from "../utils/trpc"; +import { trpc } from "../utils/trpc"; import { useDidTransition } from "./useDidTransition"; const log = aiLogger("generation"); diff --git a/apps/nextjs/src/hooks/useGenerationCallbacks.ts b/apps/nextjs/src/hooks/useGenerationCallbacks.ts index 8f5a7b016..fb25cf64d 100644 --- a/apps/nextjs/src/hooks/useGenerationCallbacks.ts +++ b/apps/nextjs/src/hooks/useGenerationCallbacks.ts @@ -1,17 +1,19 @@ import { useCallback, useEffect } from "react"; import * as Sentry from "@sentry/nextjs"; -import { +import type { AdditionalUseGenerationOptions, FailedGenerationState, GeneratingGenerationState, PendingGenerationState, SuccessfulGenerationState, +} from "hooks/useGeneration"; +import { UseGenerationError, UseGenerationStatus, useGeneration, } from "hooks/useGeneration"; -import { z } from "zod"; +import type { z } from "zod"; import useAnalytics from "@/lib/analytics/useAnalytics"; import { usePreviousValue } from "./usePreviousValue"; diff --git a/apps/nextjs/src/hooks/useMobileLessonPullOutControl.ts b/apps/nextjs/src/hooks/useMobileLessonPullOutControl.ts index 9cc76c58e..e39bdb4a8 100644 --- a/apps/nextjs/src/hooks/useMobileLessonPullOutControl.ts +++ b/apps/nextjs/src/hooks/useMobileLessonPullOutControl.ts @@ -1,9 +1,9 @@ import { useEffect, useState } from "react"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; -import { Message } from "ai"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { Message } from "ai"; -import { AilaStreamingStatus } from "@/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; +import type { AilaStreamingStatus } from "@/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus"; export const useMobileLessonPullOutControl = ({ ailaStreamingStatus, diff --git a/apps/nextjs/src/hooks/useQuizSession.ts b/apps/nextjs/src/hooks/useQuizSession.ts index 359725847..31bcc8398 100644 --- a/apps/nextjs/src/hooks/useQuizSession.ts +++ b/apps/nextjs/src/hooks/useQuizSession.ts @@ -1,14 +1,14 @@ -import { Dispatch, useEffect, useRef } from "react"; +import type { Dispatch } from "react"; +import { useEffect, useRef } from "react"; import { useUser } from "@clerk/nextjs"; import browserLogger from "@oakai/logger/browser"; import * as Sentry from "@sentry/nextjs"; import { parseLocalStorageData } from "ai-apps/common/parseLocalStorageData"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import { z } from "zod"; import { trpc } from "@/utils/trpc"; diff --git a/apps/nextjs/src/hooks/useRegenerateAnswers.ts b/apps/nextjs/src/hooks/useRegenerateAnswers.ts index 9b70a99f7..35fff7d90 100644 --- a/apps/nextjs/src/hooks/useRegenerateAnswers.ts +++ b/apps/nextjs/src/hooks/useRegenerateAnswers.ts @@ -1,11 +1,10 @@ -import { Dispatch, useCallback } from "react"; +import type { Dispatch } from "react"; +import { useCallback } from "react"; import { quizRequestGeneration } from "ai-apps/quiz-designer/quizRequestGeneration"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; diff --git a/apps/nextjs/src/hooks/useRegenerateDistractors.ts b/apps/nextjs/src/hooks/useRegenerateDistractors.ts index b937135b7..d8c7466e5 100644 --- a/apps/nextjs/src/hooks/useRegenerateDistractors.ts +++ b/apps/nextjs/src/hooks/useRegenerateDistractors.ts @@ -1,11 +1,10 @@ -import { Dispatch, useCallback } from "react"; +import type { Dispatch } from "react"; +import { useCallback } from "react"; import { quizRequestGeneration } from "ai-apps/quiz-designer/quizRequestGeneration"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState, QuizAppStateQuestion, } from "ai-apps/quiz-designer/state/types"; diff --git a/apps/nextjs/src/hooks/useSuggestedQuestions.ts b/apps/nextjs/src/hooks/useSuggestedQuestions.ts index 33b03a9f1..4a2c8f192 100644 --- a/apps/nextjs/src/hooks/useSuggestedQuestions.ts +++ b/apps/nextjs/src/hooks/useSuggestedQuestions.ts @@ -1,11 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import { extraQuizPromptInfo } from "ai-apps/quiz-designer/extraQuizPromptInfo"; -import { - QuizAppAction, - QuizAppActions, -} from "ai-apps/quiz-designer/state/actions"; -import { QuizAppState, QuizAppStatus } from "ai-apps/quiz-designer/state/types"; +import type { QuizAppAction } from "ai-apps/quiz-designer/state/actions"; +import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; +import type { QuizAppState } from "ai-apps/quiz-designer/state/types"; +import { QuizAppStatus } from "ai-apps/quiz-designer/state/types"; import { z } from "zod"; import { answersAndDistractorOutputSchema } from "@/components/AppComponents/QuizDesigner/QuizQuestionRow"; diff --git a/apps/nextjs/src/hooks/useTemporaryLessonPlanWithStreamingEdits.ts b/apps/nextjs/src/hooks/useTemporaryLessonPlanWithStreamingEdits.ts index 191857a78..d1a62112e 100644 --- a/apps/nextjs/src/hooks/useTemporaryLessonPlanWithStreamingEdits.ts +++ b/apps/nextjs/src/hooks/useTemporaryLessonPlanWithStreamingEdits.ts @@ -1,11 +1,11 @@ import { useEffect, useRef, useMemo } from "react"; +import type { PatchDocument } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { - PatchDocument, applyLessonPlanPatch, extractPatches, } from "@oakai/aila/src/protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { useThrottle } from "@uidotdev/usehooks"; import { type Message } from "ai/react"; import { deepClone } from "fast-json-patch"; diff --git a/apps/nextjs/src/lib/analytics/helpers.ts b/apps/nextjs/src/lib/analytics/helpers.ts index 0d1d6e08a..000ee732a 100644 --- a/apps/nextjs/src/lib/analytics/helpers.ts +++ b/apps/nextjs/src/lib/analytics/helpers.ts @@ -1,13 +1,13 @@ -import { ModerationDocument } from "@oakai/aila/src/protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { ModerationDocument } from "@oakai/aila/src/protocol/jsonPatchProtocol"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { lessonPlanSectionsSchema } from "@oakai/exports/src/schema/input.schema"; import { isTruthy } from "remeda"; -import { - ModeratedContentType, +import type { ModeratedContentTypeValueType, ProductValueType, } from "../avo/Avo"; +import { ModeratedContentType } from "../avo/Avo"; /** * These are the actions which a user could take which result in a message diff --git a/apps/nextjs/src/lib/analytics/hubspot/HubspotClient.ts b/apps/nextjs/src/lib/analytics/hubspot/HubspotClient.ts index 614fb28e2..1f0013a60 100644 --- a/apps/nextjs/src/lib/analytics/hubspot/HubspotClient.ts +++ b/apps/nextjs/src/lib/analytics/hubspot/HubspotClient.ts @@ -1,4 +1,4 @@ -import { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; +import type { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; import { ServicePolicyMap } from "@/lib/cookie-consent/ServicePolicyMap"; import { consentClient } from "@/lib/cookie-consent/consentClient"; diff --git a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx index ab996e3c6..ecd0a440f 100644 --- a/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx +++ b/apps/nextjs/src/lib/analytics/hubspot/HubspotLoader.tsx @@ -1,10 +1,11 @@ -import { FC, useEffect } from "react"; +import type { FC } from "react"; +import { useEffect } from "react"; import * as Sentry from "@sentry/nextjs"; import { usePathname, useSearchParams } from "next/navigation"; import Script from "next/script"; -import { ConsentState } from "@/components/ContextProviders/CookieConsentProvider"; +import type { ConsentState } from "@/components/ContextProviders/CookieConsentProvider"; import { usePersistUtmParams } from "../useUtmParams"; diff --git a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx index 9c3436335..3d2412218 100644 --- a/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx +++ b/apps/nextjs/src/lib/analytics/lessonPlanTrackingContext.tsx @@ -1,6 +1,6 @@ +import type { FC } from "react"; import { createContext, - FC, useCallback, useContext, useEffect, @@ -9,10 +9,10 @@ import { } from "react"; import { getLastAssistantMessage } from "@oakai/aila/src/helpers/chat/getLastAssistantMessage"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; -import { Message } from "ai"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { Message } from "ai"; -import { UserAction } from "./helpers"; +import type { UserAction } from "./helpers"; import { trackLessonPlanRefined } from "./trackLessonPlanRefined"; import useAnalytics from "./useAnalytics"; diff --git a/apps/nextjs/src/lib/analytics/trackLessonPlanRefined.ts b/apps/nextjs/src/lib/analytics/trackLessonPlanRefined.ts index 0f1c8c48e..6c08a2c74 100644 --- a/apps/nextjs/src/lib/analytics/trackLessonPlanRefined.ts +++ b/apps/nextjs/src/lib/analytics/trackLessonPlanRefined.ts @@ -1,5 +1,5 @@ import { parseMessageParts } from "@oakai/aila/src/protocol/jsonPatchProtocol"; -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { isToxic } from "@oakai/core/src/utils/ailaModeration/helpers"; import * as Sentry from "@sentry/nextjs"; @@ -8,11 +8,12 @@ import { isModeration, isAccountLocked, } from "@/components/AppComponents/Chat/chat-message/protocol"; -import { TrackFns } from "@/components/ContextProviders/AnalyticsProvider"; +import type { TrackFns } from "@/components/ContextProviders/AnalyticsProvider"; -import { ComponentType, ComponentTypeValueType } from "../avo/Avo"; +import type { ComponentTypeValueType } from "../avo/Avo"; +import { ComponentType } from "../avo/Avo"; +import type { UserAction } from "./helpers"; import { - UserAction, getLessonTrackingProps, getModerationTypes, isLessonComplete, diff --git a/apps/nextjs/src/lib/analytics/useAnalyticsService.ts b/apps/nextjs/src/lib/analytics/useAnalyticsService.ts index 552750387..676870543 100644 --- a/apps/nextjs/src/lib/analytics/useAnalyticsService.ts +++ b/apps/nextjs/src/lib/analytics/useAnalyticsService.ts @@ -1,12 +1,12 @@ import { useEffect, useState } from "react"; -import { +import type { AnalyticsService, ServiceName, } from "@/components/ContextProviders/AnalyticsProvider"; -import { ConsentState } from "@/components/ContextProviders/CookieConsentProvider"; +import type { ConsentState } from "@/components/ContextProviders/CookieConsentProvider"; -import { MaybeDistinctId } from "../posthog/posthog"; +import type { MaybeDistinctId } from "../posthog/posthog"; export const useAnalyticsService = ({ service, diff --git a/apps/nextjs/src/lib/avo/getAvoBridge.ts b/apps/nextjs/src/lib/avo/getAvoBridge.ts index 9cba48f8d..655a29edc 100644 --- a/apps/nextjs/src/lib/avo/getAvoBridge.ts +++ b/apps/nextjs/src/lib/avo/getAvoBridge.ts @@ -4,10 +4,10 @@ import browserLogger from "@oakai/logger/browser"; import * as Sentry from "@sentry/nextjs"; -import { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; +import type { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; -import { PosthogConfig } from "../posthog/posthog"; -import { CustomDestination } from "./Avo"; +import type { PosthogConfig } from "../posthog/posthog"; +import type { CustomDestination } from "./Avo"; type AnalyticsServices = { posthog: Pick, "track">; diff --git a/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts b/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts index a0e18facd..cc6d9c783 100644 --- a/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts +++ b/apps/nextjs/src/lib/lessonPlan/sectionsInOrder.ts @@ -1,4 +1,4 @@ -import { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; +import type { LessonPlanKeys } from "@oakai/aila/src/protocol/schema"; export const allSectionsInOrder: LessonPlanKeys[] = [ "learningOutcome", diff --git a/apps/nextjs/src/lib/posthog/posthog.ts b/apps/nextjs/src/lib/posthog/posthog.ts index 9a2fe4245..ea20b44b8 100644 --- a/apps/nextjs/src/lib/posthog/posthog.ts +++ b/apps/nextjs/src/lib/posthog/posthog.ts @@ -1,6 +1,6 @@ -import { PostHog } from "posthog-js"; +import type { PostHog } from "posthog-js"; -import { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; +import type { AnalyticsService } from "@/components/ContextProviders/AnalyticsProvider"; import { ServicePolicyMap } from "../cookie-consent/ServicePolicyMap"; import { consentClient } from "../cookie-consent/consentClient"; diff --git a/apps/nextjs/src/lib/sentry/sentrySetUser.ts b/apps/nextjs/src/lib/sentry/sentrySetUser.ts index a577dc152..ee94ea21c 100644 --- a/apps/nextjs/src/lib/sentry/sentrySetUser.ts +++ b/apps/nextjs/src/lib/sentry/sentrySetUser.ts @@ -1,4 +1,4 @@ -import { ClerkMiddlewareAuthObject } from "@clerk/nextjs/server"; +import type { ClerkMiddlewareAuthObject } from "@clerk/nextjs/server"; import * as Sentry from "@sentry/nextjs"; export function sentrySetUser({ userId }: ClerkMiddlewareAuthObject) { diff --git a/apps/nextjs/src/lib/sentry/withSentry.ts b/apps/nextjs/src/lib/sentry/withSentry.ts index 019516b69..dfdb64ac3 100644 --- a/apps/nextjs/src/lib/sentry/withSentry.ts +++ b/apps/nextjs/src/lib/sentry/withSentry.ts @@ -1,6 +1,6 @@ import { auth } from "@clerk/nextjs/server"; import * as Sentry from "@sentry/node"; -import { NextRequest, NextResponse } from "next/server"; +import type { NextRequest, NextResponse } from "next/server"; import { sentryCleanup } from "./sentryCleanup"; import { sentrySetUser } from "./sentrySetUser"; diff --git a/apps/nextjs/src/middleware.test.ts b/apps/nextjs/src/middleware.test.ts index d79e59180..29b6041c3 100644 --- a/apps/nextjs/src/middleware.test.ts +++ b/apps/nextjs/src/middleware.test.ts @@ -1,12 +1,9 @@ -import { NextRequest, NextFetchEvent, NextResponse } from "next/server"; +import type { NextFetchEvent } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; import { handleError } from "./middleware"; -import { - CspConfig, - CspEnvironment, - addCspHeaders, - buildCspHeaders, -} from "./middlewares/csp"; +import type { CspConfig, CspEnvironment } from "./middlewares/csp"; +import { addCspHeaders, buildCspHeaders } from "./middlewares/csp"; describe("handleError", () => { let mockRequest: NextRequest; diff --git a/apps/nextjs/src/middleware.ts b/apps/nextjs/src/middleware.ts index 128c9c026..5a4f0aacd 100644 --- a/apps/nextjs/src/middleware.ts +++ b/apps/nextjs/src/middleware.ts @@ -1,15 +1,12 @@ -import { NextMiddlewareResult } from "next/dist/server/web/types"; -import { - NextFetchEvent, - NextMiddleware, - NextRequest, - NextResponse, -} from "next/server"; +import type { NextMiddlewareResult } from "next/dist/server/web/types"; +import type { NextFetchEvent, NextMiddleware, NextRequest } from "next/server"; +import { NextResponse } from "next/server"; import { getRootErrorCause } from "./lib/errors/getRootErrorCause"; import { sentryCleanup } from "./lib/sentry/sentryCleanup"; import { authMiddleware } from "./middlewares/auth.middleware"; -import { CspConfig, addCspHeaders } from "./middlewares/csp"; +import type { CspConfig } from "./middlewares/csp"; +import { addCspHeaders } from "./middlewares/csp"; import { logError } from "./middlewares/middlewareErrorLogging"; const parseEnvironment = ( diff --git a/apps/nextjs/src/middlewares/auth.middleware.ts b/apps/nextjs/src/middlewares/auth.middleware.ts index 12a4bd937..f67bbb0ba 100644 --- a/apps/nextjs/src/middlewares/auth.middleware.ts +++ b/apps/nextjs/src/middlewares/auth.middleware.ts @@ -1,10 +1,8 @@ -import { - ClerkMiddlewareAuth, - clerkMiddleware, - createRouteMatcher, -} from "@clerk/nextjs/server"; +import type { ClerkMiddlewareAuth } from "@clerk/nextjs/server"; +import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server"; import { aiLogger } from "@oakai/logger"; -import { NextFetchEvent, NextRequest, NextResponse } from "next/server"; +import type { NextFetchEvent, NextRequest } from "next/server"; +import { NextResponse } from "next/server"; import { sentrySetUser } from "@/lib/sentry/sentrySetUser"; diff --git a/apps/nextjs/src/middlewares/csp.test.ts b/apps/nextjs/src/middlewares/csp.test.ts index b0e934ea8..7850f55d5 100644 --- a/apps/nextjs/src/middlewares/csp.test.ts +++ b/apps/nextjs/src/middlewares/csp.test.ts @@ -2,7 +2,8 @@ // pnpm --filter @oakai/nextjs test -- -u src/middlewares/csp.test.ts import { NextRequest } from "next/server"; -import { addCspHeaders, CspConfig, CspEnvironment } from "./csp"; +import type { CspConfig, CspEnvironment } from "./csp"; +import { addCspHeaders } from "./csp"; const environments = ["development", "production", "preview", "test"] as const; diff --git a/apps/nextjs/src/middlewares/csp.ts b/apps/nextjs/src/middlewares/csp.ts index 052993cc9..a00611977 100644 --- a/apps/nextjs/src/middlewares/csp.ts +++ b/apps/nextjs/src/middlewares/csp.ts @@ -1,4 +1,5 @@ -import { NextRequest, NextResponse } from "next/server"; +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; import { v4 as uuidv4 } from "uuid"; function generateNonce(): string { @@ -169,7 +170,7 @@ export const buildCspHeaders = (nonce: string, config: CspConfig) => { ]; for (const policyObject of additionalPolicies) { - const policyValue = policyObject[policy as keyof typeof policyObject]; + const policyValue = policyObject[policy]; if (Array.isArray(policyValue)) { value.push(...policyValue); } diff --git a/apps/nextjs/src/middlewares/middlewareErrorLogging.ts b/apps/nextjs/src/middlewares/middlewareErrorLogging.ts index 56b725aaf..2ca71e848 100644 --- a/apps/nextjs/src/middlewares/middlewareErrorLogging.ts +++ b/apps/nextjs/src/middlewares/middlewareErrorLogging.ts @@ -1,6 +1,6 @@ import { structuredLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; -import { NextFetchEvent, NextRequest } from "next/server"; +import type { NextFetchEvent, NextRequest } from "next/server"; export async function logError( rootError: unknown, diff --git a/apps/nextjs/src/mocks/analytics/provider.tsx b/apps/nextjs/src/mocks/analytics/provider.tsx index a8659afbb..837784288 100644 --- a/apps/nextjs/src/mocks/analytics/provider.tsx +++ b/apps/nextjs/src/mocks/analytics/provider.tsx @@ -5,7 +5,7 @@ See the readme for why this is needed. import React from "react"; import { aiLogger } from "@oakai/logger"; -import { PostHog } from "posthog-js"; +import type { PostHog } from "posthog-js"; import Avo from "@/lib/avo/Avo"; diff --git a/apps/nextjs/src/utils/alphabetiseArray.ts b/apps/nextjs/src/utils/alphabetiseArray.ts index 768ba9d5d..e8816c69f 100644 --- a/apps/nextjs/src/utils/alphabetiseArray.ts +++ b/apps/nextjs/src/utils/alphabetiseArray.ts @@ -1,7 +1,7 @@ /** * Alphabetizes an array of comparable elements in-place */ -import { +import type { GenerationPart, GenerationPartPlaceholder, } from "@oakai/core/src/types"; diff --git a/apps/nextjs/src/utils/scrollToRef.ts b/apps/nextjs/src/utils/scrollToRef.ts index b07de0797..0d66b1230 100644 --- a/apps/nextjs/src/utils/scrollToRef.ts +++ b/apps/nextjs/src/utils/scrollToRef.ts @@ -1,4 +1,4 @@ -import { RefObject } from "react"; +import type { RefObject } from "react"; import { aiLogger } from "@oakai/logger"; diff --git a/apps/nextjs/src/utils/trackDownload.ts b/apps/nextjs/src/utils/trackDownload.ts index 1e867de0a..e74541758 100644 --- a/apps/nextjs/src/utils/trackDownload.ts +++ b/apps/nextjs/src/utils/trackDownload.ts @@ -1,8 +1,8 @@ -import { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { getLessonTrackingProps } from "@/lib/analytics/helpers"; -import useAnalytics from "@/lib/analytics/useAnalytics"; -import { +import type useAnalytics from "@/lib/analytics/useAnalytics"; +import type { ResourceFileTypeValueType, ResourceTypeValueType, } from "@/lib/avo/Avo"; diff --git a/apps/nextjs/src/utils/trpc.tsx b/apps/nextjs/src/utils/trpc.tsx index d016e0962..d971cd801 100644 --- a/apps/nextjs/src/utils/trpc.tsx +++ b/apps/nextjs/src/utils/trpc.tsx @@ -6,9 +6,10 @@ import { type AppRouter } from "@oakai/api/src/router"; import { type ChatAppRouter } from "@oakai/api/src/router/chat"; import { transformer } from "@oakai/api/transformer"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { TRPCLink, httpBatchLink, loggerLink } from "@trpc/client"; +import type { TRPCLink } from "@trpc/client"; +import { httpBatchLink, loggerLink } from "@trpc/client"; import { createTRPCReact } from "@trpc/react-query"; -import { inferRouterInputs } from "@trpc/server"; +import type { inferRouterInputs } from "@trpc/server"; const getBaseUrl = () => { if (typeof window !== "undefined") return ""; // browser should use relative url From bc269484ed8857b10aac36e06a0dab18c8308277 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 14:17:51 +0000 Subject: [PATCH 043/127] fix: set VS Code to prefer type imports (#291) --- .vscode/settings.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 23f2c7ab4..3b3c7b68e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -194,5 +194,6 @@ "yaml.schemas": { "file:///c%3A/Users/Simon/.vscode/extensions/atlassian.atlascode-2.8.6/resources/schemas/pipelines-schema.json": "bitbucket-pipelines.yml", "https://www.artillery.io/schema.json": [] - } + }, + "typescript.preferences.preferTypeOnlyAutoImports": true } From 0b2b7c54a485a4e5409331434543c14c22756a58 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 14:18:17 +0000 Subject: [PATCH 044/127] chore: incrementally move Core not to use barrel files (#290) --- apps/nextjs/src/app/api/chat/config.ts | 2 +- .../src/app/api/chat/errorHandling.test.ts | 9 ++-- apps/nextjs/src/app/api/chat/errorHandling.ts | 2 +- apps/nextjs/src/app/api/chat/user.test.ts | 5 +- apps/nextjs/src/app/api/chat/user.ts | 2 +- .../src/app/api/chat/webActionsPlugin.test.ts | 11 ++-- .../src/app/api/chat/webActionsPlugin.ts | 10 ++-- .../src/app/api/sanitizeFilename.test.ts | 4 +- .../moderation/moderationErrorHandling.ts | 2 +- packages/api/src/context.ts | 2 +- packages/api/src/trpc.ts | 2 +- packages/core/index.ts | 2 +- .../functions/demo/populateDemoStatuses.ts | 2 +- .../lesson/generateLessonQuizEmbeddings.ts | 2 +- .../core/src/functions/lesson/generatePlan.ts | 2 +- .../core/src/functions/lesson/summarise.ts | 2 +- .../core/src/functions/lesson/summariseAll.ts | 2 +- .../core/src/functions/lessonPlan/embed.ts | 2 +- .../core/src/functions/lessonPlan/embedAll.ts | 2 +- .../src/functions/lessonPlan/embedAllParts.ts | 2 +- .../src/functions/lessonPlan/embedPart.ts | 2 +- .../src/functions/lessonPlan/generateAll.ts | 2 +- .../core/src/functions/lessonPlan/process.ts | 2 +- .../core/src/functions/lessonSummary/embed.ts | 2 +- .../src/functions/lessonSummary/embedAll.ts | 2 +- .../functions/migrations/addIdsToMessages.ts | 2 +- .../functions/migrations/kvChatsToPrisma.ts | 2 +- .../core/src/functions/quizAnswer/embedAll.ts | 2 +- .../generateQuizAnswerEmbeddings.ts | 2 +- .../src/functions/quizQuestion/embedAll.ts | 2 +- .../src/functions/quizQuestion/generateAll.ts | 2 +- .../generateQuizQuestionEmbeddings.ts | 2 +- .../src/functions/slack/notifyModeration.ts | 2 +- .../src/functions/slack/notifyRateLimit.ts | 2 +- .../core/src/functions/slack/notifyUserBan.ts | 2 +- .../core/src/functions/snippet/embedAll.ts | 2 +- .../snippet/generateForAllQuestions.ts | 2 +- .../snippet/generateSnippetEmbeddings.ts | 2 +- .../statistics/recalculateStatistics.ts | 2 +- .../subject/embedSubjectLessonQuizzes.ts | 2 +- .../subject/embedSubjectLessonTranscripts.ts | 2 +- .../subject/generatePlansForSubjectLessons.ts | 2 +- .../subject/summariseSubjectLessons.ts | 2 +- .../generateTranscriptEmbeddings.ts | 2 +- packages/core/src/inngest/index.ts | 51 +++++++++++++++++++ packages/core/src/models/lessonPlans.ts | 2 +- packages/core/src/models/lessonSummaries.ts | 2 +- packages/core/src/models/lessons.ts | 2 +- packages/core/src/models/quizAnswers.ts | 2 +- packages/core/src/models/quizQuestions.ts | 2 +- packages/core/src/models/safetyViolations.ts | 9 +--- packages/core/src/models/snippets.ts | 2 +- packages/core/src/models/userBannedError.ts | 5 ++ packages/core/src/scripts/processLessons.ts | 2 +- .../core/src/utils/camelCaseConversion.ts | 13 +++++ 55 files changed, 138 insertions(+), 71 deletions(-) create mode 100644 packages/core/src/inngest/index.ts create mode 100644 packages/core/src/models/userBannedError.ts create mode 100644 packages/core/src/utils/camelCaseConversion.ts diff --git a/apps/nextjs/src/app/api/chat/config.ts b/apps/nextjs/src/app/api/chat/config.ts index 645e827a0..be68098d1 100644 --- a/apps/nextjs/src/app/api/chat/config.ts +++ b/apps/nextjs/src/app/api/chat/config.ts @@ -4,7 +4,7 @@ import { prisma as globalPrisma, type PrismaClientWithAccelerate, } from "@oakai/db"; -import { nanoid } from "ai"; +import { nanoid } from "nanoid"; import { createWebActionsPlugin } from "./webActionsPlugin"; diff --git a/apps/nextjs/src/app/api/chat/errorHandling.test.ts b/apps/nextjs/src/app/api/chat/errorHandling.test.ts index 0a454dc8d..33f618225 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.test.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.test.ts @@ -1,6 +1,7 @@ -import { AilaAuthenticationError, AilaThreatDetectionError } from "@oakai/aila"; +import { AilaAuthenticationError } from "@oakai/aila/src/core/AilaError"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection/types"; import * as moderationErrorHandling from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import type { PrismaClientWithAccelerate } from "@oakai/db"; @@ -37,7 +38,7 @@ describe("handleChatException", () => { expect(response.status).toBe(200); - invariant(response.body instanceof ReadableStream); + invariant(response.body); const message = extractStreamMessage(await consumeStream(response.body)); expect(message).toEqual({ @@ -123,4 +124,4 @@ describe("handleChatException", () => { }); }); }); -}); \ No newline at end of file +}); diff --git a/apps/nextjs/src/app/api/chat/errorHandling.ts b/apps/nextjs/src/app/api/chat/errorHandling.ts index 3bdb68096..4bce0e12b 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.ts @@ -4,7 +4,7 @@ import type { ErrorDocument, } from "@oakai/aila/src/protocol/jsonPatchProtocol"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import type { PrismaClientWithAccelerate } from "@oakai/db"; diff --git a/apps/nextjs/src/app/api/chat/user.test.ts b/apps/nextjs/src/app/api/chat/user.test.ts index 4c9c3cedf..79e139e42 100644 --- a/apps/nextjs/src/app/api/chat/user.test.ts +++ b/apps/nextjs/src/app/api/chat/user.test.ts @@ -1,10 +1,9 @@ -import { inngest } from "@oakai/core"; import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { reportRateLimitError } from "./user"; -jest.mock("@oakai/core/src/client", () => ({ +jest.mock("@oakai/core/src/inngest", () => ({ inngest: { createFunction: jest.fn(), send: jest.fn(), @@ -63,6 +62,8 @@ describe("chat route user functions", () => { await reportRateLimitError(error, userId, chatId); + const { inngest } = await import("@oakai/core/src/inngest"); + expect(inngest.send).toHaveBeenCalledTimes(1); expect(inngest.send).toHaveBeenCalledWith({ name: "app/slack.notifyRateLimit", diff --git a/apps/nextjs/src/app/api/chat/user.ts b/apps/nextjs/src/app/api/chat/user.ts index dfa8bd629..73a19ff42 100644 --- a/apps/nextjs/src/app/api/chat/user.ts +++ b/apps/nextjs/src/app/api/chat/user.ts @@ -2,7 +2,7 @@ import { auth, clerkClient } from "@clerk/nextjs/server"; import { AilaAuthenticationError } from "@oakai/aila"; import { demoUsers, inngest } from "@oakai/core"; import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import { withTelemetry } from "@oakai/core/src/tracing/serverTracing"; import { rateLimits } from "@oakai/core/src/utils/rateLimiting/rateLimit"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts index 76cdd2238..7c82e9b47 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -1,13 +1,14 @@ -import { AilaThreatDetectionError } from "@oakai/aila"; import type { AilaPluginContext } from "@oakai/aila/src/core/plugins"; -import { inngest } from "@oakai/core"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection/types"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import type { Moderation } from "@prisma/client"; import { createWebActionsPlugin } from "./webActionsPlugin"; -jest.mock("@oakai/core", () => ({ +jest.mock("@oakai/core/src/inngest", () => ({ + __esModule: true, + inngest: { send: jest.fn(), }, @@ -66,6 +67,8 @@ describe("webActionsPlugin", () => { enqueue: mockEnqueue, }; + const { inngest } = await import("@oakai/core/src/inngest"); + const plugin = createWebActionsPlugin(prisma, safetyViolations); await plugin.onToxicModeration(moderation, pluginContext); diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 24f69be32..3b24fefeb 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -1,11 +1,9 @@ -import { AilaThreatDetectionError } from "@oakai/aila"; import type { AilaPlugin } from "@oakai/aila/src/core/plugins"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection/types"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; -import { - SafetyViolations as defaultSafetyViolations, - inngest, -} from "@oakai/core"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { inngest } from "@oakai/core/src/inngest"; +import { SafetyViolations as defaultSafetyViolations } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { waitUntil } from "@vercel/functions"; diff --git a/apps/nextjs/src/app/api/sanitizeFilename.test.ts b/apps/nextjs/src/app/api/sanitizeFilename.test.ts index ac8b3d888..e0af33511 100644 --- a/apps/nextjs/src/app/api/sanitizeFilename.test.ts +++ b/apps/nextjs/src/app/api/sanitizeFilename.test.ts @@ -3,9 +3,9 @@ import { sanitizeFilename } from "./sanitizeFilename"; describe("sanitizeFilename", () => { test("should remove special characters", () => { const lessonTitle = - "The econonomy: Boom or Bust? You decide! Let's start, shall we?"; + "The economy: Boom or Bust? You decide! Let's start, shall we?"; expect(sanitizeFilename(lessonTitle)).toBe( - "The econonomy Boom or Bust You decide Lets start shall we", + "The economy Boom or Bust You decide Lets start shall we", ); }); }); diff --git a/packages/aila/src/utils/moderation/moderationErrorHandling.ts b/packages/aila/src/utils/moderation/moderationErrorHandling.ts index 67feb2307..7ff04673e 100644 --- a/packages/aila/src/utils/moderation/moderationErrorHandling.ts +++ b/packages/aila/src/utils/moderation/moderationErrorHandling.ts @@ -1,5 +1,5 @@ import { SafetyViolations as defaultSafetyViolations } from "@oakai/core/src/models/safetyViolations"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import type { AilaThreatDetectionError } from "../../features/threatDetection/types"; diff --git a/packages/api/src/context.ts b/packages/api/src/context.ts index 31fcd29c7..abf9507b0 100644 --- a/packages/api/src/context.ts +++ b/packages/api/src/context.ts @@ -4,7 +4,7 @@ import type { } from "@clerk/backend/internal"; import { getAuth } from "@clerk/nextjs/server"; import { prisma } from "@oakai/db"; -import { type inferAsyncReturnType } from "@trpc/server"; +import type { inferAsyncReturnType } from "@trpc/server"; import type { NodeHTTPCreateContextFnOptions } from "@trpc/server/adapters/node-http"; import type { NextRequest, NextResponse } from "next/server"; diff --git a/packages/api/src/trpc.ts b/packages/api/src/trpc.ts index 6b84936e8..f31c30298 100644 --- a/packages/api/src/trpc.ts +++ b/packages/api/src/trpc.ts @@ -3,7 +3,7 @@ import { initTRPC } from "@trpc/server"; import { ZodError } from "zod"; import { transformer } from "../transformer"; -import { type Context } from "./context"; +import type { Context } from "./context"; const log = aiLogger("trpc"); diff --git a/packages/core/index.ts b/packages/core/index.ts index ef3c66798..597d7e888 100644 --- a/packages/core/index.ts +++ b/packages/core/index.ts @@ -31,7 +31,7 @@ import { generatePlansForSubjectLessons } from "./src/functions/subject/generate import { summariseSubjectLessons } from "./src/functions/subject/summariseSubjectLessons"; import { generateTranscriptEmbeddings } from "./src/functions/transcript/generateTranscriptEmbeddings"; -export { inngest } from "./src/client"; +export { inngest } from "./src/inngest"; export * from "./src/data/subjectsAndKeyStages"; export * from "./src/models"; //export * from "./src/models/promptVariants"; diff --git a/packages/core/src/functions/demo/populateDemoStatuses.ts b/packages/core/src/functions/demo/populateDemoStatuses.ts index 92259733b..356f01e5d 100644 --- a/packages/core/src/functions/demo/populateDemoStatuses.ts +++ b/packages/core/src/functions/demo/populateDemoStatuses.ts @@ -1,7 +1,7 @@ import { clerkClient } from "@clerk/nextjs/server"; import { aiLogger } from "@oakai/logger"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { populateDemoStatusesSchema } from "./populateDemoStatuses.schema"; const log = aiLogger("demo"); diff --git a/packages/core/src/functions/lesson/generateLessonQuizEmbeddings.ts b/packages/core/src/functions/lesson/generateLessonQuizEmbeddings.ts index eeff94321..36f2e4788 100644 --- a/packages/core/src/functions/lesson/generateLessonQuizEmbeddings.ts +++ b/packages/core/src/functions/lesson/generateLessonQuizEmbeddings.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Lessons } from "../../models"; export const generateLessonQuizEmbeddings = inngest.createFunction( diff --git a/packages/core/src/functions/lesson/generatePlan.ts b/packages/core/src/functions/lesson/generatePlan.ts index c3d0c9264..2a2c02232 100644 --- a/packages/core/src/functions/lesson/generatePlan.ts +++ b/packages/core/src/functions/lesson/generatePlan.ts @@ -1,7 +1,7 @@ import { prisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; const log = aiLogger("lessons"); diff --git a/packages/core/src/functions/lesson/summarise.ts b/packages/core/src/functions/lesson/summarise.ts index 42e43fb09..52e27b512 100644 --- a/packages/core/src/functions/lesson/summarise.ts +++ b/packages/core/src/functions/lesson/summarise.ts @@ -1,7 +1,7 @@ import { LessonSummaryStatus, prisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Lessons } from "../../models"; const log = aiLogger("lessons"); diff --git a/packages/core/src/functions/lesson/summariseAll.ts b/packages/core/src/functions/lesson/summariseAll.ts index d16c3f13d..f035f7e7a 100644 --- a/packages/core/src/functions/lesson/summariseAll.ts +++ b/packages/core/src/functions/lesson/summariseAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Lessons } from "../../models"; export const summariseAllLessons = inngest.createFunction( diff --git a/packages/core/src/functions/lessonPlan/embed.ts b/packages/core/src/functions/lessonPlan/embed.ts index 76c1801e5..6fe96c5eb 100644 --- a/packages/core/src/functions/lessonPlan/embed.ts +++ b/packages/core/src/functions/lessonPlan/embed.ts @@ -1,6 +1,6 @@ import { LessonPlanStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; export const embedLessonPlan = inngest.createFunction( diff --git a/packages/core/src/functions/lessonPlan/embedAll.ts b/packages/core/src/functions/lessonPlan/embedAll.ts index c7a09a9f3..9de32b0f2 100644 --- a/packages/core/src/functions/lessonPlan/embedAll.ts +++ b/packages/core/src/functions/lessonPlan/embedAll.ts @@ -1,7 +1,7 @@ import { LessonPlanStatus, prisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; const log = aiLogger("lessons"); diff --git a/packages/core/src/functions/lessonPlan/embedAllParts.ts b/packages/core/src/functions/lessonPlan/embedAllParts.ts index 8efd6fd74..8b9244eea 100644 --- a/packages/core/src/functions/lessonPlan/embedAllParts.ts +++ b/packages/core/src/functions/lessonPlan/embedAllParts.ts @@ -1,6 +1,6 @@ import { LessonPlanPartStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; export const embedAllLessonPlanParts = inngest.createFunction( diff --git a/packages/core/src/functions/lessonPlan/embedPart.ts b/packages/core/src/functions/lessonPlan/embedPart.ts index 8b2fd5429..4545fe7f1 100644 --- a/packages/core/src/functions/lessonPlan/embedPart.ts +++ b/packages/core/src/functions/lessonPlan/embedPart.ts @@ -1,6 +1,6 @@ import { LessonPlanPartStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; export const embedLessonPlanPart = inngest.createFunction( diff --git a/packages/core/src/functions/lessonPlan/generateAll.ts b/packages/core/src/functions/lessonPlan/generateAll.ts index d07d0f76f..ccda36e9c 100644 --- a/packages/core/src/functions/lessonPlan/generateAll.ts +++ b/packages/core/src/functions/lessonPlan/generateAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; export const generateAllLessonPlans = inngest.createFunction( { diff --git a/packages/core/src/functions/lessonPlan/process.ts b/packages/core/src/functions/lessonPlan/process.ts index 634373d85..33b392ff0 100644 --- a/packages/core/src/functions/lessonPlan/process.ts +++ b/packages/core/src/functions/lessonPlan/process.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonPlans } from "../../models"; export const processLessonPlan = inngest.createFunction( diff --git a/packages/core/src/functions/lessonSummary/embed.ts b/packages/core/src/functions/lessonSummary/embed.ts index 8936e5966..eee51a26a 100644 --- a/packages/core/src/functions/lessonSummary/embed.ts +++ b/packages/core/src/functions/lessonSummary/embed.ts @@ -1,6 +1,6 @@ import { LessonSummaryStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonSummaries } from "../../models"; export const embedLessonSummary = inngest.createFunction( diff --git a/packages/core/src/functions/lessonSummary/embedAll.ts b/packages/core/src/functions/lessonSummary/embedAll.ts index e39ba0d19..b1e69392a 100644 --- a/packages/core/src/functions/lessonSummary/embedAll.ts +++ b/packages/core/src/functions/lessonSummary/embedAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { LessonSummaries } from "../../models"; export const embedAllLessonSummaries = inngest.createFunction( diff --git a/packages/core/src/functions/migrations/addIdsToMessages.ts b/packages/core/src/functions/migrations/addIdsToMessages.ts index 2c461d413..5ca7c4577 100644 --- a/packages/core/src/functions/migrations/addIdsToMessages.ts +++ b/packages/core/src/functions/migrations/addIdsToMessages.ts @@ -2,7 +2,7 @@ import { Prisma, prisma } from "@oakai/db"; import crypto from "crypto"; import { z } from "zod"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { addIdsToMessagesSchema } from "./addIdsToMessages.schema"; const chatSchema = z diff --git a/packages/core/src/functions/migrations/kvChatsToPrisma.ts b/packages/core/src/functions/migrations/kvChatsToPrisma.ts index 58a2f7fa8..7861ad6d8 100644 --- a/packages/core/src/functions/migrations/kvChatsToPrisma.ts +++ b/packages/core/src/functions/migrations/kvChatsToPrisma.ts @@ -4,7 +4,7 @@ import { kv } from "@vercel/kv"; import { z } from "zod"; import { LessonPlanSchemaWhilstStreaming } from "../../../../aila/src/protocol/schema"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { kvChatsToPrismaSchema } from "./kvChatsToPrisma.schema"; const chatSchema = z diff --git a/packages/core/src/functions/quizAnswer/embedAll.ts b/packages/core/src/functions/quizAnswer/embedAll.ts index 5cd963c6e..c7054bdc6 100644 --- a/packages/core/src/functions/quizAnswer/embedAll.ts +++ b/packages/core/src/functions/quizAnswer/embedAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { QuizAnswers } from "../../models"; export const embedAllQuizAnswers = inngest.createFunction( diff --git a/packages/core/src/functions/quizAnswer/generateQuizAnswerEmbeddings.ts b/packages/core/src/functions/quizAnswer/generateQuizAnswerEmbeddings.ts index a4006643f..4dcd4f82f 100644 --- a/packages/core/src/functions/quizAnswer/generateQuizAnswerEmbeddings.ts +++ b/packages/core/src/functions/quizAnswer/generateQuizAnswerEmbeddings.ts @@ -1,6 +1,6 @@ import { prisma, QuizAnswerStatus } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { QuizAnswers } from "../../models"; export const generateQuizAnswerEmbeddings = inngest.createFunction( diff --git a/packages/core/src/functions/quizQuestion/embedAll.ts b/packages/core/src/functions/quizQuestion/embedAll.ts index 225643212..c9ee08f52 100644 --- a/packages/core/src/functions/quizQuestion/embedAll.ts +++ b/packages/core/src/functions/quizQuestion/embedAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { QuizQuestions } from "../../models"; export const embedAllQuizQuestions = inngest.createFunction( diff --git a/packages/core/src/functions/quizQuestion/generateAll.ts b/packages/core/src/functions/quizQuestion/generateAll.ts index 7b6715151..2f0cb5a18 100644 --- a/packages/core/src/functions/quizQuestion/generateAll.ts +++ b/packages/core/src/functions/quizQuestion/generateAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { QuizQuestions } from "../../models"; export const generateAllQuizQuestions = inngest.createFunction( diff --git a/packages/core/src/functions/quizQuestion/generateQuizQuestionEmbeddings.ts b/packages/core/src/functions/quizQuestion/generateQuizQuestionEmbeddings.ts index 3ca3ef4e2..b0d144099 100644 --- a/packages/core/src/functions/quizQuestion/generateQuizQuestionEmbeddings.ts +++ b/packages/core/src/functions/quizQuestion/generateQuizQuestionEmbeddings.ts @@ -1,6 +1,6 @@ import { QuizQuestionStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { QuizQuestions } from "../../models"; export const generateQuizQuestionEmbeddings = inngest.createFunction( diff --git a/packages/core/src/functions/slack/notifyModeration.ts b/packages/core/src/functions/slack/notifyModeration.ts index c2c7d4cc3..52bf6d8cd 100644 --- a/packages/core/src/functions/slack/notifyModeration.ts +++ b/packages/core/src/functions/slack/notifyModeration.ts @@ -1,4 +1,4 @@ -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { slackNotificationChannelId, slackWebClient, diff --git a/packages/core/src/functions/slack/notifyRateLimit.ts b/packages/core/src/functions/slack/notifyRateLimit.ts index 596802c7e..c1bae15b9 100644 --- a/packages/core/src/functions/slack/notifyRateLimit.ts +++ b/packages/core/src/functions/slack/notifyRateLimit.ts @@ -1,4 +1,4 @@ -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { slackNotificationChannelId, slackWebClient, diff --git a/packages/core/src/functions/slack/notifyUserBan.ts b/packages/core/src/functions/slack/notifyUserBan.ts index ea543d202..b8d59b554 100644 --- a/packages/core/src/functions/slack/notifyUserBan.ts +++ b/packages/core/src/functions/slack/notifyUserBan.ts @@ -1,4 +1,4 @@ -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { slackNotificationChannelId, slackWebClient, diff --git a/packages/core/src/functions/snippet/embedAll.ts b/packages/core/src/functions/snippet/embedAll.ts index d017f9198..60558bd67 100644 --- a/packages/core/src/functions/snippet/embedAll.ts +++ b/packages/core/src/functions/snippet/embedAll.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Snippets } from "../../models"; export const embedAllSnippets = inngest.createFunction( diff --git a/packages/core/src/functions/snippet/generateForAllQuestions.ts b/packages/core/src/functions/snippet/generateForAllQuestions.ts index 346c7e7b5..ff36f1488 100644 --- a/packages/core/src/functions/snippet/generateForAllQuestions.ts +++ b/packages/core/src/functions/snippet/generateForAllQuestions.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Snippets } from "../../models"; export const generateSnippetsForAllQuestions = inngest.createFunction( diff --git a/packages/core/src/functions/snippet/generateSnippetEmbeddings.ts b/packages/core/src/functions/snippet/generateSnippetEmbeddings.ts index 269264405..2a890d80c 100644 --- a/packages/core/src/functions/snippet/generateSnippetEmbeddings.ts +++ b/packages/core/src/functions/snippet/generateSnippetEmbeddings.ts @@ -1,6 +1,6 @@ import { SnippetStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Snippets } from "../../models"; export const generateSnippetEmbeddings = inngest.createFunction( diff --git a/packages/core/src/functions/statistics/recalculateStatistics.ts b/packages/core/src/functions/statistics/recalculateStatistics.ts index a42084325..54f73ecb5 100644 --- a/packages/core/src/functions/statistics/recalculateStatistics.ts +++ b/packages/core/src/functions/statistics/recalculateStatistics.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Statistics } from "../../models/statistics"; export const recalculateStatistics = inngest.createFunction( diff --git a/packages/core/src/functions/subject/embedSubjectLessonQuizzes.ts b/packages/core/src/functions/subject/embedSubjectLessonQuizzes.ts index 41e354bd2..859249cf9 100644 --- a/packages/core/src/functions/subject/embedSubjectLessonQuizzes.ts +++ b/packages/core/src/functions/subject/embedSubjectLessonQuizzes.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; interface SimplifiedLesson { id: string; diff --git a/packages/core/src/functions/subject/embedSubjectLessonTranscripts.ts b/packages/core/src/functions/subject/embedSubjectLessonTranscripts.ts index c697e3ccf..c4b9254dd 100644 --- a/packages/core/src/functions/subject/embedSubjectLessonTranscripts.ts +++ b/packages/core/src/functions/subject/embedSubjectLessonTranscripts.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; interface SimplifiedLesson { id: string; diff --git a/packages/core/src/functions/subject/generatePlansForSubjectLessons.ts b/packages/core/src/functions/subject/generatePlansForSubjectLessons.ts index 7a1c95e3b..dd734c769 100644 --- a/packages/core/src/functions/subject/generatePlansForSubjectLessons.ts +++ b/packages/core/src/functions/subject/generatePlansForSubjectLessons.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; interface SimplifiedLesson { id: string; diff --git a/packages/core/src/functions/subject/summariseSubjectLessons.ts b/packages/core/src/functions/subject/summariseSubjectLessons.ts index 305c42386..0a79c58d8 100644 --- a/packages/core/src/functions/subject/summariseSubjectLessons.ts +++ b/packages/core/src/functions/subject/summariseSubjectLessons.ts @@ -1,6 +1,6 @@ import { prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; interface SimplifiedLesson { id: string; diff --git a/packages/core/src/functions/transcript/generateTranscriptEmbeddings.ts b/packages/core/src/functions/transcript/generateTranscriptEmbeddings.ts index 20912d10e..65a5e582f 100644 --- a/packages/core/src/functions/transcript/generateTranscriptEmbeddings.ts +++ b/packages/core/src/functions/transcript/generateTranscriptEmbeddings.ts @@ -1,6 +1,6 @@ import { TranscriptStatus, prisma } from "@oakai/db"; -import { inngest } from "../../client"; +import { inngest } from "../../inngest"; import { Transcripts } from "../../models/transcript"; export const generateTranscriptEmbeddings = inngest.createFunction( diff --git a/packages/core/src/inngest/index.ts b/packages/core/src/inngest/index.ts new file mode 100644 index 000000000..9aba0f2c5 --- /dev/null +++ b/packages/core/src/inngest/index.ts @@ -0,0 +1,51 @@ +import { structuredLogger } from "@oakai/logger"; +import { EventSchemas, Inngest } from "inngest"; +import getConfig from "next/config"; + +import types from "../functions/event-types"; +import { eventLogger } from "../middleware/eventLogger"; + +let serverRuntimeConfig; +try { + const { serverRuntimeConfig: nextServerRuntimeConfig } = getConfig(); + serverRuntimeConfig = nextServerRuntimeConfig; +} catch (e) { + //console.log("No Next environment"); +} +const CONTEXT = serverRuntimeConfig?.DEPLOY_CONTEXT; +const BRANCH = serverRuntimeConfig?.BRANCH; + +function getInngestEnv() { + if (CONTEXT === "production") { + return "production"; + } else if (CONTEXT === "deploy-preview" && BRANCH) { + // Naively slugify, removing any non-alphanumeric + return BRANCH.replace(/\W/g, "-"); + } else { + return "development"; + } +} + +function isJestEnvironment() { + return process.env.JEST_WORKER_ID !== undefined; +} + +const inngestEnv = getInngestEnv(); +const inngestEventKey = process.env.INNGEST_EVENT_KEY; + +if (!inngestEventKey) { + throw new Error("Missing env var INNGEST_EVENT_KEY"); +} + +console.log("Inngest env:", inngestEnv); + +export const inngest = new Inngest({ + name: "Oak AI", + id: "oak-ai", + schemas: new EventSchemas().fromZod(types), + eventKey: inngestEventKey, + env: inngestEnv, + logger: structuredLogger, + middleware: [eventLogger(inngestEnv, inngestEventKey)], + isDev: process.env.NODE_ENV === "development" || isJestEnvironment(), +}); diff --git a/packages/core/src/models/lessonPlans.ts b/packages/core/src/models/lessonPlans.ts index ef907ceeb..7505fd60b 100644 --- a/packages/core/src/models/lessonPlans.ts +++ b/packages/core/src/models/lessonPlans.ts @@ -15,7 +15,7 @@ import yaml from "yaml"; import { LLMResponseJsonSchema } from "../../../aila/src/protocol/jsonPatchProtocol"; import { LessonPlanJsonSchema } from "../../../aila/src/protocol/schema"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { createOpenAIClient } from "../llm/openai"; import { template } from "../prompts/lesson-assistant"; import { RAG } from "../rag"; diff --git a/packages/core/src/models/lessonSummaries.ts b/packages/core/src/models/lessonSummaries.ts index 111c935ec..edcae0b98 100644 --- a/packages/core/src/models/lessonSummaries.ts +++ b/packages/core/src/models/lessonSummaries.ts @@ -3,7 +3,7 @@ import { Prisma } from "@prisma/client"; import { OpenAIEmbeddings } from "langchain/embeddings/openai"; import { PrismaVectorStore } from "langchain/vectorstores/prisma"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { RAG } from "../rag"; import { embedWithCache } from "../utils/embeddings"; diff --git a/packages/core/src/models/lessons.ts b/packages/core/src/models/lessons.ts index 945f174e7..981c1e59d 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -16,7 +16,7 @@ import { PromptTemplate } from "langchain/prompts"; import { RunnableSequence } from "langchain/schema/runnable"; import { z } from "zod"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { createOpenAILangchainClient } from "../llm/openai"; import type { SnippetWithLesson} from "./snippets"; import { Snippets } from "./snippets"; diff --git a/packages/core/src/models/quizAnswers.ts b/packages/core/src/models/quizAnswers.ts index 1471c884e..7c2739745 100644 --- a/packages/core/src/models/quizAnswers.ts +++ b/packages/core/src/models/quizAnswers.ts @@ -1,6 +1,6 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { embedWithCache } from "../utils/embeddings"; export class QuizAnswers { diff --git a/packages/core/src/models/quizQuestions.ts b/packages/core/src/models/quizQuestions.ts index ac3229b01..d7ebe51b6 100644 --- a/packages/core/src/models/quizQuestions.ts +++ b/packages/core/src/models/quizQuestions.ts @@ -1,6 +1,6 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { embedWithCache } from "../utils/embeddings"; export class QuizQuestions { diff --git a/packages/core/src/models/safetyViolations.ts b/packages/core/src/models/safetyViolations.ts index f1b4e98ed..823a02d67 100644 --- a/packages/core/src/models/safetyViolations.ts +++ b/packages/core/src/models/safetyViolations.ts @@ -10,7 +10,8 @@ import { structuredLogger } from "@oakai/logger"; import type { Logger as InngestLogger } from "inngest/middleware/logger"; import { posthogAiBetaServerClient } from "../analytics/posthogAiBetaServerClient"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; +import { UserBannedError } from "./userBannedError"; const ALLOWED_VIOLATIONS = parseInt( process.env.SAFETY_VIOLATIONS_MAX_ALLOWED || "5", @@ -22,12 +23,6 @@ const CHECK_WINDOW_DAYS = parseInt( ); const checkWindowMs = 1000 * 60 * 60 * 24 * CHECK_WINDOW_DAYS; -export class UserBannedError extends Error { - constructor(userId: string) { - super(`User banned: ${userId}`); - } -} - /** * SafetyViolations records safety violations and bans users who exceed the * allowed number of violations in a given time window. diff --git a/packages/core/src/models/snippets.ts b/packages/core/src/models/snippets.ts index 32661b9e8..a7bd528d8 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -24,7 +24,7 @@ import { formatDocumentsAsString } from "langchain/util/document"; import { PrismaVectorStore } from "langchain/vectorstores/prisma"; import { difference } from "remeda"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; import { createOpenAILangchainClient } from "../llm/openai"; import { embedWithCache } from "../utils/embeddings"; diff --git a/packages/core/src/models/userBannedError.ts b/packages/core/src/models/userBannedError.ts new file mode 100644 index 000000000..7541c2f88 --- /dev/null +++ b/packages/core/src/models/userBannedError.ts @@ -0,0 +1,5 @@ +export class UserBannedError extends Error { + constructor(userId: string) { + super(`User banned: ${userId}`); + } +} diff --git a/packages/core/src/scripts/processLessons.ts b/packages/core/src/scripts/processLessons.ts index 06e7c20c8..f45348a9b 100644 --- a/packages/core/src/scripts/processLessons.ts +++ b/packages/core/src/scripts/processLessons.ts @@ -1,7 +1,7 @@ // Called with pnpm run process:lessons -- --keyStage=3 --subject=religious-education import invariant from "tiny-invariant"; -import { inngest } from "../client"; +import { inngest } from "../inngest"; const main = async () => { try { diff --git a/packages/core/src/utils/camelCaseConversion.ts b/packages/core/src/utils/camelCaseConversion.ts new file mode 100644 index 000000000..e1a4ae6ac --- /dev/null +++ b/packages/core/src/utils/camelCaseConversion.ts @@ -0,0 +1,13 @@ +export function camelCaseToSentenceCase(str: string) { + return str + .replace(/([A-Z0-9])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()) + .replace(/\s[A-Z]/g, (str) => str.toLowerCase()); +} + +export function camelCaseToTitleCase(str: string) { + return str + .replace(/([A-Z0-9])/g, " $1") + .replace(/^./, (str) => str.toUpperCase()) + .replace(/\s./g, (str) => str.toUpperCase()); +} From bc8ef36921d6d6451eebdc92c986dec4325dd6a5 Mon Sep 17 00:00:00 2001 From: Adam Howard <91115+codeincontext@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:47:22 +0100 Subject: [PATCH 045/127] chore: use DEBUG logging var on the frontend (#275) --- apps/nextjs/.storybook/main.ts | 2 ++ apps/nextjs/jest.setup.js | 2 ++ apps/nextjs/next.config.js | 1 + packages/logger/index.ts | 4 +++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/nextjs/.storybook/main.ts b/apps/nextjs/.storybook/main.ts index 53b58e031..0b2d01b6e 100644 --- a/apps/nextjs/.storybook/main.ts +++ b/apps/nextjs/.storybook/main.ts @@ -2,6 +2,8 @@ import type { StorybookConfig } from "@storybook/nextjs"; import { join, dirname, resolve } from "path"; import webpack from "webpack"; +process.env.NEXT_PUBLIC_DEBUG = process.env.DEBUG; + /** * This function is used to resolve the absolute path of a package. * It is needed in projects that use Yarn PnP or are set up within a monorepo. diff --git a/apps/nextjs/jest.setup.js b/apps/nextjs/jest.setup.js index 61958adc9..f6a95f743 100644 --- a/apps/nextjs/jest.setup.js +++ b/apps/nextjs/jest.setup.js @@ -1,5 +1,7 @@ require("@testing-library/jest-dom"); +process.env.NEXT_PUBLIC_DEBUG = process.env.DEBUG; + // Mock Next.js Image component jest.mock("next/image", () => ({ __esModule: true, diff --git a/apps/nextjs/next.config.js b/apps/nextjs/next.config.js index 3f27d92fd..4cc207922 100644 --- a/apps/nextjs/next.config.js +++ b/apps/nextjs/next.config.js @@ -88,6 +88,7 @@ const getConfig = async (phase) => { env: { NEXT_PUBLIC_APP_VERSION: appVersion, NEXT_PUBLIC_RELEASE_STAGE: releaseStage, + NEXT_PUBLIC_DEBUG: process.env.DEBUG, }, productionBrowserSourceMaps: true, diff --git a/packages/logger/index.ts b/packages/logger/index.ts index 0b7b57563..fcc81e334 100644 --- a/packages/logger/index.ts +++ b/packages/logger/index.ts @@ -1,11 +1,13 @@ import debug from "debug"; +import invariant from "tiny-invariant"; import browserLogger from "./browser"; import type { StructuredLogger } from "./structuredLogger"; import structuredLogger from "./structuredLogger"; if (typeof window !== "undefined") { - debug.enable("ai:*"); + invariant(process.env.NEXT_PUBLIC_DEBUG, "NEXT_PUBLIC_DEBUG is not set"); + debug.enable(process.env.NEXT_PUBLIC_DEBUG); } const debugBase = debug("ai"); From 548b85d1db119543cb8fa2887d1593eb36b22bb7 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 15:00:18 +0000 Subject: [PATCH 046/127] chore: incrementally move to Aila not using barrel files (#289) --- .vscode/settings.json | 9 +++ apps/nextjs/src/app/api/chat/user.test.ts | 4 +- apps/nextjs/src/app/api/chat/user.ts | 2 +- .../src/app/api/chat/webActionsPlugin.test.ts | 1 + .../src/app/api/chat/webActionsPlugin.ts | 4 +- packages/aila/package.json | 1 + .../aila/src/core/Aila.liveWithOpenAI.test.ts | 4 +- packages/aila/src/core/Aila.test.ts | 10 ++- packages/aila/src/core/Aila.testHelpers.ts | 2 +- packages/aila/src/core/Aila.ts | 2 +- packages/aila/src/core/chat/AilaChat.ts | 15 ++-- .../aila/src/core/chat/AilaStreamHandler.ts | 4 +- .../aila/src/core/chat/PatchEnqueuer.test.ts | 2 + .../aila/src/core/prompt/AilaPromptBuilder.ts | 6 +- .../builders/AilaLessonPromptBuilder.ts | 2 +- packages/aila/src/core/types.ts | 2 +- .../features/americanisms/AilaAmericanisms.ts | 2 +- .../categorisers/AilaCategorisation.ts | 2 +- .../src/features/generation/AilaGeneration.ts | 4 +- .../src/features/generation/index.test.ts | 14 ++-- .../src/features/moderation/AilaModeration.ts | 12 +-- .../moderation/getSessionModerations.ts | 6 +- .../src/features/moderation/index.test.ts | 9 ++- .../moderation/moderators/OpenAiModerator.ts | 13 ++-- .../features/persistence/AilaPersistence.ts | 7 +- .../persistence/adaptors/file/index.ts | 4 +- .../features/persistence/adaptors/kv/index.ts | 2 +- .../persistence/adaptors/prisma/index.ts | 22 ++---- packages/aila/src/features/rag/AilaRag.ts | 2 +- .../snapshotStore/AilaSnapshotStore.ts | 8 +- .../src/features/threatDetection/index.ts | 1 + packages/aila/src/features/types.ts | 2 +- .../aila/src/protocol/jsonPatchProtocol.ts | 12 +-- .../aila/src/protocol/sectionToMarkdown.ts | 16 ++-- packages/api/src/middleware/rateLimiter.ts | 9 +-- packages/core/src/client.ts | 49 ------------ packages/core/src/inngest/index.ts | 10 ++- packages/core/src/llm/langchain.ts | 55 ++++++++++++++ packages/core/src/llm/openai.ts | 54 ------------- packages/core/src/models/apps.ts | 4 +- packages/core/src/models/lessonPlans.ts | 13 +--- packages/core/src/models/lessons.ts | 11 ++- packages/core/src/models/prompts.ts | 6 +- packages/core/src/models/snippets.ts | 12 +-- packages/core/src/models/statistics.ts | 8 +- packages/core/src/models/transcript.ts | 6 +- packages/core/src/rag/categorisation.ts | 16 ++++ packages/core/src/rag/index.ts | 50 ++----------- packages/core/src/rag/types.ts | 27 +++++++ .../utils/ailaModeration/moderationSchema.ts | 4 +- .../core/src/utils/getCaptionsFromFile.ts | 2 +- packages/core/src/utils/moderation.ts | 2 +- packages/core/src/utils/textify.ts | 12 +++ packages/db/client/index.ts | 49 ++++++++++++ packages/db/index.ts | 51 +------------ pnpm-lock.yaml | 75 ++----------------- 56 files changed, 310 insertions(+), 423 deletions(-) delete mode 100644 packages/core/src/client.ts create mode 100644 packages/core/src/llm/langchain.ts create mode 100644 packages/core/src/rag/categorisation.ts create mode 100644 packages/core/src/rag/types.ts create mode 100644 packages/core/src/utils/textify.ts create mode 100644 packages/db/client/index.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 3b3c7b68e..daa6fc1fd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -19,8 +19,10 @@ "autodocs", "autoprefixer", "autosize", + "backmerge", "backticked", "beitzah", + "bethclark", "bugsnag", "categorisation", "Categorised", @@ -28,6 +30,7 @@ "cloudinary", "clsx", "codegen", + "COLOR", "compat", "contrib", "cuid", @@ -40,6 +43,7 @@ "Docgen", "dockerized", "dopplerhq", + "dotenv", "EASS", "EHRC", "estree", @@ -106,6 +110,7 @@ "ponyfill", "popover", "portabletext", + "postcss", "posthog", "postpack", "posttest", @@ -117,6 +122,7 @@ "psql", "pusherapp", "ratelimit", + "refs", "Regen", "remeda", "Rerank", @@ -128,6 +134,7 @@ "sslmode", "SUBJ", "superjson", + "svgs", "tailwindcss", "tanstack", "testid", @@ -144,6 +151,7 @@ "uidotdev", "unjudged", "unsets", + "unshallow", "unsummarised", "untruncate", "untruncated", @@ -155,6 +163,7 @@ "valign", "vars", "vectorstores", + "vercel", "WCAG", "webvtt", "zadd", diff --git a/apps/nextjs/src/app/api/chat/user.test.ts b/apps/nextjs/src/app/api/chat/user.test.ts index 79e139e42..9e693e036 100644 --- a/apps/nextjs/src/app/api/chat/user.test.ts +++ b/apps/nextjs/src/app/api/chat/user.test.ts @@ -1,4 +1,5 @@ import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; +import { inngest } from "@oakai/core/src/inngest"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { reportRateLimitError } from "./user"; @@ -61,9 +62,6 @@ describe("chat route user functions", () => { const chatId = "testChatId"; await reportRateLimitError(error, userId, chatId); - - const { inngest } = await import("@oakai/core/src/inngest"); - expect(inngest.send).toHaveBeenCalledTimes(1); expect(inngest.send).toHaveBeenCalledWith({ name: "app/slack.notifyRateLimit", diff --git a/apps/nextjs/src/app/api/chat/user.ts b/apps/nextjs/src/app/api/chat/user.ts index 73a19ff42..fa0beb344 100644 --- a/apps/nextjs/src/app/api/chat/user.ts +++ b/apps/nextjs/src/app/api/chat/user.ts @@ -1,5 +1,5 @@ import { auth, clerkClient } from "@clerk/nextjs/server"; -import { AilaAuthenticationError } from "@oakai/aila"; +import { AilaAuthenticationError } from "@oakai/aila/src/core/AilaError"; import { demoUsers, inngest } from "@oakai/core"; import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; import { UserBannedError } from "@oakai/core/src/models/userBannedError"; diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts index 7c82e9b47..4519e0702 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -10,6 +10,7 @@ jest.mock("@oakai/core/src/inngest", () => ({ __esModule: true, inngest: { + createFunction: jest.fn(), send: jest.fn(), }, })); diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 3b24fefeb..a8e77900c 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -1,8 +1,8 @@ import type { AilaPlugin } from "@oakai/aila/src/core/plugins"; -import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection/types"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; +import { SafetyViolations as defaultSafetyViolations } from "@oakai/core"; import { inngest } from "@oakai/core/src/inngest"; -import { SafetyViolations as defaultSafetyViolations } from "@oakai/core/src/models/safetyViolations"; import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; diff --git a/packages/aila/package.json b/packages/aila/package.json index 206df9ad9..f6ecfd2e6 100644 --- a/packages/aila/package.json +++ b/packages/aila/package.json @@ -44,6 +44,7 @@ "@pollyjs/persister-fs": "^6.0.6", "@types/jest": "^29.5.12", "eslint": "^8.56.0", + "eslint-plugin-turbo": "^2.2.3", "jest": "^29.7.0", "setup-polly-jest": "^0.11.0", "ts-jest": "^29.1.4", diff --git a/packages/aila/src/core/Aila.liveWithOpenAI.test.ts b/packages/aila/src/core/Aila.liveWithOpenAI.test.ts index 5b9eed543..c58a64d92 100644 --- a/packages/aila/src/core/Aila.liveWithOpenAI.test.ts +++ b/packages/aila/src/core/Aila.liveWithOpenAI.test.ts @@ -1,7 +1,7 @@ -import type { AilaInitializationOptions } from "."; -import { Aila } from "."; import { MockCategoriser } from "../features/categorisation/categorisers/MockCategoriser"; +import { Aila } from "./Aila"; import { checkLastMessage, expectPatch, expectText } from "./Aila.testHelpers"; +import type { AilaInitializationOptions } from "./types"; const runInCI = process.env.CI === "true"; const runManually = process.env.RUN_LLM_TESTS === "true"; diff --git a/packages/aila/src/core/Aila.test.ts b/packages/aila/src/core/Aila.test.ts index 567944c60..12f725f3d 100644 --- a/packages/aila/src/core/Aila.test.ts +++ b/packages/aila/src/core/Aila.test.ts @@ -1,12 +1,13 @@ -import { Aila } from "."; +import type { Polly } from "@pollyjs/core"; + import { setupPolly } from "../../tests/mocks/setupPolly"; import { MockCategoriser } from "../features/categorisation/categorisers/MockCategoriser"; +import { Aila } from "./Aila"; import { AilaAuthenticationError } from "./AilaError"; import { MockLLMService } from "./llm/MockLLMService"; describe("Aila", () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let polly: any; + let polly: Polly; beforeAll(() => { polly = setupPolly(); @@ -348,7 +349,9 @@ describe("Aila", () => { const mockCategoriser = new MockCategoriser({ mockedLessonPlan }); const mockLLMResponse = [ + // eslint-disable-next-line @typescript-eslint/quotes, quotes '{"type":"patch","reasoning":"Update title","value":{"op":"replace","path":"/title","value":"Updated Mocked Lesson Plan"}}␞\n', + // eslint-disable-next-line @typescript-eslint/quotes, quotes '{"type":"patch","reasoning":"Update subject","value":{"op":"replace","path":"/subject","value":"Updated Mocked Subject"}}␞\n', ]; const mockLLMService = new MockLLMService(mockLLMResponse); @@ -379,7 +382,6 @@ describe("Aila", () => { // Use MockLLMService to generate a response await ailaInstance.generateSync({ input: "Test input" }); - console.log("Generated"); // Check if MockLLMService updates were applied expect(ailaInstance.lesson.plan.title).toBe("Updated Mocked Lesson Plan"); expect(ailaInstance.lesson.plan.subject).toBe("Updated Mocked Subject"); diff --git a/packages/aila/src/core/Aila.testHelpers.ts b/packages/aila/src/core/Aila.testHelpers.ts index e2831e130..927f88255 100644 --- a/packages/aila/src/core/Aila.testHelpers.ts +++ b/packages/aila/src/core/Aila.testHelpers.ts @@ -1,8 +1,8 @@ import { expect } from "@jest/globals"; import invariant from "tiny-invariant"; -import type { Aila } from "."; import type { MessagePart, TextDocument } from "../protocol/jsonPatchProtocol"; +import type { Aila } from "./Aila"; export function checkAssistantResponse(content: string) { // Check that the response is a string (not JSON) diff --git a/packages/aila/src/core/Aila.ts b/packages/aila/src/core/Aila.ts index 41de60f53..886bc6089 100644 --- a/packages/aila/src/core/Aila.ts +++ b/packages/aila/src/core/Aila.ts @@ -1,5 +1,5 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { prisma as globalPrisma } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db/client"; import { aiLogger } from "@oakai/logger"; import { diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 452b2a49e..6e86b29f6 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -5,17 +5,13 @@ import { } from "@oakai/core/src/utils/subjects"; import invariant from "tiny-invariant"; -import type { AilaChatService, AilaServices } from "../.."; -import { AilaError } from "../.."; import { DEFAULT_MODEL, DEFAULT_TEMPERATURE } from "../../constants"; -import type { - AilaGenerationStatus} from "../../features/generation"; -import { - AilaGeneration -} from "../../features/generation"; +import type { AilaChatService } from "../../core/AilaServices"; +import type { AilaServices } from "../../core/AilaServices"; +import { AilaGeneration } from "../../features/generation/AilaGeneration"; +import type { AilaGenerationStatus } from "../../features/generation/types"; import { generateMessageId } from "../../helpers/chat/generateMessageId"; -import type { - JsonPatchDocumentOptional} from "../../protocol/jsonPatchProtocol"; +import type { JsonPatchDocumentOptional } from "../../protocol/jsonPatchProtocol"; import { LLMMessageSchema, parseMessageParts, @@ -24,6 +20,7 @@ import type { AilaPersistedChat, AilaRagRelevantLesson, } from "../../protocol/schema"; +import { AilaError } from "../AilaError"; import type { LLMService } from "../llm/LLMService"; import { OpenAIService } from "../llm/OpenAIService"; import type { AilaPromptBuilder } from "../prompt/AilaPromptBuilder"; diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 3245d1098..caa586fe9 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -53,7 +53,9 @@ export class AilaStreamHandler { log.info("Chat completed", this._chat.iteration, this._chat.id); } catch (e) { this._chat.aila.errorReporter?.reportError(e); - throw new AilaChatError("Chat completion failed", { cause: e }); + controller.error( + new AilaChatError("Chat completion failed", { cause: e }), + ); } finally { this.closeController(); log.info("Stream closed", this._chat.iteration, this._chat.id); diff --git a/packages/aila/src/core/chat/PatchEnqueuer.test.ts b/packages/aila/src/core/chat/PatchEnqueuer.test.ts index fd2729f39..40f35df8b 100644 --- a/packages/aila/src/core/chat/PatchEnqueuer.test.ts +++ b/packages/aila/src/core/chat/PatchEnqueuer.test.ts @@ -14,6 +14,7 @@ describe("PatchEnqueuer", () => { await patchEnqueuer.enqueuePatch(path, value); + // eslint-disable-next-line @typescript-eslint/unbound-method expect(controller.enqueue).toHaveBeenCalled(); const expectedPatch = `\n␞\n${JSON.stringify({ type: "patch", @@ -21,6 +22,7 @@ describe("PatchEnqueuer", () => { value: { op: "add", path, value }, status: "complete", })}\n␞\n`; + // eslint-disable-next-line @typescript-eslint/unbound-method expect(controller.enqueue).toHaveBeenCalledWith(expectedPatch); }); diff --git a/packages/aila/src/core/prompt/AilaPromptBuilder.ts b/packages/aila/src/core/prompt/AilaPromptBuilder.ts index 3e93decbc..87e0d1265 100644 --- a/packages/aila/src/core/prompt/AilaPromptBuilder.ts +++ b/packages/aila/src/core/prompt/AilaPromptBuilder.ts @@ -1,8 +1,8 @@ import { jsonrepair } from "jsonrepair"; -import type { Message } from ".."; -import type { AilaServices } from "../.."; +import type { AilaServices } from "../../core/AilaServices"; import { tryWithErrorReporting } from "../../helpers/errorReporting"; +import type { Message } from "../chat"; export abstract class AilaPromptBuilder { protected _aila: AilaServices; @@ -41,7 +41,7 @@ export abstract class AilaPromptBuilder { } return row; }, - `Failed to parse row`, + "Failed to parse row", "info", { category: "json", diff --git a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts index b9afc0cc5..a6142cd71 100644 --- a/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts +++ b/packages/aila/src/core/prompt/builders/AilaLessonPromptBuilder.ts @@ -1,6 +1,6 @@ import type { TemplateProps } from "@oakai/core/src/prompts/lesson-assistant"; import { template } from "@oakai/core/src/prompts/lesson-assistant"; -import { prisma as globalPrisma } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db/client"; import { aiLogger } from "@oakai/logger"; import { DEFAULT_RAG_LESSON_PLANS } from "../../../constants"; diff --git a/packages/aila/src/core/types.ts b/packages/aila/src/core/types.ts index a98b8fa2a..516aa0ab6 100644 --- a/packages/aila/src/core/types.ts +++ b/packages/aila/src/core/types.ts @@ -1,4 +1,4 @@ -import type { PrismaClientWithAccelerate } from "@oakai/db"; +import type { PrismaClientWithAccelerate } from "@oakai/db/client"; import type { AilaAmericanismsFeature } from "../features/americanisms"; import type { AnalyticsAdapter } from "../features/analytics"; diff --git a/packages/aila/src/features/americanisms/AilaAmericanisms.ts b/packages/aila/src/features/americanisms/AilaAmericanisms.ts index b73444075..022d2831d 100644 --- a/packages/aila/src/features/americanisms/AilaAmericanisms.ts +++ b/packages/aila/src/features/americanisms/AilaAmericanisms.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// -import { textify } from "@oakai/core/src/models/lessonPlans"; +import { textify } from "@oakai/core/src/utils/textify"; import translator from "american-british-english-translator"; import type { AilaAmericanismsFeature } from "."; diff --git a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts index 4ff724c72..0037e6c81 100644 --- a/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts +++ b/packages/aila/src/features/categorisation/categorisers/AilaCategorisation.ts @@ -1,4 +1,4 @@ -import { CategoriseKeyStageAndSubjectResponse } from "@oakai/core/src/rag"; +import { CategoriseKeyStageAndSubjectResponse } from "@oakai/core/src/rag/categorisation"; import { keyStages, subjects } from "@oakai/core/src/utils/subjects"; import { aiLogger } from "@oakai/logger"; import type { ChatCompletionMessageParam } from "openai/resources"; diff --git a/packages/aila/src/features/generation/AilaGeneration.ts b/packages/aila/src/features/generation/AilaGeneration.ts index 9a41b76d7..3be904df3 100644 --- a/packages/aila/src/features/generation/AilaGeneration.ts +++ b/packages/aila/src/features/generation/AilaGeneration.ts @@ -4,12 +4,12 @@ import { generateAilaPromptVersionVariantSlug, } from "@oakai/core/src/prompts/lesson-assistant/variants"; import type { Prompt } from "@oakai/db"; -import { prisma } from "@oakai/db"; +import { prisma } from "@oakai/db/client"; import { aiLogger } from "@oakai/logger"; import { kv } from "@vercel/kv"; import { getEncoding } from "js-tiktoken"; -import type { AilaServices } from "../../core"; +import type { AilaServices } from "../../core/AilaServices"; import type { AilaChat } from "../../core/chat"; import type { AilaGenerationStatus } from "./types"; diff --git a/packages/aila/src/features/generation/index.test.ts b/packages/aila/src/features/generation/index.test.ts index ec36ca248..1173efc77 100644 --- a/packages/aila/src/features/generation/index.test.ts +++ b/packages/aila/src/features/generation/index.test.ts @@ -1,8 +1,8 @@ -import { AilaGeneration } from "."; -import type { AilaInitializationOptions } from "../.."; -import { Aila } from "../.."; +import { Aila } from "../../core/Aila"; import type { Message } from "../../core/chat"; import { AilaChat } from "../../core/chat"; +import type { AilaInitializationOptions } from "../../core/types"; +import { AilaGeneration } from "./AilaGeneration"; const ailaArgs: AilaInitializationOptions = { plugins: [], @@ -21,7 +21,9 @@ describe("calculateTokenUsage", () => { ]; const mockEncoding = { - encode: jest.fn().mockImplementation((text) => text.split(" ").length), + encode: jest + .fn() + .mockImplementation((text: string) => text.split(" ").length), }; jest.mock("js-tiktoken", () => ({ getEncoding: () => mockEncoding, @@ -57,7 +59,9 @@ describe("calculateTokenUsage", () => { ]; const mockEncoding = { - encode: jest.fn().mockImplementation((text) => text.split(" ").length), + encode: jest + .fn() + .mockImplementation((text: string) => text.split(" ").length), }; jest.mock("js-tiktoken", () => ({ getEncoding: () => mockEncoding, diff --git a/packages/aila/src/features/moderation/AilaModeration.ts b/packages/aila/src/features/moderation/AilaModeration.ts index db4f6e684..3eab050be 100644 --- a/packages/aila/src/features/moderation/AilaModeration.ts +++ b/packages/aila/src/features/moderation/AilaModeration.ts @@ -1,4 +1,4 @@ -import { Moderations } from "@oakai/core"; +import { Moderations } from "@oakai/core/src/models/moderations"; import { getCategoryGroup, getMockModerationResult, @@ -6,16 +6,12 @@ import { isToxic, } from "@oakai/core/src/utils/ailaModeration/helpers"; import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; -import type { - Moderation, - PrismaClientWithAccelerate} from "@oakai/db"; -import { - prisma as globalPrisma, -} from "@oakai/db"; +import type { Moderation, PrismaClientWithAccelerate } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import invariant from "tiny-invariant"; -import type { AilaServices } from "../../core"; +import type { AilaServices } from "../../core/AilaServices"; import type { Message } from "../../core/chat"; import type { AilaPluginContext } from "../../core/plugins/types"; import { getLastAssistantMessage } from "../../helpers/chat/getLastAssistantMessage"; diff --git a/packages/aila/src/features/moderation/getSessionModerations.ts b/packages/aila/src/features/moderation/getSessionModerations.ts index 522e6f8dd..57e5a8bd7 100644 --- a/packages/aila/src/features/moderation/getSessionModerations.ts +++ b/packages/aila/src/features/moderation/getSessionModerations.ts @@ -1,6 +1,6 @@ -import { Moderations } from "@oakai/core"; -import type { Moderation} from "@oakai/db"; -import { prisma } from "@oakai/db"; +import { Moderations } from "@oakai/core/src/models/moderations"; +import { prisma } from "@oakai/db/client"; +import type { Moderation } from "@prisma/client"; export async function getSessionModerations( appSessionId: string, diff --git a/packages/aila/src/features/moderation/index.test.ts b/packages/aila/src/features/moderation/index.test.ts index 58601196c..f84c77cb6 100644 --- a/packages/aila/src/features/moderation/index.test.ts +++ b/packages/aila/src/features/moderation/index.test.ts @@ -1,12 +1,13 @@ // Generated by CodiumAI -import type { Moderations } from "@oakai/core"; +import type { Moderations } from "@oakai/core/src/models/moderations"; import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import { AilaModeration } from "."; -import { Aila } from "../.."; -import type { AilaChatInitializationOptions, Message } from "../../core"; +import { Aila } from "../../core/Aila"; +import type { Message } from "../../core/chat"; import type { AilaPlugin } from "../../core/plugins"; +import type { AilaChatInitializationOptions } from "../../core/types"; import type { LooseLessonPlan } from "../../protocol/schema"; import type { AilaModerator } from "./moderators"; import { MockModerator } from "./moderators/MockModerator"; @@ -182,6 +183,7 @@ describe("AilaModeration", () => { messages, }; const moderations = { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return create: jest.fn((mod) => ({ id: "ABC", ...mod })), } as unknown as Moderations; const mockPlugin = { @@ -211,6 +213,7 @@ describe("AilaModeration", () => { categories: moderationResult.categories, id: "ABC", }); + // eslint-disable-next-line @typescript-eslint/unbound-method expect(mockPlugin.onToxicModeration).toHaveBeenCalledWith( expect.objectContaining({ categories: ["t/encouragement-illegal-activity"], diff --git a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts index 21cd89273..5d186e632 100644 --- a/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts +++ b/packages/aila/src/features/moderation/moderators/OpenAiModerator.ts @@ -1,10 +1,7 @@ import { createOpenAIClient } from "@oakai/core/src/llm/openai"; import { moderationPrompt } from "@oakai/core/src/utils/ailaModeration/moderationPrompt"; -import type { - ModerationResult} from "@oakai/core/src/utils/ailaModeration/moderationSchema"; -import { - moderationResponseSchema, -} from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { ModerationResult } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import { moderationResponseSchema } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; import type OpenAI from "openai"; import type { @@ -18,7 +15,7 @@ import { DEFAULT_MODERATION_MODEL, DEFAULT_MODERATION_TEMPERATURE, } from "../../../constants"; -import type { AilaServices } from "../../../core"; +import type { AilaServices } from "../../../core/AilaServices"; const log = aiLogger("aila:moderation"); @@ -141,14 +138,14 @@ export class OpenAiModerator extends AilaModerator { message: "No message.content in moderation response from OpenAI", data: { moderationResponse }, }); - throw new AilaModerationError(`Failed to get moderation response`); + throw new AilaModerationError("Failed to get moderation response"); } if (!response.data) { this._aila?.errorReporter?.addBreadcrumb({ message: "Invalid moderation response", data: { moderationResponse }, }); - throw new AilaModerationError(`No moderation response`); + throw new AilaModerationError("No moderation response"); } const { categories, justification, scores } = response.data; diff --git a/packages/aila/src/features/persistence/AilaPersistence.ts b/packages/aila/src/features/persistence/AilaPersistence.ts index e031e3611..a375e4328 100644 --- a/packages/aila/src/features/persistence/AilaPersistence.ts +++ b/packages/aila/src/features/persistence/AilaPersistence.ts @@ -1,11 +1,12 @@ import type { GenerationStatus } from "@prisma/client"; import invariant from "tiny-invariant"; -import type { AilaChatService, AilaServices } from "../../core"; -import { AilaError } from "../../core"; +import { AilaError } from "../../core/AilaError"; +import type { AilaChatService } from "../../core/AilaServices"; +import type { AilaServices } from "../../core/AilaServices"; import type { AilaOptionsWithDefaultFallbackValues } from "../../core/types"; import type { AilaPersistedChat } from "../../protocol/schema"; -import type { AilaGeneration } from "../generation"; +import type { AilaGeneration } from "../generation/AilaGeneration"; export abstract class AilaPersistence { protected _chat: AilaChatService; diff --git a/packages/aila/src/features/persistence/adaptors/file/index.ts b/packages/aila/src/features/persistence/adaptors/file/index.ts index a63cecc1a..cfe5bff7a 100644 --- a/packages/aila/src/features/persistence/adaptors/file/index.ts +++ b/packages/aila/src/features/persistence/adaptors/file/index.ts @@ -1,6 +1,6 @@ import { AilaPersistence } from "../.."; -import type { AilaChatService, AilaServices } from "../../../../core"; -import type { AilaGeneration } from "../../../generation"; +import type { AilaChatService, AilaServices } from "../../../../core/AilaServices"; +import type { AilaGeneration } from "../../../generation/AilaGeneration"; export class AilaFilePersistence extends AilaPersistence { constructor({ aila, chat }: { aila: AilaServices; chat: AilaChatService }) { diff --git a/packages/aila/src/features/persistence/adaptors/kv/index.ts b/packages/aila/src/features/persistence/adaptors/kv/index.ts index a1cdf8d76..4423d0622 100644 --- a/packages/aila/src/features/persistence/adaptors/kv/index.ts +++ b/packages/aila/src/features/persistence/adaptors/kv/index.ts @@ -1,7 +1,7 @@ import { kv } from "@vercel/kv"; import { AilaPersistence } from "../.."; -import type { AilaChatService, AilaServices } from "../../../../core"; +import type { AilaChatService, AilaServices } from "../../../../core/AilaServices"; export class AilaKVPersistence extends AilaPersistence { constructor({ chat, aila }: { chat: AilaChatService; aila: AilaServices }) { diff --git a/packages/aila/src/features/persistence/adaptors/prisma/index.ts b/packages/aila/src/features/persistence/adaptors/prisma/index.ts index 8a1ecb0c0..de7c8cb17 100644 --- a/packages/aila/src/features/persistence/adaptors/prisma/index.ts +++ b/packages/aila/src/features/persistence/adaptors/prisma/index.ts @@ -1,25 +1,19 @@ -import type { - Prisma, - PrismaClientWithAccelerate} from "@oakai/db"; -import { - prisma as globalPrisma, -} from "@oakai/db"; +import type { Prisma, PrismaClientWithAccelerate } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db/client"; import { aiLogger } from "@oakai/logger"; import { AilaPersistence } from "../.."; +import { AilaAuthenticationError } from "../../../../core/AilaError"; import type { AilaChatService, - AilaServices} from "../../../../core"; -import { - AilaAuthenticationError -} from "../../../../core"; + AilaServices, +} from "../../../../core/AilaServices"; import type { AilaPersistedChat, - LessonPlanKeys} from "../../../../protocol/schema"; -import { - chatSchema, + LessonPlanKeys, } from "../../../../protocol/schema"; -import type { AilaGeneration } from "../../../generation"; +import { chatSchema } from "../../../../protocol/schema"; +import type { AilaGeneration } from "../../../generation/AilaGeneration"; const log = aiLogger("aila:persistence"); diff --git a/packages/aila/src/features/rag/AilaRag.ts b/packages/aila/src/features/rag/AilaRag.ts index f1ba55851..f9b58fb71 100644 --- a/packages/aila/src/features/rag/AilaRag.ts +++ b/packages/aila/src/features/rag/AilaRag.ts @@ -1,6 +1,6 @@ import { RAG } from "@oakai/core/src/rag"; import type { PrismaClientWithAccelerate } from "@oakai/db"; -import { prisma as globalPrisma } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db/client"; import { aiLogger } from "@oakai/logger"; import type { AilaRagFeature } from "."; diff --git a/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts index 3089de3b4..7b8d54833 100644 --- a/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts +++ b/packages/aila/src/features/snapshotStore/AilaSnapshotStore.ts @@ -1,10 +1,10 @@ -import { LessonSnapshots } from "@oakai/core"; -import type { PrismaClientWithAccelerate} from "@oakai/db"; -import { prisma as globalPrisma } from "@oakai/db"; +import { LessonSnapshots } from "@oakai/core/src/models/lessonSnapshots"; +import type { PrismaClientWithAccelerate } from "@oakai/db"; +import { prisma as globalPrisma } from "@oakai/db/client"; import type { LessonSnapshotTrigger } from "@prisma/client"; import invariant from "tiny-invariant"; -import type { AilaServices } from "../../core"; +import type { AilaServices } from "../../core/AilaServices"; import type { LooseLessonPlan } from "../../protocol/schema"; export class AilaSnapshotStore { diff --git a/packages/aila/src/features/threatDetection/index.ts b/packages/aila/src/features/threatDetection/index.ts index b06023e5e..43d34cdc2 100644 --- a/packages/aila/src/features/threatDetection/index.ts +++ b/packages/aila/src/features/threatDetection/index.ts @@ -1,2 +1,3 @@ export { AilaThreatDetection } from "./AilaThreatDetection"; export { AilaThreatDetector } from "./detectors/AilaThreatDetector"; +export { AilaThreatDetectionError } from "./types"; diff --git a/packages/aila/src/features/types.ts b/packages/aila/src/features/types.ts index c44ca2ef5..0b75b745c 100644 --- a/packages/aila/src/features/types.ts +++ b/packages/aila/src/features/types.ts @@ -3,7 +3,7 @@ import type { AilaPluginContext } from "../core/plugins"; import type { ModerationDocument } from "../protocol/jsonPatchProtocol"; import type { AilaPersistedChat, LooseLessonPlan } from "../protocol/schema"; import type { AilaErrorBreadcrumb, AilaErrorSeverity } from "./errorReporting/types"; -import type { AilaGeneration } from "./generation"; +import type { AilaGeneration } from "./generation/AilaGeneration"; import type { AilaThreatDetector } from "./threatDetection"; export interface AilaModerationFeature { diff --git a/packages/aila/src/protocol/jsonPatchProtocol.ts b/packages/aila/src/protocol/jsonPatchProtocol.ts index 7bc3f2463..90533503b 100644 --- a/packages/aila/src/protocol/jsonPatchProtocol.ts +++ b/packages/aila/src/protocol/jsonPatchProtocol.ts @@ -1,19 +1,13 @@ import { moderationCategoriesSchema } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { aiLogger } from "@oakai/logger"; import * as Sentry from "@sentry/nextjs"; -import type { - Operation} from "fast-json-patch"; -import { - applyPatch, - deepClone, - JsonPatchError, -} from "fast-json-patch"; +import type { Operation } from "fast-json-patch"; +import { applyPatch, deepClone, JsonPatchError } from "fast-json-patch"; import untruncateJson from "untruncate-json"; import { z } from "zod"; import zodToJsonSchema from "zod-to-json-schema"; -import type { - LooseLessonPlan} from "./schema"; +import type { LooseLessonPlan } from "./schema"; import { BasedOnOptionalSchema, BasedOnSchema, diff --git a/packages/aila/src/protocol/sectionToMarkdown.ts b/packages/aila/src/protocol/sectionToMarkdown.ts index a2d345aac..e9b8f36fd 100644 --- a/packages/aila/src/protocol/sectionToMarkdown.ts +++ b/packages/aila/src/protocol/sectionToMarkdown.ts @@ -1,16 +1,12 @@ import { camelCaseToSentenceCase } from "@oakai/core/src/utils/camelCaseToSentenceCase"; import { isArray, isNumber, isObject, isString } from "remeda"; -import type { - QuizOptional} from "./schema"; -import { - CycleOptionalSchema, - QuizOptionalSchema, -} from "./schema"; +import type { QuizOptional } from "./schema"; +import { CycleOptionalSchema, QuizOptionalSchema } from "./schema"; export function sortIgnoringSpecialChars(strings: string[]): string[] { // Function to normalize strings by removing *, -, and spaces - const normalize = (str: string) => str.replace(/[\*\-\s]/g, ""); + const normalize = (str: string) => str.replace(/[*\-\s]/g, ""); // Sort the array using the normalized values for comparison return strings.sort((a, b) => { @@ -46,12 +42,12 @@ export function sectionToMarkdown( return `${content}\n\n### Explanation\n\n${sectionToMarkdown("explanation", cycle.explanation ?? "…")}\n\n### Check for Understanding\n\n${cycle.checkForUnderstanding ? organiseAnswersAndDistractors(cycle.checkForUnderstanding) : "…"}\n\n### Practice\n\n${cycle.practice ?? "…"}\n\n### Feedback\n\n${cycle.feedback ?? "…"}`; } catch (e) { // Invalid schema - return `## There's been a problem\n\nIt looks like this learning cycle hasn't generated properly. Tap **Retry** or ask for this section to be regenerated.`; + return "## There's been a problem\n\nIt looks like this learning cycle hasn't generated properly. Tap **Retry** or ask for this section to be regenerated."; } } if (isArray(value)) { - if (value.map((v) => typeof v).every((v) => v === "string")) { + if (value.every((v): v is string => typeof v === "string")) { // Render arrays of strings as bullets return value.map((v) => `- ${v}`).join("\n\n"); } else { @@ -85,8 +81,10 @@ export function sectionToMarkdown( .filter((v) => v[keys[0]] && v[keys[1]]) .map((v) => { // @ts-expect-error - we know that the keys exist + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const header = v[keys[0]]; // @ts-expect-error - we know that the keys exist + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const body = v[keys[1]]; return `### ${header}\n\n${body}`; }) diff --git a/packages/api/src/middleware/rateLimiter.ts b/packages/api/src/middleware/rateLimiter.ts index b12ae86a6..c1b7a14e2 100644 --- a/packages/api/src/middleware/rateLimiter.ts +++ b/packages/api/src/middleware/rateLimiter.ts @@ -1,10 +1,7 @@ -import { inngest } from "@oakai/core"; +import { inngest } from "@oakai/core/src/inngest"; import { rateLimits } from "@oakai/core/src/utils/rateLimiting/rateLimit"; -import type { - RateLimiter} from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; -import { - RateLimitExceededError -} from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; +import type { RateLimiter } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; +import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { TRPCError } from "@trpc/server"; import { t } from "../trpc"; diff --git a/packages/core/src/client.ts b/packages/core/src/client.ts deleted file mode 100644 index e41f819bc..000000000 --- a/packages/core/src/client.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { structuredLogger } from "@oakai/logger"; -import { EventSchemas, Inngest } from "inngest"; -import getConfig from "next/config"; - -import types from "./functions/event-types"; -import { eventLogger } from "./middleware/eventLogger"; - -let serverRuntimeConfig; -try { - const { serverRuntimeConfig: nextServerRuntimeConfig } = getConfig(); - serverRuntimeConfig = nextServerRuntimeConfig; -} catch (e) { - //console.log("No Next environment"); -} -const CONTEXT = serverRuntimeConfig?.DEPLOY_CONTEXT; -const BRANCH = serverRuntimeConfig?.BRANCH; - -function getInngestEnv() { - if (CONTEXT === "production") { - return "production"; - } else if (CONTEXT === "deploy-preview" && BRANCH) { - // Naively slugify, removing any non-alphanumeric - return BRANCH.replace(/\W/g, "-"); - } else { - return "development"; - } -} - -function isJestEnvironment() { - return process.env.JEST_WORKER_ID !== undefined; -} - -const inngestEnv = getInngestEnv(); -const inngestEventKey = process.env.INNGEST_EVENT_KEY; - -if (!inngestEventKey) { - throw new Error("Missing env var INNGEST_EVENT_KEY"); -} - -export const inngest = new Inngest({ - name: "Oak AI", - id: "oak-ai", - schemas: new EventSchemas().fromZod(types), - eventKey: inngestEventKey, - env: inngestEnv, - logger: structuredLogger, - middleware: [eventLogger(inngestEnv, inngestEventKey)], - isDev: process.env.NODE_ENV === "development" || isJestEnvironment(), -}); diff --git a/packages/core/src/inngest/index.ts b/packages/core/src/inngest/index.ts index 9aba0f2c5..a2d792d0c 100644 --- a/packages/core/src/inngest/index.ts +++ b/packages/core/src/inngest/index.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ + +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { structuredLogger } from "@oakai/logger"; import { EventSchemas, Inngest } from "inngest"; import getConfig from "next/config"; @@ -12,8 +15,9 @@ try { } catch (e) { //console.log("No Next environment"); } -const CONTEXT = serverRuntimeConfig?.DEPLOY_CONTEXT; -const BRANCH = serverRuntimeConfig?.BRANCH; + +const CONTEXT = serverRuntimeConfig?.DEPLOY_CONTEXT as string | undefined; +const BRANCH = serverRuntimeConfig?.BRANCH as string | undefined; function getInngestEnv() { if (CONTEXT === "production") { @@ -37,8 +41,6 @@ if (!inngestEventKey) { throw new Error("Missing env var INNGEST_EVENT_KEY"); } -console.log("Inngest env:", inngestEnv); - export const inngest = new Inngest({ name: "Oak AI", id: "oak-ai", diff --git a/packages/core/src/llm/langchain.ts b/packages/core/src/llm/langchain.ts new file mode 100644 index 000000000..1b0f30414 --- /dev/null +++ b/packages/core/src/llm/langchain.ts @@ -0,0 +1,55 @@ +import { ChatOpenAI as LangchainChatOpenAI } from "langchain/chat_models/openai"; +import type { BaseLLMParams } from "langchain/llms/base"; +import type { + AzureOpenAIInput, + OpenAIInput} from "langchain/llms/openai"; +import { + OpenAI as OpenAILangchain, +} from "langchain/llms/openai"; +import type { ClientOptions } from "openai"; + +import { heliconeHeaders } from "./helicone"; + +export function createOpenAILangchainClient({ + app, + fields = {}, +}: { + app: string; + fields?: Partial & + Partial & + BaseLLMParams & { + configuration?: ClientOptions; + }; +}) { + const defaultHeaders = heliconeHeaders({ app }); + return new OpenAILangchain({ + ...fields, + configuration: { + apiKey: process.env.OPENAI_API_KEY, + baseURL: process.env.HELICONE_EU_HOST, + defaultHeaders, + }, + }); +} + +export function createOpenAILangchainChatClient({ + app, + fields = {}, +}: { + app: string; + fields?: Partial & + Partial & + BaseLLMParams & { + configuration?: ClientOptions; + }; +}) { + const defaultHeaders = heliconeHeaders({ app }); + return new LangchainChatOpenAI({ + ...fields, + configuration: { + apiKey: process.env.OPENAI_API_KEY, + baseURL: process.env.HELICONE_EU_HOST, + defaultHeaders, + }, + }); +} diff --git a/packages/core/src/llm/openai.ts b/packages/core/src/llm/openai.ts index 7aa0210b4..dea3ef760 100644 --- a/packages/core/src/llm/openai.ts +++ b/packages/core/src/llm/openai.ts @@ -1,13 +1,5 @@ import type { OpenAIProvider } from "@ai-sdk/openai"; import { createOpenAI } from "@ai-sdk/openai"; -import { ChatOpenAI as LangchainChatOpenAI } from "langchain/chat_models/openai"; -import type { BaseLLMParams } from "langchain/llms/base"; -import type { - AzureOpenAIInput, - OpenAIInput} from "langchain/llms/openai"; -import { - OpenAI as OpenAILangchain, -} from "langchain/llms/openai"; import type { ClientOptions } from "openai"; import OpenAI from "openai"; @@ -57,54 +49,8 @@ function createOpenAIModerationsClient() { return openAi.moderations; } -function createOpenAILangchainClient({ - app, - fields = {}, -}: { - app: string; - fields?: Partial & - Partial & - BaseLLMParams & { - configuration?: ClientOptions; - }; -}) { - const defaultHeaders = heliconeHeaders({ app }); - return new OpenAILangchain({ - ...fields, - configuration: { - apiKey: process.env.OPENAI_API_KEY, - baseURL: process.env.HELICONE_EU_HOST, - defaultHeaders, - }, - }); -} - -function createOpenAILangchainChatClient({ - app, - fields = {}, -}: { - app: string; - fields?: Partial & - Partial & - BaseLLMParams & { - configuration?: ClientOptions; - }; -}) { - const defaultHeaders = heliconeHeaders({ app }); - return new LangchainChatOpenAI({ - ...fields, - configuration: { - apiKey: process.env.OPENAI_API_KEY, - baseURL: process.env.HELICONE_EU_HOST, - defaultHeaders, - }, - }); -} - export { createOpenAIClient, createVercelOpenAIClient, createOpenAIModerationsClient, - createOpenAILangchainClient, - createOpenAILangchainChatClient, }; diff --git a/packages/core/src/models/apps.ts b/packages/core/src/models/apps.ts index 9e45c85e1..4c9b6563a 100644 --- a/packages/core/src/models/apps.ts +++ b/packages/core/src/models/apps.ts @@ -1,5 +1,5 @@ -import type { PrismaClientWithAccelerate, Prompt } from "@oakai/db"; -import { Prisma } from "@oakai/db"; +import type { Prompt, PrismaClientWithAccelerate } from "@oakai/db"; +import { Prisma } from "@prisma/client"; import type { App } from "@prisma/client"; import { Prompts } from "./prompts"; diff --git a/packages/core/src/models/lessonPlans.ts b/packages/core/src/models/lessonPlans.ts index 7505fd60b..736c6fc58 100644 --- a/packages/core/src/models/lessonPlans.ts +++ b/packages/core/src/models/lessonPlans.ts @@ -11,7 +11,6 @@ import { LessonPlanStatus } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; -import yaml from "yaml"; import { LLMResponseJsonSchema } from "../../../aila/src/protocol/jsonPatchProtocol"; import { LessonPlanJsonSchema } from "../../../aila/src/protocol/schema"; @@ -21,22 +20,12 @@ import { template } from "../prompts/lesson-assistant"; import { RAG } from "../rag"; import { camelCaseToSentenceCase } from "../utils/camelCaseToSentenceCase"; import { embedWithCache } from "../utils/embeddings"; +import { textify } from "../utils/textify"; import type { Caption} from "./types/caption"; import { CaptionsSchema } from "./types/caption"; const log = aiLogger("lessons"); -// Simplifies the input to a string for embedding -export function textify(input: string | string[] | object): string { - if (Array.isArray(input)) { - return input.map((row) => textify(row)).join("\n"); - } else if (typeof input === "object") { - return yaml.stringify(input); - } else { - return input; - } -} - export type LessonPlanWithLesson = LessonPlan & { lesson: Omit< Lesson, diff --git a/packages/core/src/models/lessons.ts b/packages/core/src/models/lessons.ts index 981c1e59d..358d3fdc9 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -5,10 +5,9 @@ import type { QuizQuestion, Snippet, SnippetVariant, - Transcript} from "@oakai/db"; -import { - ZLesson, + Transcript, } from "@oakai/db"; +import { ZLesson } from "@oakai/db"; import { ZNewLesson } from "@oakai/db/schemas/lesson"; import { aiLogger } from "@oakai/logger"; import { StructuredOutputParser } from "langchain/output_parsers"; @@ -17,10 +16,10 @@ import { RunnableSequence } from "langchain/schema/runnable"; import { z } from "zod"; import { inngest } from "../inngest"; -import { createOpenAILangchainClient } from "../llm/openai"; -import type { SnippetWithLesson} from "./snippets"; +import { createOpenAILangchainClient } from "../llm/langchain"; +import type { SnippetWithLesson } from "./snippets"; import { Snippets } from "./snippets"; -import type { Caption} from "./types/caption"; +import type { Caption } from "./types/caption"; import { CaptionsSchema } from "./types/caption"; const log = aiLogger("lessons"); diff --git a/packages/core/src/models/prompts.ts b/packages/core/src/models/prompts.ts index b2a6b633e..d7317c6fe 100644 --- a/packages/core/src/models/prompts.ts +++ b/packages/core/src/models/prompts.ts @@ -7,7 +7,7 @@ import type { BaseMessage} from "langchain/schema"; import { SystemMessage } from "langchain/schema"; import untruncateJson from "untruncate-json"; -import { createOpenAILangchainChatClient } from "../llm/openai"; +import { createOpenAILangchainChatClient } from "../llm/langchain"; type CompletionMeta = { timeTaken: number; @@ -133,7 +133,7 @@ export class Prompts { } }, handleChainError: (err) => { - this.logger.error(err, `LLM chain error`); + this.logger.error(err, "LLM chain error"); }, }, ], @@ -162,7 +162,7 @@ export class Prompts { } if ("errorMessage" in result && typeof result.errorMessage === "string") { - this.logger.info(`llm refused to fulfil prompt: %s`, result.errorMessage); + this.logger.info("llm refused to fulfil prompt: %s", result.errorMessage); throw new LLMRefusalError(result.errorMessage, meta); } diff --git a/packages/core/src/models/snippets.ts b/packages/core/src/models/snippets.ts index a7bd528d8..0b7781333 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -1,11 +1,5 @@ -import type { - PrismaClientWithAccelerate, - Snippet} from "@oakai/db"; -import { - Prisma, - SnippetStatus, - SnippetVariant, -} from "@oakai/db"; +import type { PrismaClientWithAccelerate, Snippet } from "@oakai/db"; +import { Prisma, SnippetStatus, SnippetVariant } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { LLMChain, RetrievalQAChain } from "langchain/chains"; import { OpenAIEmbeddings } from "langchain/embeddings/openai"; @@ -25,7 +19,7 @@ import { PrismaVectorStore } from "langchain/vectorstores/prisma"; import { difference } from "remeda"; import { inngest } from "../inngest"; -import { createOpenAILangchainClient } from "../llm/openai"; +import { createOpenAILangchainClient } from "../llm/langchain"; import { embedWithCache } from "../utils/embeddings"; const log = aiLogger("snippets"); diff --git a/packages/core/src/models/statistics.ts b/packages/core/src/models/statistics.ts index 3e05d685c..7a37061da 100644 --- a/packages/core/src/models/statistics.ts +++ b/packages/core/src/models/statistics.ts @@ -15,7 +15,7 @@ export class Statistics { const sql = this.getStatsUpsertSql( Prisma.raw("median-generation-total-duration-ms"), Prisma.raw( - `percentile_cont(0.5) WITHIN GROUP (ORDER BY (EXTRACT(EPOCH FROM (completed_at - created_at)) * 1000))::INT`, + "percentile_cont(0.5) WITHIN GROUP (ORDER BY (EXTRACT(EPOCH FROM (completed_at - created_at)) * 1000))::INT", ), ); await this.prisma.$executeRaw(sql); @@ -25,7 +25,7 @@ export class Statistics { const sql = this.getStatsUpsertSql( Prisma.raw("mean-generation-total-duration-ms"), Prisma.raw( - `AVG((EXTRACT(EPOCH FROM (completed_at - created_at)) * 1000))::INT`, + "AVG((EXTRACT(EPOCH FROM (completed_at - created_at)) * 1000))::INT", ), ); await this.prisma.$executeRaw(sql); @@ -35,7 +35,7 @@ export class Statistics { const sql = this.getStatsUpsertSql( Prisma.raw("median-generation-llm-duration-ms"), Prisma.raw( - `ROUND(percentile_cont(0.5) WITHIN GROUP (ORDER BY llm_time_taken))`, + "ROUND(percentile_cont(0.5) WITHIN GROUP (ORDER BY llm_time_taken))", ), ); await this.prisma.$executeRaw(sql); @@ -44,7 +44,7 @@ export class Statistics { async updateMeanGenerationLlmDurations() { const sql = this.getStatsUpsertSql( Prisma.raw("mean-generation-llm-duration-ms"), - Prisma.raw(`AVG(generations.llm_time_taken)::INT`), + Prisma.raw("AVG(generations.llm_time_taken)::INT"), ); await this.prisma.$executeRaw(sql); } diff --git a/packages/core/src/models/transcript.ts b/packages/core/src/models/transcript.ts index 51808e70e..d6698e32c 100644 --- a/packages/core/src/models/transcript.ts +++ b/packages/core/src/models/transcript.ts @@ -1,4 +1,5 @@ import type { PrismaClientWithAccelerate } from "@oakai/db"; +import { aiLogger } from "@oakai/logger"; import { Document } from "langchain/document"; import { StructuredOutputParser } from "langchain/output_parsers"; import { PromptTemplate } from "langchain/prompts"; @@ -6,10 +7,9 @@ import { RunnableSequence } from "langchain/schema/runnable"; import { RecursiveCharacterTextSplitter } from "langchain/text_splitter"; import { z } from "zod"; -import { createOpenAILangchainClient } from "../llm/openai"; -import { aiLogger } from "@oakai/logger"; +import { createOpenAILangchainClient } from "../llm/langchain"; -const log = aiLogger("transcripts") +const log = aiLogger("transcripts"); interface TranscriptWithRaw { raw: string; diff --git a/packages/core/src/rag/categorisation.ts b/packages/core/src/rag/categorisation.ts new file mode 100644 index 000000000..50a364e5c --- /dev/null +++ b/packages/core/src/rag/categorisation.ts @@ -0,0 +1,16 @@ +import z from "zod"; + +// Make a new Zod schema for a response from OpenAI for the categoriseKeyStageAndSubject function + +export const CategoriseKeyStageAndSubjectResponse = z.object({ + keyStage: z.string().optional().nullable(), + subject: z.string().optional().nullable(), + title: z.string().optional().nullable(), + topic: z.string().optional().nullable(), + error: z.string().optional(), + reasoning: z.string().optional().nullable(), +}); + +export type CategorisedKeyStageAndSubject = z.infer< + typeof CategoriseKeyStageAndSubjectResponse +>; diff --git a/packages/core/src/rag/index.ts b/packages/core/src/rag/index.ts index 8ce25e86f..ed115b234 100644 --- a/packages/core/src/rag/index.ts +++ b/packages/core/src/rag/index.ts @@ -16,7 +16,6 @@ import { CohereClient } from "cohere-ai"; import type { RerankResponse } from "cohere-ai/api"; import type { ChatCompletionMessageParam } from "openai/resources/chat/completions"; import { Md5 } from "ts-md5"; -import z from "zod"; import { DEFAULT_CATEGORISE_MODEL } from "../../../aila/src/constants"; import type { OpenAICompletionWithLoggingOptions } from "../../../aila/src/lib/openai/OpenAICompletionWithLogging"; @@ -24,51 +23,16 @@ import { OpenAICompletionWithLogging } from "../../../aila/src/lib/openai/OpenAI import type { JsonValue } from "../models/prompts"; import { slugify } from "../utils/slugify"; import { keyStages, subjects } from "../utils/subjects"; +import { CategoriseKeyStageAndSubjectResponse } from "./categorisation"; +import type { + FilterOptions, + KeyStageAndSubject, + LessonPlanWithPartialLesson, + SimilarityResultWithScore, +} from "./types"; const log = aiLogger("rag"); -interface FilterOptions { - key_stage_id?: object; - subject_id?: object; -} - -export interface LessonPlanWithPartialLesson extends LessonPlan { - lesson: { - id: string; - slug: string; - title: string; - }; -} - -export type SimilarityResultWithScore = [ - // eslint-disable-next-line @typescript-eslint/consistent-type-imports - import("@langchain/core/documents").DocumentInterface< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - Record - >, - number, -]; - -export interface KeyStageAndSubject { - keyStage?: KeyStage; - subject?: Subject; -} - -// Make a new Zod schema for a response from OpenAI for the categoriseKeyStageAndSubject function - -export const CategoriseKeyStageAndSubjectResponse = z.object({ - keyStage: z.string().optional().nullable(), - subject: z.string().optional().nullable(), - title: z.string().optional().nullable(), - topic: z.string().optional().nullable(), - error: z.string().optional(), - reasoning: z.string().optional().nullable(), -}); - -export type CategorisedKeyStageAndSubject = z.infer< - typeof CategoriseKeyStageAndSubjectResponse ->; - export class RAG { prisma: PrismaClientWithAccelerate; private _chatMeta: OpenAICompletionWithLoggingOptions; diff --git a/packages/core/src/rag/types.ts b/packages/core/src/rag/types.ts new file mode 100644 index 000000000..d3151fd8b --- /dev/null +++ b/packages/core/src/rag/types.ts @@ -0,0 +1,27 @@ +import type { KeyStage, LessonPlan, Subject } from "@prisma/client"; + +export interface FilterOptions { + key_stage_id?: object; + subject_id?: object; +} + +export interface LessonPlanWithPartialLesson extends LessonPlan { + lesson: { + id: string; + slug: string; + title: string; + }; +} + +export type SimilarityResultWithScore = [ + import("@langchain/core/documents").DocumentInterface< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Record + >, + number, +]; + +export interface KeyStageAndSubject { + keyStage?: KeyStage; + subject?: Subject; +} diff --git a/packages/core/src/utils/ailaModeration/moderationSchema.ts b/packages/core/src/utils/ailaModeration/moderationSchema.ts index eaf59b0e8..8c1bc6612 100644 --- a/packages/core/src/utils/ailaModeration/moderationSchema.ts +++ b/packages/core/src/utils/ailaModeration/moderationSchema.ts @@ -38,7 +38,7 @@ export const moderationCategoriesSchema = z.array( z.literal("t/encouragement-violence"), ]) .describe( - `If the content scores less than 5 for any group, specify the categories on which it failed.`, + "If the content scores less than 5 for any group, specify the categories on which it failed.", ), ); @@ -57,7 +57,7 @@ export const moderationResponseSchema = z.object({ p: likertScale.describe("Physical activity and safety score"), t: likertScale.describe("Toxic score"), }), - justification: z.string().describe(`Add justification for your scores.`), + justification: z.string().describe("Add justification for your scores."), categories: moderationCategoriesSchema, }); diff --git a/packages/core/src/utils/getCaptionsFromFile.ts b/packages/core/src/utils/getCaptionsFromFile.ts index dc188493a..96551c789 100644 --- a/packages/core/src/utils/getCaptionsFromFile.ts +++ b/packages/core/src/utils/getCaptionsFromFile.ts @@ -1,6 +1,6 @@ import { Storage } from "@google-cloud/storage"; import { aiLogger } from "@oakai/logger"; -import type { Cue} from "webvtt-parser"; +import type { Cue } from "webvtt-parser"; import { WebVTTParser } from "webvtt-parser"; const log = aiLogger("transcripts"); diff --git a/packages/core/src/utils/moderation.ts b/packages/core/src/utils/moderation.ts index 31616ea24..3fec874e1 100644 --- a/packages/core/src/utils/moderation.ts +++ b/packages/core/src/utils/moderation.ts @@ -33,7 +33,7 @@ const ENGLISH_THRESHOLD = if (MODERATE_LANGUAGE && Number.isNaN(ENGLISH_THRESHOLD)) { throw new Error( - `Missing or invalid env var MODERATION_THRESHOLD_ENGLISH_LANGUAGE`, + "Missing or invalid env var MODERATION_THRESHOLD_ENGLISH_LANGUAGE", ); } diff --git a/packages/core/src/utils/textify.ts b/packages/core/src/utils/textify.ts new file mode 100644 index 000000000..c3eefe420 --- /dev/null +++ b/packages/core/src/utils/textify.ts @@ -0,0 +1,12 @@ +import yaml from "yaml"; + +// Simplifies the input to a string for embedding +export function textify(input: string | string[] | object): string { + if (Array.isArray(input)) { + return input.map((row) => textify(row)).join("\n"); + } else if (typeof input === "object") { + return yaml.stringify(input); + } else { + return input; + } +} diff --git a/packages/db/client/index.ts b/packages/db/client/index.ts new file mode 100644 index 000000000..636ff043f --- /dev/null +++ b/packages/db/client/index.ts @@ -0,0 +1,49 @@ +import { aiLogger } from "@oakai/logger"; +import { PrismaClient } from "@prisma/client"; +import { withAccelerate } from "@prisma/extension-accelerate"; + +const log = aiLogger("db"); + +const createPrismaClient = () => { + const client = new PrismaClient({ + log: [ + { emit: "stdout", level: "error" }, + // Prisma supports DEBUG strings (eg: prisma*, prisma:client), but they're noisy debug messages. + // Instead, we forward the typical logs based on ai:db + { emit: "event", level: "query" }, + { emit: "event", level: "warn" }, + ], + }); + + client.$on("query", (e) => { + log.info(e.query); + }); + client.$on("warn", (e) => { + log.info(e.message); + }); + + return client.$extends(withAccelerate()); +}; + +// Create an instance to extract the type +const extendedPrisma = createPrismaClient(); +export type PrismaClientWithAccelerate = typeof extendedPrisma; + +declare global { + // allow global `var` declarations + // eslint-disable-next-line no-var + var prisma: PrismaClientWithAccelerate | undefined; +} + +let prisma: PrismaClientWithAccelerate; + +if (process.env.NODE_ENV === "production") { + prisma = createPrismaClient(); +} else { + if (!global.prisma) { + global.prisma = createPrismaClient(); + } + prisma = global.prisma; +} + +export { prisma }; diff --git a/packages/db/index.ts b/packages/db/index.ts index 555228f7c..534b81b20 100644 --- a/packages/db/index.ts +++ b/packages/db/index.ts @@ -1,53 +1,4 @@ -import { aiLogger } from "@oakai/logger"; -import { PrismaClient } from "@prisma/client"; -import { withAccelerate } from "@prisma/extension-accelerate"; - -const log = aiLogger("db"); - -const createPrismaClient = () => { - const client = new PrismaClient({ - log: [ - { emit: "stdout", level: "error" }, - // Prisma supports DEBUG strings (eg: prisma*, prisma:client), but they're noisy debug messages. - // Instead, we forward the typical logs based on ai:db - { emit: "event", level: "query" }, - { emit: "event", level: "warn" }, - ], - }); - - client.$on("query", (e) => { - log.info(e.query); - }); - client.$on("warn", (e) => { - log.info(e.message); - }); - - return client.$extends(withAccelerate()); -}; - -// Create an instance to extract the type -const extendedPrisma = createPrismaClient(); -export type PrismaClientWithAccelerate = typeof extendedPrisma; - -declare global { - // allow global `var` declarations - // eslint-disable-next-line no-var - var prisma: PrismaClientWithAccelerate | undefined; -} - -let prisma: PrismaClientWithAccelerate; - -if (process.env.NODE_ENV === "production") { - prisma = createPrismaClient(); -} else { - if (!global.prisma) { - global.prisma = createPrismaClient(); - } - prisma = global.prisma; -} - -export { prisma }; - +export * from "./client"; export * from "@prisma/client"; export * from "./prisma/zod-schemas"; export * from "./schemas"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d655dc25a..d2cd61e20 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -550,6 +550,9 @@ importers: eslint: specifier: ^8.56.0 version: 8.56.0 + eslint-plugin-turbo: + specifier: ^2.2.3 + version: 2.2.3(eslint@8.56.0) jest: specifier: ^29.7.0 version: 29.7.0(@types/node@18.18.5)(ts-node@10.9.2) @@ -8013,7 +8016,7 @@ packages: resolve: 1.22.8 rollup: 3.29.5 stacktrace-parser: 0.1.10 - webpack: 5.93.0 + webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - '@opentelemetry/api' - '@opentelemetry/core' @@ -8146,7 +8149,7 @@ packages: '@sentry/bundler-plugin-core': 2.22.3 unplugin: 1.0.1 uuid: 9.0.1 - webpack: 5.93.0 + webpack: 5.93.0(esbuild@0.21.5) transitivePeerDependencies: - encoding - supports-color @@ -12898,7 +12901,6 @@ packages: /dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} engines: {node: '>=12'} - dev: false /dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} @@ -13561,7 +13563,6 @@ packages: dependencies: dotenv: 16.0.3 eslint: 8.56.0 - dev: false /eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} @@ -22436,31 +22437,6 @@ packages: serialize-javascript: 6.0.2 terser: 5.31.3 webpack: 5.93.0(esbuild@0.21.5) - dev: true - - /terser-webpack-plugin@5.3.10(webpack@5.93.0): - resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==} - engines: {node: '>= 10.13.0'} - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true - dependencies: - '@jridgewell/trace-mapping': 0.3.25 - jest-worker: 27.5.1 - schema-utils: 3.3.0 - serialize-javascript: 6.0.2 - terser: 5.31.3 - webpack: 5.93.0 - dev: false /terser@5.31.3: resolution: {integrity: sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==} @@ -23650,46 +23626,6 @@ packages: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} dev: true - /webpack@5.93.0: - resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} - engines: {node: '>=10.13.0'} - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.7 - '@types/estree': 1.0.5 - '@webassemblyjs/ast': 1.12.1 - '@webassemblyjs/wasm-edit': 1.12.1 - '@webassemblyjs/wasm-parser': 1.12.1 - acorn: 8.12.1 - acorn-import-attributes: 1.9.5(acorn@8.12.1) - browserslist: 4.23.3 - chrome-trace-event: 1.0.4 - enhanced-resolve: 5.17.1 - es-module-lexer: 1.5.4 - eslint-scope: 5.1.1 - events: 3.3.0 - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 - mime-types: 2.1.35 - neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.10(webpack@5.93.0) - watchpack: 2.4.1 - webpack-sources: 3.2.3 - transitivePeerDependencies: - - '@swc/core' - - esbuild - - uglify-js - dev: false - /webpack@5.93.0(esbuild@0.21.5): resolution: {integrity: sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==} engines: {node: '>=10.13.0'} @@ -23728,7 +23664,6 @@ packages: - '@swc/core' - esbuild - uglify-js - dev: true /webvtt-parser@2.2.0: resolution: {integrity: sha512-FzmaED+jZyt8SCJPTKbSsimrrnQU8ELlViE1wuF3x1pgiQUM8Llj5XWj2j/s6Tlk71ucPfGSMFqZWBtKn/0uEA==} From c9cb82b2df566b0ec2a952b21d22e87d61fcd094 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 15:38:18 +0000 Subject: [PATCH 047/127] fix: await the enqueue method in web actions (#293) --- apps/nextjs/src/app/api/chat/webActionsPlugin.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index a8e77900c..46daa425b 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -33,11 +33,11 @@ export const createWebActionsPlugin: PluginCreator = ( prisma, SafetyViolations, ); - enqueue(heliconeErrorMessage); + await enqueue(heliconeErrorMessage); } if (error instanceof Error) { - enqueue({ + await enqueue({ type: "error", message: error.message, value: `Sorry, an error occurred: ${error.message}`, @@ -80,7 +80,7 @@ export const createWebActionsPlugin: PluginCreator = ( } catch (error) { if (error instanceof UserBannedError) { log.info("User is banned, queueing account lock message"); - enqueue({ + await enqueue({ type: "action", action: "SHOW_ACCOUNT_LOCKED", }); From a7eaefa5acef4d89a60dd79301cdf4be61db6830 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 16:03:23 +0000 Subject: [PATCH 048/127] fix: pages that do not load data do not need to be async (#296) --- apps/nextjs/src/app/aila/[id]/layout.tsx | 4 +--- apps/nextjs/src/app/aila/help/page.tsx | 2 +- apps/nextjs/src/app/aila/page.tsx | 4 ++-- apps/nextjs/src/app/faqs/page.tsx | 2 +- apps/nextjs/src/app/generations/page.tsx | 2 +- apps/nextjs/src/app/legal/account-locked/page.tsx | 2 +- apps/nextjs/src/app/lesson-planner/page.tsx | 2 +- apps/nextjs/src/app/lesson-planner/preview/page.tsx | 2 +- apps/nextjs/src/app/quiz-designer/page.tsx | 2 +- apps/nextjs/src/app/quiz-designer/preview/page.tsx | 2 +- apps/nextjs/src/app/test-support/clerk/page.tsx | 2 +- 11 files changed, 12 insertions(+), 14 deletions(-) diff --git a/apps/nextjs/src/app/aila/[id]/layout.tsx b/apps/nextjs/src/app/aila/[id]/layout.tsx index a31f4fe7f..89534d9e0 100644 --- a/apps/nextjs/src/app/aila/[id]/layout.tsx +++ b/apps/nextjs/src/app/aila/[id]/layout.tsx @@ -2,9 +2,7 @@ interface ChatLayoutProps { children: React.ReactNode; } -export default async function ChatLayout({ - children, -}: Readonly) { +export default function ChatLayout({ children }: Readonly) { return (
diff --git a/apps/nextjs/src/app/aila/help/page.tsx b/apps/nextjs/src/app/aila/help/page.tsx index d6598506f..a49d678c5 100644 --- a/apps/nextjs/src/app/aila/help/page.tsx +++ b/apps/nextjs/src/app/aila/help/page.tsx @@ -1,5 +1,5 @@ import Help from "."; -export default async function HelpPage() { +export default function HelpPage() { return ; } diff --git a/apps/nextjs/src/app/aila/page.tsx b/apps/nextjs/src/app/aila/page.tsx index e5d4e01a5..4d8e8e054 100644 --- a/apps/nextjs/src/app/aila/page.tsx +++ b/apps/nextjs/src/app/aila/page.tsx @@ -5,11 +5,11 @@ import { redirect } from "next/navigation"; import { ChatStart } from "@/components/AppComponents/Chat/chat-start"; import Layout from "@/components/AppComponents/Layout"; -export default async function IndexPage() { +export default function IndexPage() { const clerkAuthentication = auth(); const { userId }: { userId: string | null } = clerkAuthentication; if (!userId) { - redirect(`/sign-in?next=/aila`); + redirect("/sign-in?next=/aila"); } return ( diff --git a/apps/nextjs/src/app/faqs/page.tsx b/apps/nextjs/src/app/faqs/page.tsx index 43148a4c4..4a7701661 100644 --- a/apps/nextjs/src/app/faqs/page.tsx +++ b/apps/nextjs/src/app/faqs/page.tsx @@ -1,6 +1,6 @@ import FAQPage from "."; -const FAQ = async () => { +const FAQ = () => { return ; }; diff --git a/apps/nextjs/src/app/generations/page.tsx b/apps/nextjs/src/app/generations/page.tsx index 4d70f247d..718eb0df7 100644 --- a/apps/nextjs/src/app/generations/page.tsx +++ b/apps/nextjs/src/app/generations/page.tsx @@ -1,3 +1,3 @@ -export default async function GenerationsPage() { +export default function GenerationsPage() { return null; } diff --git a/apps/nextjs/src/app/legal/account-locked/page.tsx b/apps/nextjs/src/app/legal/account-locked/page.tsx index 5cb497153..d1f64fc61 100644 --- a/apps/nextjs/src/app/legal/account-locked/page.tsx +++ b/apps/nextjs/src/app/legal/account-locked/page.tsx @@ -1,5 +1,5 @@ import AccountLocked from "./account-locked"; -export default async function SuspendedPage() { +export default function SuspendedPage() { return ; } diff --git a/apps/nextjs/src/app/lesson-planner/page.tsx b/apps/nextjs/src/app/lesson-planner/page.tsx index 089e4c78c..d6cadf0fe 100644 --- a/apps/nextjs/src/app/lesson-planner/page.tsx +++ b/apps/nextjs/src/app/lesson-planner/page.tsx @@ -1,5 +1,5 @@ import LessonPlannerPage from "."; -export default async function LessonPlanner() { +export default function LessonPlanner() { return ; } diff --git a/apps/nextjs/src/app/lesson-planner/preview/page.tsx b/apps/nextjs/src/app/lesson-planner/preview/page.tsx index 466483d34..24a1db312 100644 --- a/apps/nextjs/src/app/lesson-planner/preview/page.tsx +++ b/apps/nextjs/src/app/lesson-planner/preview/page.tsx @@ -1,5 +1,5 @@ import PreviewRedirect from "./preview-redirect"; -export default async function PreviewRedirectPage() { +export default function PreviewRedirectPage() { return ; } diff --git a/apps/nextjs/src/app/quiz-designer/page.tsx b/apps/nextjs/src/app/quiz-designer/page.tsx index 33f6d834e..42876ecdc 100644 --- a/apps/nextjs/src/app/quiz-designer/page.tsx +++ b/apps/nextjs/src/app/quiz-designer/page.tsx @@ -1,5 +1,5 @@ import QuizDesignerPage from "./quiz-designer-page"; -export default async function QuizDesigner() { +export default function QuizDesigner() { return ; } diff --git a/apps/nextjs/src/app/quiz-designer/preview/page.tsx b/apps/nextjs/src/app/quiz-designer/preview/page.tsx index 466483d34..24a1db312 100644 --- a/apps/nextjs/src/app/quiz-designer/preview/page.tsx +++ b/apps/nextjs/src/app/quiz-designer/preview/page.tsx @@ -1,5 +1,5 @@ import PreviewRedirect from "./preview-redirect"; -export default async function PreviewRedirectPage() { +export default function PreviewRedirectPage() { return ; } diff --git a/apps/nextjs/src/app/test-support/clerk/page.tsx b/apps/nextjs/src/app/test-support/clerk/page.tsx index 6a01d80c2..d2d56463f 100644 --- a/apps/nextjs/src/app/test-support/clerk/page.tsx +++ b/apps/nextjs/src/app/test-support/clerk/page.tsx @@ -5,7 +5,7 @@ import { notFound } from "next/navigation"; * This is a minimal page that gives it access to a clerk instance */ -export default async function TestSupportSignIn() { +export default function TestSupportSignIn() { if ( process.env.NODE_ENV !== "development" && process.env.VERCEL_ENV !== "preview" From cd35dc15b97960a18196119540cc5848f58eaa47 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 16:09:19 +0000 Subject: [PATCH 049/127] fix: rename accordian to accordion (#297) --- apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx | 2 +- ...empty-screen-accordian.tsx => empty-screen-accordion.tsx} | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) rename apps/nextjs/src/components/AppComponents/Chat/{empty-screen-accordian.tsx => empty-screen-accordion.tsx} (98%) diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx index 40ed7c88a..b15cbc9d4 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx @@ -17,7 +17,7 @@ import { trpc } from "@/utils/trpc"; import { useDialog } from "../DialogContext"; import ChatPanelDisclaimer from "./chat-panel-disclaimer"; import { ChatStartForm } from "./chat-start-form"; -import EmptyScreenAccordion from "./empty-screen-accordian"; +import EmptyScreenAccordion from "./empty-screen-accordion"; const log = aiLogger("chat"); diff --git a/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordian.tsx b/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx similarity index 98% rename from apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordian.tsx rename to apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx index 1aee07a50..4a4efa09f 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordian.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/empty-screen-accordion.tsx @@ -157,11 +157,12 @@ const AccordionItem = React.forwardRef( AccordionItem.displayName = "AccordionItem"; const AccordionTrigger = React.forwardRef< - HTMLDivElement, + HTMLButtonElement, AccordionTriggerProps ->(({ children, ...props }) => ( +>(({ children, ...props }, forwardedRef) => ( From be791584805d219db1da4949a8a32c0a0f40b589 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 16:09:42 +0000 Subject: [PATCH 050/127] fix: completion and system messages do not need to be async (#294) --- packages/aila/src/core/chat/AilaChat.ts | 6 +++--- packages/aila/src/core/chat/AilaStreamHandler.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 6e86b29f6..37ccfc79a 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -156,7 +156,7 @@ export class AilaChat implements AilaChatService { }); } - public async systemMessage() { + public systemMessage() { invariant(this._generation?.systemPrompt, "System prompt not initialised"); return { id: generateMessageId({ role: "system" }), @@ -165,12 +165,12 @@ export class AilaChat implements AilaChatService { }; } - public async completionMessages() { + public completionMessages() { const reducedMessages = this._promptBuilder.reduceMessagesForPrompt( this._messages, ); - const systemMessage = await this.systemMessage(); + const systemMessage = this.systemMessage(); const applicableMessages: Message[] = [systemMessage, ...reducedMessages]; // only send if (this._aila?.lesson.hasSetInitialState) { diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index caa586fe9..bf7c1098a 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -90,7 +90,7 @@ export class AilaStreamHandler { type: "comment", value: "CHAT_START", }); - const messages = await this._chat.completionMessages(); + const messages = this._chat.completionMessages(); this._streamReader = await this._chat.createChatCompletionObjectStream(messages); this._isStreaming = true; From 730dbb94a786f9f8a6e03e9ed4d9ff90853522fd Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 16:11:02 +0000 Subject: [PATCH 051/127] fix: await persisting the generation (#292) --- packages/aila/src/core/chat/AilaChat.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 37ccfc79a..3a71f524c 100644 --- a/packages/aila/src/core/chat/AilaChat.ts +++ b/packages/aila/src/core/chat/AilaChat.ts @@ -276,7 +276,7 @@ export class AilaChat implements AilaChatService { invariant(responseText, "Response text not set"); await this._generation.complete({ status, responseText }); } - this._generation.persist(status); + await this._generation.persist(status); } private async persistChat() { From 1212eb50f5e2e3ee913b57ec7bffc2fcabe981c8 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 16:56:13 +0000 Subject: [PATCH 052/127] fix: linting for app components (#295) --- .../Chat/ChatModerationDisplay.stories.tsx | 2 +- .../Chat/Chat/ChatModerationDisplay.tsx | 2 +- .../Chat/Chat/hooks/useAilaStreamingStatus.ts | 8 ++-- .../AppComponents/Chat/Chat/utils/index.ts | 10 ++-- .../AppComponents/Chat/chat-lhs-header.tsx | 4 +- .../AppComponents/Chat/chat-list.tsx | 3 +- .../Chat/chat-right-hand-side-lesson.tsx | 4 +- .../AppComponents/Chat/chat-share-dialog.tsx | 2 +- .../AppComponents/Chat/chat-start.tsx | 3 +- .../Chat/guidance-required.stories.tsx | 2 +- .../AppComponents/Chat/prompt-form.tsx | 1 + .../AppComponents/Chat/sidebar-list.tsx | 2 +- .../Chat/toxic-moderation-view.tsx | 2 +- .../AppComponents/Chat/ui/button.tsx | 48 +++++++++---------- .../AppComponents/Chat/ui/tooltip.tsx | 25 +++++----- .../ComparativeJudgement/PreviewContent.tsx | 10 ++-- .../ComparativeJudgement/index.tsx | 2 +- .../components/AppComponents/Layout/index.tsx | 2 +- .../AppComponents/QuizDesigner/ExportMenu.tsx | 2 +- .../QuizQuestionRow/ControllerRow.tsx | 2 +- ...eButttonGroup.tsx => ShareButtonGroup.tsx} | 0 .../GenerationInputAndText.tsx | 2 +- .../download/DownloadAllButton.tsx | 10 ++-- 23 files changed, 74 insertions(+), 74 deletions(-) rename apps/nextjs/src/components/AppComponents/common/{ShareButttonGroup.tsx => ShareButtonGroup.tsx} (100%) diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.stories.tsx index dcea3d3d2..84aa6955e 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.stories.tsx @@ -1,4 +1,4 @@ -import { type PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import type { Meta, StoryObj } from "@storybook/react"; import { ChatModerationDisplay } from "./ChatModerationDisplay"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx index 82f53e05f..fbf72ab34 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/ChatModerationDisplay.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { type PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { Flex } from "@radix-ui/themes"; import ToxicModerationView from "../toxic-moderation-view"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts index 50168c43e..3ea9adca2 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/hooks/useAilaStreamingStatus.ts @@ -20,8 +20,8 @@ export const useAilaStreamingStatus = ({ messages: Message[]; }): AilaStreamingStatus => { const ailaStreamingStatus = useMemo(() => { - const moderationStart = `MODERATION_START`; - const chatStart = `CHAT_START`; + const moderationStart = "MODERATION_START"; + const chatStart = "CHAT_START"; if (messages.length === 0) return "Idle"; const lastMessage = messages[messages.length - 1]; @@ -33,8 +33,8 @@ export const useAilaStreamingStatus = ({ } else if (content.includes(moderationStart)) { return "Moderating"; } else if ( - content.includes(`"type":"prompt"`) || - content.includes(`\\"type\\":\\"prompt\\"`) + content.includes('"type":"prompt"') || + content.includes('\\"type\\":\\"prompt\\"') ) { return "StreamingChatResponse"; } else if (content.includes(chatStart)) { diff --git a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts index 1f4cac571..48df113c6 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts +++ b/apps/nextjs/src/components/AppComponents/Chat/Chat/utils/index.ts @@ -1,12 +1,12 @@ -import { type LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; +import type { LooseLessonPlan } from "@oakai/aila/src/protocol/schema"; import { aiLogger } from "@oakai/logger"; -import { type Message } from "ai/react"; +import type { Message } from "ai/react"; const log = aiLogger("chat"); export function findMessageIdFromContent({ content }: { content: string }) { return content - .split(`␞`) + .split("␞") .map((s) => { try { return JSON.parse(s.trim()); @@ -21,12 +21,12 @@ export function findMessageIdFromContent({ content }: { content: string }) { export function findLatestServerSideState(workingMessages: Message[]) { log.info("Finding latest server-side state", { workingMessages }); const lastMessage = workingMessages[workingMessages.length - 1]; - if (!lastMessage?.content.includes(`"type":"state"`)) { + if (!lastMessage?.content.includes('"type":"state"')) { log.info("No server state found"); return; } const state: LooseLessonPlan = lastMessage.content - .split(`␞`) + .split("␞") .map((s) => { try { return JSON.parse(s.trim()); diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx index d0acd9c16..f01676dec 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-lhs-header.tsx @@ -40,13 +40,13 @@ const ChatLhsHeader = ({ New lesson
-
+
diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx index fa0c5c7f6..5a6aaff8b 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-list.tsx @@ -32,7 +32,8 @@ function DemoLimitMessage({ id }: Readonly<{ id: string }>) { message={{ id: "demo-limit", role: "assistant", - content: `{"type": "error", "message": "**Your lesson is complete**\\nYou can no longer edit this lesson. [Create new lesson.](/aila)"}`, + content: + '{"type": "error", "message": "**Your lesson is complete**\\nYou can no longer edit this lesson. [Create new lesson.](/aila)"}', }} persistedModerations={[]} separator={} diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx index 21685515c..7f3b515e1 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-right-hand-side-lesson.tsx @@ -61,7 +61,7 @@ const ChatRightHandSideLesson = ({ const endOfDocRef = useRef(null); return (
{ closeMobileLessonPullOut(); }} - className={`${demo.isDemoUser ? `mt-25` : ``} flex items-center justify-center gap-3 `} + className={`${demo.isDemoUser ? "mt-25" : ""} flex items-center justify-center gap-3 `} > diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx index 08392232d..f6ab67705 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-share-dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react"; import { toast } from "react-hot-toast"; import { aiLogger } from "@oakai/logger"; -import { type DialogProps } from "@radix-ui/react-dialog"; +import type { DialogProps } from "@radix-ui/react-dialog"; import { Button } from "@/components/AppComponents/Chat/ui/button"; import { diff --git a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx index b15cbc9d4..cbb91a319 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/chat-start.tsx @@ -24,7 +24,8 @@ const log = aiLogger("chat"); const exampleMessages = [ { heading: "History • Key stage 3 • The end of Roman Britain ", - message: `Create a lesson plan about The End of Roman Britain for Key Stage 3 History`, + message: + "Create a lesson plan about The End of Roman Britain for Key Stage 3 History", }, ]; diff --git a/apps/nextjs/src/components/AppComponents/Chat/guidance-required.stories.tsx b/apps/nextjs/src/components/AppComponents/Chat/guidance-required.stories.tsx index a85f96a87..8d7b547d3 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/guidance-required.stories.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/guidance-required.stories.tsx @@ -1,4 +1,4 @@ -import { type PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import type { Meta, StoryObj } from "@storybook/react"; import { GuidanceRequired } from "./guidance-required"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx b/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx index 41d453d46..04e600d87 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/prompt-form.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useRef } from "react"; + import type { UseChatHelpers } from "ai/react"; import { diff --git a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx index 9f5faea5f..d68c6860a 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/sidebar-list.tsx @@ -4,7 +4,7 @@ import { ClearHistory } from "@/components/AppComponents/Chat/clear-history"; import { SidebarItems } from "@/components/AppComponents/Chat/sidebar-items"; import { trpc } from "@/utils/trpc"; -export async function SidebarList() { +export function SidebarList() { const chatsRequest = trpc.chat.appSessions.getSidebarChats.useQuery(); const chats = chatsRequest.data; diff --git a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx index 2341659e9..853024644 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/toxic-moderation-view.tsx @@ -1,6 +1,6 @@ import Textarea from "react-textarea-autosize"; -import { type PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; +import type { PersistedModerationBase } from "@oakai/core/src/utils/ailaModeration/moderationSchema"; import { useModerationFeedbackSurvey } from "hooks/surveys/useModerationFeedbackSurvey"; import { useRouter } from "next/navigation"; diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx index 131a0d415..8f4e0aa35 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/button.tsx @@ -6,53 +6,53 @@ import { cva, type VariantProps } from "class-variance-authority"; import { cn } from "@/lib/utils"; const buttonVariants = cva( - 'inline-flex items-center justify-center rounded-md text-sm font-medium shadow ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50', + "inline-flex items-center justify-center rounded-md text-sm font-medium shadow ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", { variants: { variant: { default: - 'bg-primary text-primary-foreground shadow-md hover:bg-primary/90', + "bg-primary text-primary-foreground shadow-md hover:bg-primary/90", destructive: - 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + "bg-destructive text-destructive-foreground hover:bg-destructive/90", outline: - 'border border-input hover:bg-accent hover:text-accent-foreground', + "border border-input hover:bg-accent hover:text-accent-foreground", secondary: - 'bg-secondary text-secondary-foreground hover:bg-secondary/80', - ghost: 'shadow-none hover:bg-accent hover:text-accent-foreground', - link: 'text-primary underline-offset-4 shadow-none hover:underline' + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "shadow-none hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 shadow-none hover:underline", }, size: { - default: 'h-18 px-10 py-7', - sm: 'h-18 rounded-md px-9', - lg: 'h-22 rounded-md px-18', - icon: 'h-18 w-18 p-0' - } + default: "h-18 px-10 py-7", + sm: "h-18 rounded-md px-9", + lg: "h-22 rounded-md px-18", + icon: "h-18 w-18 p-0", + }, }, defaultVariants: { - variant: 'default', - size: 'default' - } - } -) + variant: "default", + size: "default", + }, + }, +); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean + asChild?: boolean; } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : 'button' + const Comp = asChild ? Slot : "button"; return ( - ) - } -) -Button.displayName = 'Button' + ); + }, +); +Button.displayName = "Button"; -export { Button, buttonVariants } +export { Button, buttonVariants }; diff --git a/apps/nextjs/src/components/AppComponents/Chat/ui/tooltip.tsx b/apps/nextjs/src/components/AppComponents/Chat/ui/tooltip.tsx index 2ba086a4c..baa2503dc 100644 --- a/apps/nextjs/src/components/AppComponents/Chat/ui/tooltip.tsx +++ b/apps/nextjs/src/components/AppComponents/Chat/ui/tooltip.tsx @@ -1,15 +1,16 @@ -'use client' +"use client"; -import * as React from 'react' -import * as TooltipPrimitive from '@radix-ui/react-tooltip' +import * as React from "react"; -import { cn } from '@/lib/utils' +import * as TooltipPrimitive from "@radix-ui/react-tooltip"; -const TooltipProvider = TooltipPrimitive.Provider +import { cn } from "@/lib/utils"; -const Tooltip = TooltipPrimitive.Root +const TooltipProvider = TooltipPrimitive.Provider; -const TooltipTrigger = TooltipPrimitive.Trigger +const Tooltip = TooltipPrimitive.Root; + +const TooltipTrigger = TooltipPrimitive.Trigger; const TooltipContent = React.forwardRef< React.ElementRef, @@ -19,12 +20,12 @@ const TooltipContent = React.forwardRef< ref={ref} sideOffset={sideOffset} className={cn( - 'z-50 overflow-hidden rounded-md border bg-popover px-9 py-5.5 text-xs font-medium text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-5 data-[side=left]:slide-in-from-right-5 data-[side=right]:slide-in-from-left-5 data-[side=top]:slide-in-from-bottom-5', - className + "py-5.5 z-50 overflow-hidden rounded-md border bg-popover px-9 text-xs font-medium text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-5 data-[side=left]:slide-in-from-right-5 data-[side=right]:slide-in-from-left-5 data-[side=top]:slide-in-from-bottom-5", + className, )} {...props} /> -)) -TooltipContent.displayName = TooltipPrimitive.Content.displayName +)); +TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } +export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; diff --git a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx index 1277cf06f..c742826b3 100644 --- a/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx +++ b/apps/nextjs/src/components/AppComponents/ComparativeJudgement/PreviewContent.tsx @@ -1,8 +1,5 @@ import { Box, Flex, Text } from "@radix-ui/themes"; -import type { - AnswerAndDistractor, - OptionWithPrompt, -} from "ai-apps/comparative-judgement/state/types"; +import type { OptionWithPrompt } from "ai-apps/comparative-judgement/state/types"; import { sortAlphabetically } from "@/utils/alphabetiseArray"; @@ -16,8 +13,7 @@ type PreviewContentProps = { const PreviewContent = ({ option, question }: PreviewContentProps) => { if (!option?.answerAndDistractor) return null; - const answersAndDistractors = - option?.answerAndDistractor; + const answersAndDistractors = option?.answerAndDistractor; const { answers, distractors } = answersAndDistractors; const answerAndDistractorArray = [...answers, ...distractors]; @@ -39,7 +35,7 @@ const PreviewContent = ({ option, question }: PreviewContentProps) => { - {option.isOakQuestion ? "Created by a Human" : `Created by Oak AI`} + {option.isOakQuestion ? "Created by a Human" : "Created by Oak AI"} {!option.isOakQuestion && ( )} {isLoading && ( -
+
{children}
diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx index 23968628d..35a626543 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/ExportMenu.tsx @@ -13,7 +13,7 @@ import LoadingWheel from "@/components/LoadingWheel"; import useAnalytics from "@/lib/analytics/useAnalytics"; import ChatButton from "../Chat/ui/chat-button"; -import ShareButtonGroup from "../common/ShareButttonGroup"; +import ShareButtonGroup from "../common/ShareButtonGroup"; import DownloadGiftButton from "./DownloadGiftButton"; interface ExportMenuProps { diff --git a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx index 85dacbf45..49fa1d7f7 100644 --- a/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx +++ b/apps/nextjs/src/components/AppComponents/QuizDesigner/QuizQuestionRow/ControllerRow.tsx @@ -8,7 +8,7 @@ import { QuizAppActions } from "ai-apps/quiz-designer/state/actions"; import useAnalytics from "@/lib/analytics/useAnalytics"; import ChatButton from "../../Chat/ui/chat-button"; -import ShareButtonGroup from "../../common/ShareButttonGroup"; +import ShareButtonGroup from "../../common/ShareButtonGroup"; type ControllerProps = { hasQuestions: boolean; diff --git a/apps/nextjs/src/components/AppComponents/common/ShareButttonGroup.tsx b/apps/nextjs/src/components/AppComponents/common/ShareButtonGroup.tsx similarity index 100% rename from apps/nextjs/src/components/AppComponents/common/ShareButttonGroup.tsx rename to apps/nextjs/src/components/AppComponents/common/ShareButtonGroup.tsx diff --git a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx index b524bfcb8..23e2b08be 100644 --- a/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx +++ b/apps/nextjs/src/components/AppComponents/common/SingleGeneration/GenerationInputAndText.tsx @@ -59,7 +59,7 @@ function GenerationInputAndText({ className={ userIsEditing ? "hidden" - : `flex text-lg ${isLoading && `line-through opacity-60 `}` + : `flex text-lg ${isLoading && "line-through opacity-60 "}` } > {children} diff --git a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx index 7b3f0081b..4e75ffbe2 100644 --- a/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx +++ b/apps/nextjs/src/components/AppComponents/download/DownloadAllButton.tsx @@ -21,7 +21,7 @@ import SlidesIcon from "../../SVGParts/SlidesIcon"; const log = aiLogger("chat"); -const allexportLinksObject = z.object({ +const allExportLinksObject = z.object({ lessonSlides: z.string(), lessonPlan: z.string(), worksheet: z.string(), @@ -114,7 +114,7 @@ export const DownloadAllButton = ({ try { const lessonTitle = lesson.title; if (!lessonTitle) return; - const parsedData = allexportLinksObject.parse(data); + const parsedData = allExportLinksObject.parse(data); mutateAsync({ lessonTitle, slidesLink: parsedData.lessonSlides, @@ -188,8 +188,8 @@ export const DownloadAllButton = ({
Email me - {isSuccess && `- email sent`}{" "} - {isError && `- There was an error sending the email!`} + {isSuccess && "- email sent"}{" "} + {isError && "- There was an error sending the email!"} Google account needed for this option @@ -202,7 +202,7 @@ export const DownloadAllButton = ({ return ( <>