From 986763654d0a4978d9b047f9aa9927f14b3d1eeb Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 13:19:44 +0000 Subject: [PATCH 1/5] Aila package imports --- .vscode/settings.json | 12 +++- .../src/app/api/chat/errorHandling.test.ts | 4 +- apps/nextjs/src/app/api/chat/errorHandling.ts | 2 +- apps/nextjs/src/app/api/chat/user.ts | 4 +- .../src/app/api/chat/webActionsPlugin.test.ts | 4 +- .../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 | 17 +++--- .../aila/src/core/chat/AilaStreamHandler.ts | 8 ++- .../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 +++--- .../moderation/moderationErrorHandling.ts | 2 +- packages/core/src/inngest/index.ts | 53 ++++++++++++++++++ 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 | 2 +- packages/core/src/models/prompts.ts | 6 +- packages/core/src/models/safetyViolations.ts | 9 +-- packages/core/src/models/snippets.ts | 2 +- packages/core/src/models/statistics.ts | 8 +-- packages/core/src/models/transcript.ts | 6 +- packages/core/src/models/userBannedError.ts | 5 ++ 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 +---------------- 57 files changed, 360 insertions(+), 296 deletions(-) create mode 100644 packages/core/src/inngest/index.ts create mode 100644 packages/core/src/llm/langchain.ts create mode 100644 packages/core/src/models/userBannedError.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 23f2c7ab4..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", @@ -194,5 +203,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 } diff --git a/apps/nextjs/src/app/api/chat/errorHandling.test.ts b/apps/nextjs/src/app/api/chat/errorHandling.test.ts index 573c8550c..30cbbf627 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.test.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.test.ts @@ -1,6 +1,6 @@ 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 { UserBannedError } from "@oakai/core/src/models/userBannedError"; import { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { PrismaClientWithAccelerate } from "@oakai/db"; @@ -123,4 +123,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 be22e99d6..796616243 100644 --- a/apps/nextjs/src/app/api/chat/errorHandling.ts +++ b/apps/nextjs/src/app/api/chat/errorHandling.ts @@ -4,7 +4,7 @@ import { 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 { TracingSpan } from "@oakai/core/src/tracing/serverTracing"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { PrismaClientWithAccelerate } from "@oakai/db"; diff --git a/apps/nextjs/src/app/api/chat/user.ts b/apps/nextjs/src/app/api/chat/user.ts index dfa8bd629..fa0beb344 100644 --- a/apps/nextjs/src/app/api/chat/user.ts +++ b/apps/nextjs/src/app/api/chat/user.ts @@ -1,8 +1,8 @@ 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/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 289b5a6a3..5be434f8e 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -1,7 +1,7 @@ -import { AilaThreatDetectionError } from "@oakai/aila"; import { AilaPluginContext } from "@oakai/aila/src/core/plugins"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection"; import { inngest } from "@oakai/core"; -import { UserBannedError } from "@oakai/core/src/models/safetyViolations"; +import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { Moderation } from "@prisma/client"; diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 2d24a3aa2..8aef6edd2 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -1,11 +1,11 @@ -import { AilaThreatDetectionError } from "@oakai/aila"; import { AilaPlugin } from "@oakai/aila/src/core/plugins"; +import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection"; 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 { UserBannedError } from "@oakai/core/src/models/userBannedError"; import { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; import { waitUntil } from "@vercel/functions"; 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..20dbd6de8 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"; @@ -279,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() { diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 3245d1098..82de47fc1 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -44,7 +44,7 @@ export class AilaStreamHandler { await this.readFromStream(); } } catch (e) { - this.handleStreamError(e); + await this.handleStreamError(e); log.info("Stream error", e, this._chat.iteration, this._chat.id); } finally { this._isStreaming = false; @@ -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); @@ -107,7 +109,7 @@ export class AilaStreamHandler { for (const plugin of this._chat.aila.plugins ?? []) { await plugin.onStreamError?.(error, { aila: this._chat.aila, - enqueue: this._chat.enqueue, + enqueue: (patch) => this._chat.enqueue(patch), }); } 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/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/core/src/inngest/index.ts b/packages/core/src/inngest/index.ts new file mode 100644 index 000000000..58b9654da --- /dev/null +++ b/packages/core/src/inngest/index.ts @@ -0,0 +1,53 @@ +/* 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"; + +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 as string | undefined; +const BRANCH = serverRuntimeConfig?.BRANCH as string | undefined; + +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/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 ef907ceeb..0bb65161c 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 945f174e7..2ade1a0e5 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -17,7 +17,7 @@ import { RunnableSequence } from "langchain/schema/runnable"; import { z } from "zod"; import { inngest } from "../client"; -import { createOpenAILangchainClient } from "../llm/openai"; +import { createOpenAILangchainClient } from "../llm/langchain"; import type { SnippetWithLesson} from "./snippets"; import { Snippets } from "./snippets"; import type { Caption} from "./types/caption"; 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/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..24892a59e 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -25,7 +25,7 @@ import { PrismaVectorStore } from "langchain/vectorstores/prisma"; import { difference } from "remeda"; import { inngest } from "../client"; -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/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/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"; From fd57d084ab6afb46e2a0a47916166b0af7ddaddf Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 13:24:36 +0000 Subject: [PATCH 2/5] Lockfile --- pnpm-lock.yaml | 75 ++++---------------------------------------------- 1 file changed, 5 insertions(+), 70 deletions(-) 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 5782d7251c361ee2729a4fce535743ea422b3ce0 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 14:32:02 +0000 Subject: [PATCH 3/5] inngest tests --- apps/nextjs/src/app/api/chat/user.test.ts | 6 ++---- apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts | 2 +- apps/nextjs/src/app/api/chat/webActionsPlugin.ts | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/nextjs/src/app/api/chat/user.test.ts b/apps/nextjs/src/app/api/chat/user.test.ts index 79e139e42..08486f58e 100644 --- a/apps/nextjs/src/app/api/chat/user.test.ts +++ b/apps/nextjs/src/app/api/chat/user.test.ts @@ -1,9 +1,10 @@ import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; +import { inngest } from "@oakai/core/src/client"; import { RateLimitExceededError } from "@oakai/core/src/utils/rateLimiting/userBasedRateLimiter"; import { reportRateLimitError } from "./user"; -jest.mock("@oakai/core/src/inngest", () => ({ +jest.mock("@oakai/core/src/client", () => ({ inngest: { createFunction: jest.fn(), send: jest.fn(), @@ -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/webActionsPlugin.test.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts index 7c82e9b47..c5c5b6d5d 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -6,7 +6,7 @@ import type { Moderation } from "@prisma/client"; import { createWebActionsPlugin } from "./webActionsPlugin"; -jest.mock("@oakai/core/src/inngest", () => ({ +jest.mock("@oakai/core/src/client", () => ({ __esModule: true, inngest: { diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 2188e8195..0eba25566 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -1,10 +1,8 @@ import type { AilaPlugin } from "@oakai/aila/src/core/plugins"; import { AilaThreatDetectionError } from "@oakai/aila/src/features/threatDetection"; import { handleHeliconeError } from "@oakai/aila/src/utils/moderation/moderationErrorHandling"; -import { - SafetyViolations as defaultSafetyViolations, - inngest, -} from "@oakai/core"; +import { SafetyViolations as defaultSafetyViolations } from "@oakai/core"; +import { inngest } from "@oakai/core/src/client"; import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; From e37190a6ea46170f9ccae0d1d9ee36850f47c973 Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 14:36:58 +0000 Subject: [PATCH 4/5] Merge main and update to use inngest instead of client.ts --- apps/nextjs/src/app/api/chat/user.test.ts | 4 +- .../src/app/api/chat/webActionsPlugin.test.ts | 3 +- .../src/app/api/chat/webActionsPlugin.ts | 2 +- packages/api/src/middleware/rateLimiter.ts | 9 ++-- packages/core/src/client.ts | 49 ------------------- packages/core/src/models/lessons.ts | 2 +- packages/core/src/models/snippets.ts | 2 +- 7 files changed, 10 insertions(+), 61 deletions(-) delete mode 100644 packages/core/src/client.ts diff --git a/apps/nextjs/src/app/api/chat/user.test.ts b/apps/nextjs/src/app/api/chat/user.test.ts index 08486f58e..9e693e036 100644 --- a/apps/nextjs/src/app/api/chat/user.test.ts +++ b/apps/nextjs/src/app/api/chat/user.test.ts @@ -1,10 +1,10 @@ import { posthogAiBetaServerClient } from "@oakai/core/src/analytics/posthogAiBetaServerClient"; -import { inngest } from "@oakai/core/src/client"; +import { inngest } from "@oakai/core/src/inngest"; 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(), diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts index c5c5b6d5d..4519e0702 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.test.ts @@ -6,10 +6,11 @@ import type { Moderation } from "@prisma/client"; import { createWebActionsPlugin } from "./webActionsPlugin"; -jest.mock("@oakai/core/src/client", () => ({ +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 0eba25566..46daa425b 100644 --- a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts +++ b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts @@ -2,7 +2,7 @@ import type { AilaPlugin } from "@oakai/aila/src/core/plugins"; 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/client"; +import { inngest } from "@oakai/core/src/inngest"; import { UserBannedError } from "@oakai/core/src/models/userBannedError"; import type { PrismaClientWithAccelerate } from "@oakai/db"; import { aiLogger } from "@oakai/logger"; 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/models/lessons.ts b/packages/core/src/models/lessons.ts index baef62b08..358d3fdc9 100644 --- a/packages/core/src/models/lessons.ts +++ b/packages/core/src/models/lessons.ts @@ -15,7 +15,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/langchain"; import type { SnippetWithLesson } from "./snippets"; import { Snippets } from "./snippets"; diff --git a/packages/core/src/models/snippets.ts b/packages/core/src/models/snippets.ts index 50b37bf6a..0b7781333 100644 --- a/packages/core/src/models/snippets.ts +++ b/packages/core/src/models/snippets.ts @@ -18,7 +18,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/langchain"; import { embedWithCache } from "../utils/embeddings"; From 12add47ca8ee07b6dde7ffa9e68bece76f60ae1d Mon Sep 17 00:00:00 2001 From: Stef Lewandowski Date: Tue, 29 Oct 2024 14:42:32 +0000 Subject: [PATCH 5/5] Revert await fixes for unawaited promises --- apps/nextjs/src/app/api/chat/webActionsPlugin.ts | 6 +++--- packages/aila/src/core/chat/AilaChat.ts | 2 +- packages/aila/src/core/chat/AilaStreamHandler.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/nextjs/src/app/api/chat/webActionsPlugin.ts b/apps/nextjs/src/app/api/chat/webActionsPlugin.ts index 46daa425b..a8e77900c 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, ); - await enqueue(heliconeErrorMessage); + enqueue(heliconeErrorMessage); } if (error instanceof Error) { - await enqueue({ + 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"); - await enqueue({ + enqueue({ type: "action", action: "SHOW_ACCOUNT_LOCKED", }); diff --git a/packages/aila/src/core/chat/AilaChat.ts b/packages/aila/src/core/chat/AilaChat.ts index 20dbd6de8..6e86b29f6 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 }); } - await this._generation.persist(status); + this._generation.persist(status); } private async persistChat() { diff --git a/packages/aila/src/core/chat/AilaStreamHandler.ts b/packages/aila/src/core/chat/AilaStreamHandler.ts index 82de47fc1..caa586fe9 100644 --- a/packages/aila/src/core/chat/AilaStreamHandler.ts +++ b/packages/aila/src/core/chat/AilaStreamHandler.ts @@ -44,7 +44,7 @@ export class AilaStreamHandler { await this.readFromStream(); } } catch (e) { - await this.handleStreamError(e); + this.handleStreamError(e); log.info("Stream error", e, this._chat.iteration, this._chat.id); } finally { this._isStreaming = false; @@ -109,7 +109,7 @@ export class AilaStreamHandler { for (const plugin of this._chat.aila.plugins ?? []) { await plugin.onStreamError?.(error, { aila: this._chat.aila, - enqueue: (patch) => this._chat.enqueue(patch), + enqueue: this._chat.enqueue, }); }