From a044779aea7453e1adeb2283178ad63d708c780d Mon Sep 17 00:00:00 2001 From: Stephanie Cao Date: Tue, 19 Nov 2024 09:54:47 -0500 Subject: [PATCH] chore: deprecate lightspeed plugins (#2545) * deprecate lightspeed plugins in backstage-plugins Signed-off-by: Stephanie * clean up yarn dependency Signed-off-by: Stephanie --------- Signed-off-by: Stephanie --- plugins/lightspeed-backend/.eslintignore | 2 - plugins/lightspeed-backend/.eslintrc.js | 1 - plugins/lightspeed-backend/.lintstagedrc.json | 4 - plugins/lightspeed-backend/.prettierignore | 12 - plugins/lightspeed-backend/.prettierrc.js | 20 - plugins/lightspeed-backend/README.md | 47 +- .../__fixtures__/chatResponse.json | 70 - .../__fixtures__/handlers.ts | 45 - .../app-config.janus-idp.yaml | 0 plugins/lightspeed-backend/config.d.ts | 25 - plugins/lightspeed-backend/dev/index.ts | 7 - plugins/lightspeed-backend/package.json | 81 - .../src/handlers/chatHistory.test.ts | 179 -- .../src/handlers/chatHistory.ts | 124 -- .../src/handlers/conversationId.test.ts | 53 - .../src/handlers/conversationId.ts | 44 - plugins/lightspeed-backend/src/index.ts | 8 - plugins/lightspeed-backend/src/plugin.ts | 40 - .../src/service/router.test.ts | 645 ------- .../lightspeed-backend/src/service/router.ts | 363 ---- .../lightspeed-backend/src/service/types.ts | 47 - .../src/service/validation.ts | 61 - plugins/lightspeed-backend/src/setupTests.ts | 1 - plugins/lightspeed-backend/tsconfig.json | 9 - plugins/lightspeed-backend/turbo.json | 8 - plugins/lightspeed/.eslintignore | 2 - plugins/lightspeed/.eslintrc.js | 1 - plugins/lightspeed/.lintstagedrc.json | 4 - plugins/lightspeed/.prettierignore | 12 - plugins/lightspeed/.prettierrc.js | 20 - plugins/lightspeed/CHANGELOG.md | 23 - plugins/lightspeed/README.md | 153 +- plugins/lightspeed/app-config.janus-idp.yaml | 14 - plugins/lightspeed/config.d.ts | 26 - plugins/lightspeed/dev/index.tsx | 14 - plugins/lightspeed/package.json | 98 - .../lightspeed/src/api/LightspeedApiClient.ts | 137 -- plugins/lightspeed/src/api/api.ts | 24 - .../src/components/LightSpeedChat.tsx | 298 --- .../src/components/LightspeedChatBox.tsx | 86 - .../components/LightspeedChatBoxHeader.tsx | 61 - .../src/components/LightspeedIcon.tsx | 9 - .../src/components/LightspeedPage.tsx | 84 - plugins/lightspeed/src/globals.d.ts | 4 - .../useBackstageUserIdentity.test.ts | 38 - .../useConversationMessages.test.tsx | 429 ----- .../__tests__/useDeleteConversation.test.tsx | 127 -- plugins/lightspeed/src/hooks/useAllModels.ts | 18 - .../src/hooks/useBackstageUserIdentity.ts | 12 - .../src/hooks/useConversationMessages.ts | 264 --- .../lightspeed/src/hooks/useConversations.ts | 18 - .../src/hooks/useCreateConversation.ts | 19 - .../src/hooks/useCreateCoversationMessage.ts | 35 - .../src/hooks/useDeleteConversation.ts | 44 - plugins/lightspeed/src/images/logo.svg | 1 - plugins/lightspeed/src/index.ts | 2 - plugins/lightspeed/src/plugin.test.ts | 7 - plugins/lightspeed/src/plugin.ts | 41 - plugins/lightspeed/src/routes.ts | 5 - plugins/lightspeed/src/setupTests.ts | 13 - plugins/lightspeed/src/types.ts | 34 - .../lightspeed-chatbot-utils.test.ts | 284 --- .../src/utils/lightspeed-chatbox-utils.ts | 182 -- plugins/lightspeed/src/utils/queryClient.ts | 4 - plugins/lightspeed/tsconfig.json | 9 - plugins/lightspeed/turbo.json | 8 - yarn.lock | 1714 +---------------- 67 files changed, 39 insertions(+), 6235 deletions(-) delete mode 100644 plugins/lightspeed-backend/.eslintignore delete mode 100644 plugins/lightspeed-backend/.eslintrc.js delete mode 100644 plugins/lightspeed-backend/.lintstagedrc.json delete mode 100644 plugins/lightspeed-backend/.prettierignore delete mode 100644 plugins/lightspeed-backend/.prettierrc.js delete mode 100644 plugins/lightspeed-backend/__fixtures__/chatResponse.json delete mode 100644 plugins/lightspeed-backend/__fixtures__/handlers.ts delete mode 100644 plugins/lightspeed-backend/app-config.janus-idp.yaml delete mode 100644 plugins/lightspeed-backend/config.d.ts delete mode 100644 plugins/lightspeed-backend/dev/index.ts delete mode 100644 plugins/lightspeed-backend/package.json delete mode 100644 plugins/lightspeed-backend/src/handlers/chatHistory.test.ts delete mode 100644 plugins/lightspeed-backend/src/handlers/chatHistory.ts delete mode 100644 plugins/lightspeed-backend/src/handlers/conversationId.test.ts delete mode 100644 plugins/lightspeed-backend/src/handlers/conversationId.ts delete mode 100644 plugins/lightspeed-backend/src/index.ts delete mode 100644 plugins/lightspeed-backend/src/plugin.ts delete mode 100644 plugins/lightspeed-backend/src/service/router.test.ts delete mode 100644 plugins/lightspeed-backend/src/service/router.ts delete mode 100644 plugins/lightspeed-backend/src/service/types.ts delete mode 100644 plugins/lightspeed-backend/src/service/validation.ts delete mode 100644 plugins/lightspeed-backend/src/setupTests.ts delete mode 100644 plugins/lightspeed-backend/tsconfig.json delete mode 100644 plugins/lightspeed-backend/turbo.json delete mode 100644 plugins/lightspeed/.eslintignore delete mode 100644 plugins/lightspeed/.eslintrc.js delete mode 100644 plugins/lightspeed/.lintstagedrc.json delete mode 100644 plugins/lightspeed/.prettierignore delete mode 100644 plugins/lightspeed/.prettierrc.js delete mode 100644 plugins/lightspeed/CHANGELOG.md delete mode 100644 plugins/lightspeed/app-config.janus-idp.yaml delete mode 100644 plugins/lightspeed/config.d.ts delete mode 100644 plugins/lightspeed/dev/index.tsx delete mode 100644 plugins/lightspeed/package.json delete mode 100644 plugins/lightspeed/src/api/LightspeedApiClient.ts delete mode 100644 plugins/lightspeed/src/api/api.ts delete mode 100644 plugins/lightspeed/src/components/LightSpeedChat.tsx delete mode 100644 plugins/lightspeed/src/components/LightspeedChatBox.tsx delete mode 100644 plugins/lightspeed/src/components/LightspeedChatBoxHeader.tsx delete mode 100644 plugins/lightspeed/src/components/LightspeedIcon.tsx delete mode 100644 plugins/lightspeed/src/components/LightspeedPage.tsx delete mode 100644 plugins/lightspeed/src/globals.d.ts delete mode 100644 plugins/lightspeed/src/hooks/__tests__/useBackstageUserIdentity.test.ts delete mode 100644 plugins/lightspeed/src/hooks/__tests__/useConversationMessages.test.tsx delete mode 100644 plugins/lightspeed/src/hooks/__tests__/useDeleteConversation.test.tsx delete mode 100644 plugins/lightspeed/src/hooks/useAllModels.ts delete mode 100644 plugins/lightspeed/src/hooks/useBackstageUserIdentity.ts delete mode 100644 plugins/lightspeed/src/hooks/useConversationMessages.ts delete mode 100644 plugins/lightspeed/src/hooks/useConversations.ts delete mode 100644 plugins/lightspeed/src/hooks/useCreateConversation.ts delete mode 100644 plugins/lightspeed/src/hooks/useCreateCoversationMessage.ts delete mode 100644 plugins/lightspeed/src/hooks/useDeleteConversation.ts delete mode 100644 plugins/lightspeed/src/images/logo.svg delete mode 100644 plugins/lightspeed/src/index.ts delete mode 100644 plugins/lightspeed/src/plugin.test.ts delete mode 100644 plugins/lightspeed/src/plugin.ts delete mode 100644 plugins/lightspeed/src/routes.ts delete mode 100644 plugins/lightspeed/src/setupTests.ts delete mode 100644 plugins/lightspeed/src/types.ts delete mode 100644 plugins/lightspeed/src/utils/__tests__/lightspeed-chatbot-utils.test.ts delete mode 100644 plugins/lightspeed/src/utils/lightspeed-chatbox-utils.ts delete mode 100644 plugins/lightspeed/src/utils/queryClient.ts delete mode 100644 plugins/lightspeed/tsconfig.json delete mode 100644 plugins/lightspeed/turbo.json diff --git a/plugins/lightspeed-backend/.eslintignore b/plugins/lightspeed-backend/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/lightspeed-backend/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/lightspeed-backend/.eslintrc.js b/plugins/lightspeed-backend/.eslintrc.js deleted file mode 100644 index e2a53a6ad2..0000000000 --- a/plugins/lightspeed-backend/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/lightspeed-backend/.lintstagedrc.json b/plugins/lightspeed-backend/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/lightspeed-backend/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/lightspeed-backend/.prettierignore b/plugins/lightspeed-backend/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/lightspeed-backend/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/lightspeed-backend/.prettierrc.js b/plugins/lightspeed-backend/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/lightspeed-backend/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/lightspeed-backend/README.md b/plugins/lightspeed-backend/README.md index d3ae6391e2..c0602efebc 100644 --- a/plugins/lightspeed-backend/README.md +++ b/plugins/lightspeed-backend/README.md @@ -1,46 +1,3 @@ -# Lightspeed Backend +# Deprecated -This is the lightspeed backend plugin that enables you to interact with any LLM server running a model with OpenAI's API compatibility. - -## Getting Started - -### Installing the plugin - -```bash -yarn add --cwd packages/backend @janus-idp/backstage-plugin-lightspeed-backend -``` - -### Configuring the Backend - -Add the following to your `packages/backend/src/index.ts` file: - -```ts title="packages/backend/src/index.ts" -const backend = createBackend(); - -// Add the following line -backend.add(import('@janus-idp/backstage-plugin-lightspeed-backend')); - -backend.start(); -``` - -### Plugin Configurations - -Add the following lightspeed configurations into your `app-config.yaml` file: - -```yaml -lightspeed: - servers: - - id: - url: - token: # dummy token -``` - -Example local development configuration: - -```yaml -lightspeed: - servers: - - id: 'my-llm-server' - url: 'https://localhost:443/v1' - token: 'js92n-ssj28dbdk902' # dummy token -``` +This package has been moved to the [red-hat-developer/rhdh-plugins](https://github.com/redhat-developer/rhdh-plugins) repository. Migrate to using `@red-hat-developer-hub/backstage-plugin-lightspeed-backend` instead. diff --git a/plugins/lightspeed-backend/__fixtures__/chatResponse.json b/plugins/lightspeed-backend/__fixtures__/chatResponse.json deleted file mode 100644 index f729c06b40..0000000000 --- a/plugins/lightspeed-backend/__fixtures__/chatResponse.json +++ /dev/null @@ -1,70 +0,0 @@ -[ - { - "id": "chatcmpl-303", - "object": "chat.completion.chunk", - "created": 1728388569, - "model": "test-model", - "system_fingerprint": "dummy_fingerprint", - "choices": [ - { - "index": 0, - "delta": { - "role": "assistant", - "content": "Mockup" - }, - "finish_reason": null - } - ] - }, - { - "id": "chatcmpl-303", - "object": "chat.completion.chunk", - "created": 1728388569, - "model": "test-model", - "system_fingerprint": "dummy_fingerprint", - "choices": [ - { - "index": 0, - "delta": { - "role": "assistant", - "content": "AI" - }, - "finish_reason": null - } - ] - }, - { - "id": "chatcmpl-303", - "object": "chat.completion.chunk", - "created": 1728388569, - "model": "test-model", - "system_fingerprint": "dummy_fingerprint", - "choices": [ - { - "index": 0, - "delta": { - "role": "assistant", - "content": "Message" - }, - "finish_reason": null - } - ] - }, - { - "id": "chatcmpl-303", - "object": "chat.completion.chunk", - "created": 1728388569, - "model": "test-model", - "system_fingerprint": "dummy_fingerprint", - "choices": [ - { - "index": 0, - "delta": { - "role": "assistant", - "content": "" - }, - "finish_reason": "stop" - } - ] - } -] diff --git a/plugins/lightspeed-backend/__fixtures__/handlers.ts b/plugins/lightspeed-backend/__fixtures__/handlers.ts deleted file mode 100644 index f57d86523a..0000000000 --- a/plugins/lightspeed-backend/__fixtures__/handlers.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { http, HttpResponse } from 'msw'; - -const localHostAndPort = 'localhost:443'; -export const LOCAL_AI_ADDR = `http://${localHostAndPort}/v1`; - -function loadTestFixture(filePathFromFixturesDir: string) { - return require(`${__dirname}/${filePathFromFixturesDir}`); -} - -export const handlers = [ - http.post(`${LOCAL_AI_ADDR}/chat/completions`, () => { - const textEncoder = new TextEncoder(); - const mockData = loadTestFixture('chatResponse.json'); - - const stream = new ReadableStream({ - start(controller) { - mockData.forEach((chunk: any) => { - controller.enqueue( - textEncoder.encode(`data: ${JSON.stringify(chunk)}\n\n`), - ); - }); - controller.close(); - }, - }); - - return new HttpResponse(stream, { - headers: { - 'Content-Type': 'text/plain', - }, - }); - }), - - http.get(`${LOCAL_AI_ADDR}/models`, () => { - const mockModelRes = { - object: 'list', - data: [ - { - id: 'ibm-granite-8b-code-instruct', - object: 'model', - }, - ], - }; - return HttpResponse.json(mockModelRes); - }), -]; diff --git a/plugins/lightspeed-backend/app-config.janus-idp.yaml b/plugins/lightspeed-backend/app-config.janus-idp.yaml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/plugins/lightspeed-backend/config.d.ts b/plugins/lightspeed-backend/config.d.ts deleted file mode 100644 index 10b47648fb..0000000000 --- a/plugins/lightspeed-backend/config.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -export interface Config { - /** - * Configuration required for using lightspeed - * @visibility frontend - */ - lightspeed: { - servers: Array<{ - /** - * The id of the server. - * @visibility frontend - */ - id: string; - /** - * The url of the server. - * @visibility frontend - */ - url: string; - /** - * The access token for authenticating server. - * @visibility secret - */ - token?: string; - }>; - }; -} diff --git a/plugins/lightspeed-backend/dev/index.ts b/plugins/lightspeed-backend/dev/index.ts deleted file mode 100644 index 417ed9c3dd..0000000000 --- a/plugins/lightspeed-backend/dev/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createBackend } from '@backstage/backend-defaults'; - -import lightspeedPlugin from '../src'; - -const backend = createBackend(); -backend.add(lightspeedPlugin); -backend.start(); diff --git a/plugins/lightspeed-backend/package.json b/plugins/lightspeed-backend/package.json deleted file mode 100644 index 132d91be2d..0000000000 --- a/plugins/lightspeed-backend/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "@janus-idp/backstage-plugin-lightspeed-backend", - "version": "0.1.0", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", - "private": true, - "publishConfig": { - "access": "public" - }, - "backstage": { - "role": "backend-plugin", - "pluginId": "lightspeed", - "pluginPackages": [ - "@janus-idp/backstage-plugin-lightspeed", - "@janus-idp/backstage-plugin-lightspeed-backend" - ], - "supported-versions": "1.32.5" - }, - "exports": { - ".": "./src/index.ts", - "./package.json": "./package.json" - }, - "typesVersions": { - "*": { - "package.json": [ - "package.json" - ] - } - }, - "scripts": { - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", - "postpack": "backstage-cli package postpack", - "prepack": "backstage-cli package prepack", - "start": "backstage-cli package start", - "test": "backstage-cli package test --passWithNoTests --coverage", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." - }, - "dependencies": { - "@backstage/backend-defaults": "^0.5.2", - "@backstage/backend-plugin-api": "^1.0.1", - "@langchain/core": "^0.2.30", - "@langchain/openai": "^0.2.8", - "express": "^4.18.2", - "http-proxy-middleware": "^3.0.2" - }, - "devDependencies": { - "@backstage/backend-test-utils": "1.0.2", - "@backstage/cli": "0.28.2", - "@backstage/config": "1.2.0", - "@types/express": "4.17.21", - "@types/supertest": "2.0.16", - "msw": "2.4.0", - "prettier": "3.3.3", - "supertest": "6.3.4" - }, - "files": [ - "dist", - "config.d.ts", - "dist-dynamic/*.*", - "dist-dynamic/dist/**", - "app-config.janus-idp.yaml" - ], - "configSchema": "config.d.ts", - "repository": { - "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", - "directory": "plugins/lightspeed-backend" - }, - "keywords": [ - "backstage", - "plugin" - ], - "homepage": "https://janus-idp.io/", - "bugs": "https://github.com/janus-idp/backstage-plugins/issues" -} diff --git a/plugins/lightspeed-backend/src/handlers/chatHistory.test.ts b/plugins/lightspeed-backend/src/handlers/chatHistory.test.ts deleted file mode 100644 index 8b8e0e57b9..0000000000 --- a/plugins/lightspeed-backend/src/handlers/chatHistory.test.ts +++ /dev/null @@ -1,179 +0,0 @@ -import { AIMessage, HumanMessage } from '@langchain/core/messages'; - -import { Roles } from '../service/types'; -import { - deleteHistory, - loadAllConversations, - loadHistory, - saveHistory, - saveSummary, -} from './chatHistory'; - -const mockUser = 'user1'; -const mockConversationId = `${mockUser}+1q2w3e4r-qwer1234`; - -const mockConversationId2 = `${mockUser}+9i8u7y6t-654rew3`; - -describe('Test History Functions', () => { - afterEach(async () => { - // Clear the history store before each test - await deleteHistory(mockConversationId); - }); - - test('saveHistory should save a human message', async () => { - const message = 'Hello, how are you?'; - const timestamp = Date.now(); - - await saveHistory(mockConversationId, Roles.HumanRole, message, timestamp); - - const history = (await loadHistory(mockConversationId, 10)).history; - expect(history.length).toBe(1); - expect(history[0]).toBeInstanceOf(HumanMessage); - expect(history[0].content).toBe(message); - expect(history[0].response_metadata.created_at).toBe(timestamp); - }); - - test('saveHistory should save an AI message', async () => { - const message = 'I am fine, thank you!'; - const model = 'mock-model'; - const timestamp = Date.now(); - - await saveHistory( - mockConversationId, - Roles.AIRole, - message, - timestamp, - model, - ); - - const history = (await loadHistory(mockConversationId, 10)).history; - - expect(history.length).toBe(1); - expect(history[0]).toBeInstanceOf(AIMessage); - expect(history[0].content).toBe(message); - expect(history[0].response_metadata.created_at).toBe(timestamp); - expect(history[0].response_metadata.model).toBe(model); - }); - - test('saveHistory and loadHistory with multiple messages', async () => { - await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); - await saveHistory( - mockConversationId, - Roles.AIRole, - 'Hi! How can I help you today?', - ); - - const history = (await loadHistory(mockConversationId, 10)).history; - expect(history.length).toBe(2); - expect(history[0]).toBeInstanceOf(HumanMessage); - expect(history[0].content).toBe('Hello'); - expect(history[1]).toBeInstanceOf(AIMessage); - expect(history[1].content).toBe('Hi! How can I help you today?'); - }); - - test('saveHistory and loadHistory with exact number of messages', async () => { - await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); - await saveHistory( - mockConversationId, - Roles.AIRole, - 'Hi! How can I help you today?', - ); - - const history = (await loadHistory(mockConversationId, 1)).history; - expect(history.length).toBe(1); - expect(history[0]).toBeInstanceOf(AIMessage); - expect(history[0].content).toBe('Hi! How can I help you today?'); - }); - - test('saveHistory should throw an error for unknown roles', async () => { - await expect( - saveHistory(mockConversationId, 'UnknownRole', 'Message'), - ).rejects.toThrow('Unknown role: UnknownRole'); - }); - - test('saveSummary should throw an error for unknown conversationId', async () => { - await expect( - saveSummary('UnknownConversationId', 'summary'), - ).rejects.toThrow('unknown conversation_id: UnknownConversationId'); - }); - - test('saveSummary should save a summary', async () => { - const message = 'Hello, how are you?'; - const mockSummary = 'mock summary'; - - await saveHistory(mockConversationId, Roles.HumanRole, message); - await saveSummary(mockConversationId, mockSummary); - - const conversationHistory = await loadHistory(mockConversationId, 10); - expect(conversationHistory.summary).toBe(mockSummary); - }); - - test('saveHistory should preserve previous summary', async () => { - const message = 'Hello, how are you?'; - const mockSummary = 'mock summary'; - - await saveHistory(mockConversationId, Roles.HumanRole, message); - await saveSummary(mockConversationId, mockSummary); - - let conversationHistory = await loadHistory(mockConversationId, 10); - expect(conversationHistory.history.length).toBe(1); - expect(conversationHistory.summary).toBe(mockSummary); - - await saveHistory( - mockConversationId, - Roles.AIRole, - 'Hi! How can I help you today?', - ); - conversationHistory = await loadHistory(mockConversationId, 10); - expect(conversationHistory.history.length).toBe(2); - expect(conversationHistory.summary).toBe(mockSummary); - }); - - test('loadAllConversations should return all conversation_id for given user_id', async () => { - await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); - await saveHistory( - mockConversationId, - Roles.AIRole, - 'Hi! How can I help you today?', - ); - - await saveHistory(mockConversationId2, Roles.HumanRole, 'Hello'); - await saveHistory( - mockConversationId2, - Roles.AIRole, - 'Hi! How can I help you today?', - ); - - const conversationList = await loadAllConversations(mockUser); - expect(conversationList.length).toBe(2); - expect( - conversationList.some(item => item.includes(mockConversationId)), - ).toBe(true); - expect( - conversationList.some(item => item.includes(mockConversationId2)), - ).toBe(true); - }); - - test('deleteHistory should delete specific conversation', async () => { - await saveHistory(mockConversationId, Roles.HumanRole, 'Hello'); - await saveHistory('conv2', Roles.AIRole, 'Hi! How can I help you today?'); - - const history1 = (await loadHistory(mockConversationId, 1)).history; - expect(history1.length).toBe(1); - - const history2 = (await loadHistory('conv2', 1)).history; - expect(history2.length).toBe(1); - - await deleteHistory('conv2'); - - await expect(loadHistory('conv2', 1)).rejects.toThrow( - 'unknown conversation_id: conv2', - ); - - expect(await loadHistory(mockConversationId, 1)).toBeDefined(); - }); - - test('deleteHistory should not return error with unknown id', async () => { - await expect(() => deleteHistory(mockConversationId)).not.toThrow(); - }); -}); diff --git a/plugins/lightspeed-backend/src/handlers/chatHistory.ts b/plugins/lightspeed-backend/src/handlers/chatHistory.ts deleted file mode 100644 index d33a2eb1ab..0000000000 --- a/plugins/lightspeed-backend/src/handlers/chatHistory.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { - AIMessage, - HumanMessage, - SystemMessage, - type BaseMessage, -} from '@langchain/core/messages'; -import { InMemoryStore } from '@langchain/core/stores'; - -import { Roles } from '../service/types'; - -export type ConversationHistory = { - history: BaseMessage[]; - summary?: string; -}; -const historyStore = new InMemoryStore(); - -export async function saveHistory( - conversation_id: string, - role: string, - message: string, - timestamp?: number, - model?: string, -): Promise { - let newMessage: BaseMessage; - switch (role) { - case Roles.AIRole: { - newMessage = new AIMessage({ - content: message, - response_metadata: { - created_at: timestamp || Date.now(), - model: model, - }, - }); - break; - } - case Roles.HumanRole: { - newMessage = new HumanMessage({ - content: message, - response_metadata: { - created_at: timestamp || Date.now(), - }, - }); - break; - } - case Roles.SystemRole: { - newMessage = new SystemMessage({ - content: message, - response_metadata: { - created_at: timestamp || Date.now(), - }, - }); - break; - } - default: - throw new Error(`Unknown role: ${role}`); - } - - const sessionHistory = await historyStore.mget([conversation_id]); - let newHistory: BaseMessage[] = []; - let summary = ''; - if (sessionHistory && sessionHistory[0]) { - newHistory = sessionHistory[0].history; - summary = sessionHistory[0].summary ? sessionHistory[0].summary : ''; - } - newHistory.push(newMessage); - const newConversationHistory: ConversationHistory = { - history: newHistory, - summary: summary, - }; - await historyStore.mset([[conversation_id, newConversationHistory]]); -} - -export async function saveSummary( - conversation_id: string, - summary: string, -): Promise { - const sessionHistory = await historyStore.mget([conversation_id]); - if (!sessionHistory[0]) { - throw new Error(`unknown conversation_id: ${conversation_id}`); - } - const newConversationHistory: ConversationHistory = { - history: sessionHistory[0].history, - summary: summary, - }; - await historyStore.mset([[conversation_id, newConversationHistory]]); -} - -// export async function loadHistory( -// conversation_id: string, -// historyLength: number, -// ): Promise { -// const sessionHistory = await historyStore.mget([conversation_id]); -// if (!sessionHistory[0]) { -// throw new Error(`unknown conversation_id: ${conversation_id}`); -// } -// return sessionHistory[0]?.history?.slice(-historyLength); -// } - -export async function deleteHistory(conversation_id: string): Promise { - return await historyStore.mdelete([conversation_id]); -} - -export async function loadAllConversations(user_id: string): Promise { - const conversationIDList = []; - for await (const key of historyStore.yieldKeys(user_id)) { - conversationIDList.push(key); - } - return conversationIDList; -} - -export async function loadHistory( - conversation_id: string, - historyLength: number, -): Promise { - const sessionHistory = await historyStore.mget([conversation_id]); - if (!sessionHistory[0]) { - throw new Error(`unknown conversation_id: ${conversation_id}`); - } - const responseConversationHistory: ConversationHistory = { - history: sessionHistory[0].history?.slice(-historyLength), - summary: sessionHistory[0].summary, - }; - return responseConversationHistory; -} diff --git a/plugins/lightspeed-backend/src/handlers/conversationId.test.ts b/plugins/lightspeed-backend/src/handlers/conversationId.test.ts deleted file mode 100644 index 867a8ba42c..0000000000 --- a/plugins/lightspeed-backend/src/handlers/conversationId.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { - generateConversationId, - getUserId, - INVALID_FORTMAT_ERROR, - validateUserRequest, -} from './conversationId'; - -const mockUser = `user1`; -const mockUser1ConversationId = 'user1+1q2w3e4rqwer1234'; -const mockUser2ConversationId = 'user2+1kluA927dg2on22w'; - -function isAlphanumeric(str: string) { - return /^[a-zA-Z0-9]+$/.test(str); -} - -describe('Test conversation_id Functions', () => { - test('generateConversationId should return random string', async () => { - const conversation_id1 = generateConversationId(mockUser); - const conversation_id2 = generateConversationId(mockUser); - expect(conversation_id1.length).toEqual(conversation_id2.length); - expect(conversation_id1.length).toBe(mockUser.length + 17); // user_id length + `+` + 16-character session_id - expect(conversation_id1).not.toBe(conversation_id2); - const [user_id1, session_id1] = conversation_id1.split('+'); - const [user_id2, session_id2] = conversation_id2.split('+'); - expect(isAlphanumeric(session_id1)).toBe(true); - expect(isAlphanumeric(session_id2)).toBe(true); - - expect(user_id1).toBe(user_id2); - }); - - test('getUserId should return currect user_Id', async () => { - const user_Id = getUserId(mockUser1ConversationId); - expect(user_Id).toEqual(mockUser); - }); - - test('getUserId should throw an error on invalid format', async () => { - expect(() => getUserId('user1-293819dncjdd2Sc1')).toThrow( - INVALID_FORTMAT_ERROR, - ); - }); - - test('validateUserRequest should not throw error on valid request', async () => { - expect(() => - validateUserRequest(mockUser1ConversationId, mockUser), - ).not.toThrow(); - }); - - test('validateUserRequest should return false on different user', async () => { - expect(() => - validateUserRequest(mockUser2ConversationId, mockUser), - ).toThrow(); - }); -}); diff --git a/plugins/lightspeed-backend/src/handlers/conversationId.ts b/plugins/lightspeed-backend/src/handlers/conversationId.ts deleted file mode 100644 index 009929a88f..0000000000 --- a/plugins/lightspeed-backend/src/handlers/conversationId.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { randomBytes } from 'crypto'; - -const SESSION_ID_LENGTH = 16; -export const INVALID_FORTMAT_ERROR = - 'Invalid format: Must be in + format'; - -function generateSessionId(length = SESSION_ID_LENGTH) { - const characters = - 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - const bytes = randomBytes(length); - let sessionId = ''; - - for (let i = 0; i < length; i++) { - const randomIndex = bytes[i] % characters.length; // Ensures the index is within the range - sessionId += characters[randomIndex]; - } - - return sessionId; -} - -export function generateConversationId(user_id: string) { - const session_id = generateSessionId(); - return `${user_id}+${session_id}`; -} - -export function getUserId(conversation_id: string) { - const [user_id, session_id] = conversation_id.split('+'); - - // Check if both userId and sessionId are present - if (user_id && session_id) { - return user_id; - } - throw new Error(INVALID_FORTMAT_ERROR); -} - -export function validateUserRequest(conversation_id: string, user_id: string) { - const requestUserId = getUserId(conversation_id); - if (requestUserId === user_id) { - return; - } - throw new Error( - `Invalid request: requested conversation_id: ${conversation_id} does not belong to authenticated user ${user_id}`, - ); -} diff --git a/plugins/lightspeed-backend/src/index.ts b/plugins/lightspeed-backend/src/index.ts deleted file mode 100644 index d68ea39a85..0000000000 --- a/plugins/lightspeed-backend/src/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * The lightspeed backend plugin. - * - * @packageDocumentation - */ - -export { lightspeedPlugin as default } from './plugin'; -export * from './service/router'; diff --git a/plugins/lightspeed-backend/src/plugin.ts b/plugins/lightspeed-backend/src/plugin.ts deleted file mode 100644 index b093ca27e1..0000000000 --- a/plugins/lightspeed-backend/src/plugin.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { - coreServices, - createBackendPlugin, -} from '@backstage/backend-plugin-api'; - -import { createRouter } from './service/router'; - -/** - * The lightspeed backend plugin. - */ -export const lightspeedPlugin = createBackendPlugin({ - pluginId: 'lightspeed', - register(env) { - env.registerInit({ - deps: { - logger: coreServices.logger, - config: coreServices.rootConfig, - http: coreServices.httpRouter, - httpAuth: coreServices.httpAuth, - userInfo: coreServices.userInfo, - }, - async init({ logger, config, http, httpAuth, userInfo }) { - http.use( - await createRouter({ - config: config, - logger: logger, - httpAuth: httpAuth, - userInfo: userInfo, - }), - ); - - // allow health endpoint to be unauthenticated accessible - http.addAuthPolicy({ - path: '/health', - allow: 'unauthenticated', - }); - }, - }); - }, -}); diff --git a/plugins/lightspeed-backend/src/service/router.test.ts b/plugins/lightspeed-backend/src/service/router.test.ts deleted file mode 100644 index fe5c598df2..0000000000 --- a/plugins/lightspeed-backend/src/service/router.test.ts +++ /dev/null @@ -1,645 +0,0 @@ -import { type BackendFeature } from '@backstage/backend-plugin-api'; -import { - mockCredentials, - mockServices, - startTestBackend, -} from '@backstage/backend-test-utils'; - -import { AIMessage, HumanMessage } from '@langchain/core/messages'; -import { ChatPromptTemplate } from '@langchain/core/prompts'; -import express from 'express'; -import { http, HttpResponse } from 'msw'; -import { setupServer } from 'msw/node'; -import request from 'supertest'; - -import { handlers, LOCAL_AI_ADDR } from '../../__fixtures__/handlers'; -import { deleteHistory, saveHistory } from '../handlers/chatHistory'; -import { lightspeedPlugin } from '../plugin'; -import { Roles } from '../service/types'; -import { ConversationSummary } from './types'; - -const mockUserId = `user: default/user1`; -const mockConversationId = `${mockUserId}+1q2w3e4r-qwer1234`; -const encodedConversationId = encodeURIComponent(mockConversationId); -const mockConversationId2 = `${mockUserId}+9i8u7y6t-654rew3`; - -const mockAnotherUserId = `user: default/anotheruser`; -const mockAnotherConversationId = `${mockAnotherUserId}+1q2w3e4r-qwer1234`; -const encodedAnotherConversationId = encodeURIComponent( - mockAnotherConversationId, -); - -const mockModel = 'test-model'; -const mockToken = 'dummy-token'; - -const BASE_CONFIG = { - lightspeed: { - servers: [ - { - id: 'test-server', - url: LOCAL_AI_ADDR, - token: mockToken, - }, - ], - }, -}; - -const mockServerResponse = { - data: [ - { - id: mockModel, - }, - ], -}; -// Mocking the actual request that the proxy would make -jest.mock('http-proxy-middleware', () => ({ - createProxyMiddleware: jest - .fn() - .mockImplementation(() => (req: express.Request, res: express.Response) => { - if (req.headers.authorization !== `Bearer ${mockToken}`) { - res.status(403).json({ error: 'unauthorized' }); // test if config.token has been added in authorization header - return; - } - if (req.path === '/models') { - res.status(200).json(mockServerResponse); - return; - } - res.status(404).json({ error: 'unknown path' }); - }), -})); - -jest.mock('@backstage/backend-plugin-api', () => ({ - ...jest.requireActual('@backstage/backend-plugin-api'), - UserInfoService: jest.fn().mockImplementation(() => ({ - getUserInfo: jest.fn().mockResolvedValue({ - BackstageUserInfo: { - userEntityRef: mockUserId, - }, - }), - })), -})); -const splitJsonObjects = (response: { text: string }): string[] => - response.text.split('}{').map((chunk, index, arr) => { - if (index === 0) { - return `${chunk}}`; - } else if (index === arr.length - 1) { - return `{${chunk}`; - } - return `{${chunk}}`; - }); - -describe('lightspeed router tests', () => { - const server = setupServer(...handlers); - - beforeAll(() => - server.listen({ - /* - * This is required so that msw doesn't throw - * warnings when the backend is requesting an endpoint - */ - onUnhandledRequest: (req, print) => { - if (req.url.includes('/api/lightspeed')) { - // bypass - return; - } - print.warning(); - }, - }), - ); - - afterAll(() => server.close()); - - afterEach(() => { - jest.clearAllMocks(); - server.resetHandlers(); - }); - - async function startBackendServer(config?: Record) { - const features: (BackendFeature | Promise<{ default: BackendFeature }>)[] = - [ - lightspeedPlugin, - mockServices.rootLogger.factory(), - mockServices.rootConfig.factory({ - data: { ...BASE_CONFIG, ...(config || {}) }, - }), - mockServices.httpAuth.factory({ - defaultCredentials: mockCredentials.user(mockUserId), - }), - mockServices.userInfo.factory(), - ]; - return (await startTestBackend({ features })).server; - } - - describe('GET /health', () => { - it('returns ok', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - '/api/lightspeed/health', - ); - - expect(response.status).toEqual(200); - expect(response.body).toEqual({ status: 'ok' }); - }); - }); - - describe('/v1/* proxy middleware', () => { - it('should proxy requests to /v1/models', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - '/api/lightspeed/v1/models', - ); - - expect(response.status).toBe(200); - expect(response.body).toEqual(mockServerResponse); - }); - - it('unknown path', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - '/api/lightspeed/v1/unknown', - ); - - expect(response.status).toBe(404); - expect(String(response.error)).toContain( - 'Error: cannot GET /api/lightspeed/v1/unknown (404)', - ); - }); - }); - - describe('POST /conversations', () => { - it('generate new conversation_id', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer).post( - `/api/lightspeed/conversations`, - ); - expect(response.statusCode).toEqual(200); - const conversation_id = response.body.conversation_id; - - expect(conversation_id.length).toBe(mockUserId.length + 17); // user_id length + `+` + 16-character session_id - const [user_id, session_id] = conversation_id.split('+'); - expect(user_id).toBe(mockUserId); - expect(/^[a-zA-Z0-9]+$/.test(session_id)).toBe(true); - }); - }); - - describe('GET and DELETE /conversations/:conversation_id', () => { - const humanMessage = 'Hello'; - const aiMessage = 'Hi! How can I help you today?'; - - beforeEach(async () => { - await saveHistory( - mockConversationId, - Roles.HumanRole, - humanMessage, - Date.now(), - ); - await saveHistory( - mockConversationId, - Roles.AIRole, - aiMessage, - Date.now(), - mockModel, - ); - }); - - it('load history', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - `/api/lightspeed/conversations/${encodedConversationId}`, - ); - expect(response.statusCode).toEqual(200); - // Parse response body - const responseData = response.body; - - // Check that responseData is an array - expect(Array.isArray(responseData)).toBe(true); - expect(responseData.length).toBe(2); - - expect(responseData[0].id).toContain('HumanMessage'); - expect(responseData[0].kwargs?.content).toBe(humanMessage); - expect( - responseData[0].kwargs?.response_metadata.created_at, - ).toBeDefined(); - - expect(responseData[1].id).toContain('AIMessage'); - expect(responseData[1].kwargs?.content).toBe(aiMessage); - expect( - responseData[1].kwargs?.response_metadata.created_at, - ).toBeDefined(); - expect(responseData[1].kwargs?.response_metadata.model).toBe(mockModel); - }); - - it('delete history', async () => { - // delete request - const backendServer = await startBackendServer(); - const deleteResponse = await request(backendServer).delete( - `/api/lightspeed/conversations/${encodedConversationId}`, - ); - expect(deleteResponse.statusCode).toEqual(200); - }); - - it('load history with deleted conversation_id', async () => { - await deleteHistory(mockConversationId); - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - `/api/lightspeed/conversations/${encodedConversationId}`, - ); - expect(response.statusCode).toEqual(500); - expect(response.body.error).toContain('unknown conversation_id'); - }); - - it('load history from another authenticated user should error out', async () => { - const backendServer = await startBackendServer(); - - const response = await request(backendServer).get( - `/api/lightspeed/conversations/${encodedAnotherConversationId}`, - ); - expect(response.statusCode).toEqual(500); - expect(response.body.error).toContain( - 'does not belong to authenticated user', - ); - }); - - it('delete history from another authenticated user should error out', async () => { - const backendServer = await startBackendServer(); - const deleteResponse = await request(backendServer).delete( - `/api/lightspeed/conversations/${encodedAnotherConversationId}`, - ); - expect(deleteResponse.statusCode).toEqual(500); - expect(deleteResponse.body.error).toContain( - 'does not belong to authenticated user', - ); - }); - }); - - describe('POST /v1/query', () => { - let chatOpenAISpy: any; - - const setupStreamSpy = (mockStream: jest.Mock) => { - // Spy on fromMessages and mock its return value - jest - .spyOn(ChatPromptTemplate, 'fromMessages') - .mockImplementationOnce((): any => ({ - pipe: jest.fn().mockReturnValueOnce({ - stream: mockStream, - }), - })); - }; - beforeEach(() => { - chatOpenAISpy = jest.spyOn(ChatPromptTemplate, 'fromMessages'); - }); - - afterEach(() => { - chatOpenAISpy.mockRestore(); - chatOpenAISpy.mockReset(); - jest.clearAllMocks(); - }); - - it('chat completions', async () => { - const backendServer = await startBackendServer(); - - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - query: 'Hello', - serverURL: LOCAL_AI_ADDR, - }); - - expect(response.statusCode).toEqual(200); - const expectedData = 'Mockup AI Message'; - let receivedData = ''; - const chunkList = splitJsonObjects(response); - - expect(chunkList.length).toEqual(4); - // Parse each chunk individually - chunkList.forEach(chunk => { - const parsedChunk = JSON.parse(chunk); - expect(parsedChunk.conversation_id).toEqual(mockConversationId); - if ( - parsedChunk.response?.kwargs?.response_metadata?.finish_reason !== - 'stop' - ) { - receivedData += parsedChunk.response?.kwargs?.content; - receivedData += ' '; - } - }); - receivedData = receivedData.trimEnd(); // remove space at the last chunk - expect(receivedData).toEqual(expectedData); - }); - - it('should not have any history for the initial conversation', async () => { - const mockStream = jest.fn().mockImplementation(async function* stream() { - yield { content: 'Chunk 1', response_metadata: {} }; - }); - - setupStreamSpy(mockStream); - - const backendServer = await startBackendServer(); - await deleteHistory(mockConversationId); // delete existing conversation history - - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - query: 'Hi', - serverURL: LOCAL_AI_ADDR, - }); - - expect(response.statusCode).toEqual(200); - expect(mockStream).toHaveBeenCalled(); - - const streamCalledWithMessages = mockStream.mock.calls[0][0].messages; - - expect(streamCalledWithMessages).toHaveLength(1); - expect(streamCalledWithMessages[0]).toBeInstanceOf(HumanMessage); - expect(streamCalledWithMessages[0]).toEqual( - expect.objectContaining({ content: 'Hi' }), - ); - }); - - it('should call the stream with conversation history', async () => { - const mockStream = jest.fn().mockImplementation(async function* stream() { - yield { content: 'Chunk 1', response_metadata: {} }; - }); - - setupStreamSpy(mockStream); - - const backendServer = await startBackendServer(); - await deleteHistory(mockConversationId); // delete existing conversation history - - const humanMessage = 'Hi'; - const aiMessage = 'Hi! How can I help you today?'; - await saveHistory(mockConversationId, Roles.HumanRole, humanMessage); - await saveHistory(mockConversationId, Roles.AIRole, aiMessage); - - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - query: 'What is Langchain?', - serverURL: LOCAL_AI_ADDR, - }); - - expect(response.statusCode).toEqual(200); - expect(mockStream).toHaveBeenCalled(); - - const streamCalledWithMessages = mockStream.mock.calls[0][0].messages; - - expect(streamCalledWithMessages).toHaveLength(3); - expect(streamCalledWithMessages[0]).toBeInstanceOf(HumanMessage); - expect(streamCalledWithMessages[0]).toEqual( - expect.objectContaining({ content: 'Hi' }), - ); - - expect(streamCalledWithMessages[1]).toBeInstanceOf(AIMessage); - expect(streamCalledWithMessages[1]).toEqual( - expect.objectContaining({ content: 'Hi! How can I help you today?' }), - ); - - expect(streamCalledWithMessages[2]).toBeInstanceOf(HumanMessage); - expect(streamCalledWithMessages[2]).toEqual( - expect.objectContaining({ content: 'What is Langchain?' }), - ); - }); - - it('should contain message timestamp in human and ai message, and ai model in ai message', async () => { - const delay = (ms = 100) => - new Promise(resolve => setTimeout(resolve, ms)); - const mockStream = jest.fn().mockImplementation(async function* stream() { - await delay(); - yield { content: 'Chunk 1', response_metadata: {} }; - await delay(); // delay of 100ms - yield { content: 'Chunk 2', response_metadata: {} }; - }); - setupStreamSpy(mockStream); // use mockStream - - const backendServer = await startBackendServer(); - await deleteHistory(mockConversationId); - - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - query: 'Hello', - serverURL: LOCAL_AI_ADDR, - }); - - const jsonStrings = splitJsonObjects(response); - const aiMessages = jsonStrings.map(str => { - try { - return JSON.parse(str); - } catch (error) { - console.error(`Failed to parse: ${str}`); - throw error; - } - }); - - expect(response.statusCode).toEqual(200); - expect(mockStream).toHaveBeenCalled(); - - const humanMessage = mockStream.mock.calls[0][0].messages[0]; - const humanMessageTimestamp = humanMessage.response_metadata.created_at; - expect(humanMessage).toBeInstanceOf(HumanMessage); - expect(humanMessage).toEqual( - expect.objectContaining({ content: 'Hello' }), - ); - - // check each ai message chunk timestamp to be greater than human message timestamp - aiMessages.forEach(chunk => { - expect(chunk.response.response_metadata.created_at).toBeGreaterThan( - humanMessageTimestamp, - ); - expect(chunk.response.response_metadata.model).toBe(mockModel); - }); - }); - - it('returns 400 if conversation_id is missing', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - }); - expect(response.statusCode).toEqual(400); - expect(response.body.error).toBe( - 'conversation_id is required and must be a non-empty string', - ); - expect(chatOpenAISpy).not.toHaveBeenCalled(); - }); - - it('returns 400 if serverURL is missing', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - }); - expect(response.statusCode).toEqual(400); - expect(response.body.error).toBe( - 'serverURL is required and must be a non-empty string', - ); - expect(chatOpenAISpy).not.toHaveBeenCalled(); - }); - - it('returns 400 if model is missing', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - conversation_id: mockConversationId, - serverURL: LOCAL_AI_ADDR, - }); - expect(response.statusCode).toEqual(400); - expect(response.body.error).toBe( - 'model is required and must be a non-empty string', - ); - expect(chatOpenAISpy).not.toHaveBeenCalled(); - }); - - it('returns 400 if query is missing', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockConversationId, - serverURL: LOCAL_AI_ADDR, - }); - expect(response.statusCode).toEqual(400); - expect(response.body.error).toBe( - 'query is required and must be a non-empty string', - ); - expect(chatOpenAISpy).not.toHaveBeenCalled(); - }); - - it('returns 500 if unexpected error', async () => { - const backendServer = await startBackendServer(); - const nonExistentModel = 'nonexistent-model'; - server.use( - http.post(`${LOCAL_AI_ADDR}/chat/completions`, () => { - return new HttpResponse( - JSON.stringify({ - error: { - message: `model "${nonExistentModel}" not found, try pulling it first`, - type: 'api_error', - }, - }), - { status: 404 }, - ); - }), - ); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: nonExistentModel, - conversation_id: mockConversationId, - serverURL: LOCAL_AI_ADDR, - query: 'Hello', - }); - expect(response.statusCode).toEqual(500); - }); - - it('returns 500 if query sent for a different user', async () => { - const backendServer = await startBackendServer(); - const response = await request(backendServer) - .post('/api/lightspeed/v1/query') - .send({ - model: mockModel, - conversation_id: mockAnotherConversationId, - query: 'Hello', - serverURL: LOCAL_AI_ADDR, - }); - expect(response.statusCode).toEqual(500); - expect(response.body.error).toContain( - 'does not belong to authenticated user', - ); - }); - }); - - describe('GET /conversations', () => { - const mockSummary = 'mock summary'; - const mockAIMessage = new AIMessage(mockSummary); - const mockInvokeReturnValue = jest.fn().mockResolvedValue(mockAIMessage); - let chatPromptSpy: jest.SpyInstance; - - beforeEach(() => { - // Setup fresh spy for each test - chatPromptSpy = jest - .spyOn(ChatPromptTemplate, 'fromMessages') - .mockImplementation( - () => - ({ - pipe: jest.fn().mockReturnValue({ - invoke: mockInvokeReturnValue, - }), - }) as any, - ); - - // Clear any existing history - jest.clearAllMocks(); - }); - - afterEach(() => { - chatPromptSpy.mockRestore(); - jest.clearAllMocks(); - }); - - const humanMessage = 'Hello'; - const aiMessage = 'Hi! How can I help you today?'; - it('load conversations list with summary', async () => { - await saveHistory(mockConversationId, Roles.HumanRole, humanMessage); - await saveHistory(mockConversationId, Roles.AIRole, aiMessage); - - // wait for 1ms for the second conversation to be saved to test for timestamp - await new Promise(resolve => setTimeout(resolve, 1)); - - await saveHistory(mockConversationId2, Roles.HumanRole, humanMessage); - await saveHistory(mockConversationId2, Roles.AIRole, aiMessage); - - await new Promise(resolve => setTimeout(resolve, 1)); - const mockConversationId3 = 'user: default/user1+9i8u7y6t-654red3'; - await saveHistory(mockConversationId3, Roles.HumanRole, humanMessage); - await saveHistory(mockConversationId3, Roles.AIRole, aiMessage); - - const backendServer = await startBackendServer(); - const response = await request(backendServer).get( - `/api/lightspeed/conversations`, - ); - expect(response.statusCode).toEqual(200); - // Parse response body - const responseData: ConversationSummary[] = response.body; - - // Check that responseData is an array - expect(Array.isArray(responseData)).toBe(true); - expect(responseData.length).toBe(3); - const ids = responseData.map(item => item.conversation_id); - - // Check if both expected IDs are present - expect(ids).toContain(mockConversationId); - expect(ids).toContain(mockConversationId2); - expect(ids).toContain(mockConversationId3); - - // check the summary - expect(responseData[0].summary).toBe(mockSummary); - expect(responseData[1].summary).toBe(mockSummary); - expect(responseData[2].summary).toBe(mockSummary); - - // check the timestamp is in descending order - expect(responseData[0].lastMessageTimestamp).toBeDefined(); - expect(responseData[1].lastMessageTimestamp).toBeDefined(); - expect(responseData[2].lastMessageTimestamp).toBeDefined(); - expect(responseData[0].lastMessageTimestamp).toBeGreaterThan( - responseData[1].lastMessageTimestamp, - ); - expect(responseData[1].lastMessageTimestamp).toBeGreaterThan( - responseData[2].lastMessageTimestamp, - ); - }); - }); -}); diff --git a/plugins/lightspeed-backend/src/service/router.ts b/plugins/lightspeed-backend/src/service/router.ts deleted file mode 100644 index 065f489cb3..0000000000 --- a/plugins/lightspeed-backend/src/service/router.ts +++ /dev/null @@ -1,363 +0,0 @@ -import { MiddlewareFactory } from '@backstage/backend-defaults/rootHttpRouter'; - -import { BaseMessage, HumanMessage } from '@langchain/core/messages'; -import { - ChatPromptTemplate, - MessagesPlaceholder, -} from '@langchain/core/prompts'; -import { ChatOpenAI } from '@langchain/openai'; -import express, { Router } from 'express'; -import { createProxyMiddleware } from 'http-proxy-middleware'; - -import { - deleteHistory, - loadAllConversations, - loadHistory, - saveHistory, - saveSummary, -} from '../handlers/chatHistory'; -import { - generateConversationId, - validateUserRequest, -} from '../handlers/conversationId'; -import { - ConversationSummary, - DEFAULT_HISTORY_LENGTH, - QueryRequestBody, - Roles, - RouterOptions, -} from './types'; -import { - validateCompletionsRequest, - validateLoadHistoryRequest, -} from './validation'; - -export async function createRouter( - options: RouterOptions, -): Promise { - const { logger, config, httpAuth, userInfo } = options; - - const router = Router(); - router.use(express.json()); - - router.get('/health', (_, response) => { - response.json({ status: 'ok' }); - }); - - // Middleware proxy to exclude /v1/query - router.use('/v1', async (req, res, next) => { - if (req.path === '/query') { - return next(); // This will skip proxying and go to /v1/query endpoint - } - - // TODO: parse server_id from req.body and get URL and token when multi-server is supported - const user = await userInfo.getUserInfo(await httpAuth.credentials(req)); - const userEntity = user.userEntityRef; - - logger.info(`/v1 receives call from user: ${userEntity}`); - - // Proxy middleware configuration - const apiProxy = createProxyMiddleware({ - target: config.getConfigArray('lightspeed.servers')[0].getString('url'), // currently only single llm server is supported - changeOrigin: true, - }); - // For all other /v1/* requests, use the proxy - const apiToken = config - .getConfigArray('lightspeed.servers')[0] - .getOptionalString('token'); // currently only single llm server is supported - req.headers.authorization = `Bearer ${apiToken}`; - return apiProxy(req, res, next); - }); - - router.post('/conversations', async (request, response) => { - try { - const userEntity = await userInfo.getUserInfo( - await httpAuth.credentials(request), - ); - const user_id = userEntity.userEntityRef; - - logger.info(`POST /conversations receives call from user: ${user_id}`); - - const conversation_id = generateConversationId(user_id); - - response.status(200).json({ conversation_id: conversation_id }); - response.end(); - } catch (error) { - const errormsg = `${error}`; - logger.error(errormsg); - response.status(500).json({ error: errormsg }); - } - }); - - router.get('/conversations', async (request, response) => { - try { - const userEntity = await userInfo.getUserInfo( - await httpAuth.credentials(request), - ); - const user_id = userEntity.userEntityRef; - logger.info(`GET /conversations receives call from user: ${user_id}`); - const conversationList = await loadAllConversations(user_id); - const conversationSummaryList: ConversationSummary[] = []; - - // currently only single llm server is supported - const serverURL = config - .getConfigArray('lightspeed.servers')[0] - .getString('url'); - const apiToken = config - .getConfigArray('lightspeed.servers')[0] - .getOptionalString('token'); - - // get model list and select first model to use for conversation summary - const url = new URL(`${serverURL}/models`); - const res = await fetch(url, { - method: 'GET', - headers: { - Authorization: `Bearer ${apiToken}`, - }, - }); - const data = await res.json(); - let model = ''; - if (data.data && data.data[0]) { - model = data.data[0].id; - logger.info(`using model ${model} for retriving conversation summary`); - } else { - throw Error(`no available model found in server ${serverURL}`); - } - - // get summary - const promises = conversationList.map(async conversation_id => { - const conversationHistory = await loadHistory( - conversation_id, - DEFAULT_HISTORY_LENGTH, - ); - const history = conversationHistory.history; - const LastMessage = history[history.length - 1]; - const timestamp = LastMessage.response_metadata.created_at; - let chatSummary = conversationHistory.summary; - if (!chatSummary) { - const openAIApi = new ChatOpenAI({ - apiKey: apiToken || 'sk-no-key-required', // set to sk-no-key-required if api token is not provided - model: model, - streaming: false, - temperature: 0, - configuration: { - baseOptions: { - headers: { - ...(apiToken && { Authorization: `Bearer ${apiToken}` }), - }, - }, - baseURL: serverURL, - }, - }); - - const summarizePrompt = ChatPromptTemplate.fromMessages([ - [ - 'system', - "Your task is to summarize of user's main purpose of a conversation in a few words without any introductory phrases. ", - ], - new MessagesPlaceholder('messages'), - ]); - - const newchain = summarizePrompt.pipe(openAIApi); - const summary = await newchain.invoke({ - messages: [ - ...history, - new HumanMessage({ - content: - 'summarize the above conversation to be displayed as a title in frontend for people to get a main subject of the conversation. do not form a sentence, only return the subject of the main purpose. ', - }), - ], - }); - chatSummary = String(summary.content); - saveSummary(conversation_id, chatSummary); - } - const conversationSummary: ConversationSummary = { - conversation_id: conversation_id, - summary: chatSummary, - lastMessageTimestamp: timestamp, - }; - conversationSummaryList.push(conversationSummary); - }); - await Promise.all(promises); - // Sorting the array - conversationSummaryList.sort( - (a, b) => b.lastMessageTimestamp - a.lastMessageTimestamp, - ); - response.status(200).json(conversationSummaryList); - response.end(); - } catch (error) { - const errormsg = `${error}`; - logger.error(errormsg); - console.log(errormsg); - response.status(500).json({ error: errormsg }); - } - }); - - router.get( - '/conversations/:conversation_id', - validateLoadHistoryRequest, - async (request, response) => { - const conversation_id = request.params.conversation_id; - const historyLength = Number(request.query.historyLength); - - const loadhistoryLength: number = historyLength || DEFAULT_HISTORY_LENGTH; - try { - const userEntity = await userInfo.getUserInfo( - await httpAuth.credentials(request), - ); - const user_id = userEntity.userEntityRef; - logger.info( - `GET /conversations/:conversation_id receives call from user: ${user_id}`, - ); - - validateUserRequest(conversation_id, user_id); // will throw error and return 500 with error message if user_id does not match - - const conversationHistory = await loadHistory( - conversation_id, - loadhistoryLength, - ); - response.status(200).json(conversationHistory.history); - response.end(); - } catch (error) { - const errormsg = `${error}`; - logger.error(errormsg); - response.status(500).json({ error: errormsg }); - } - }, - ); - - router.delete( - '/conversations/:conversation_id', - async (request, response) => { - const conversation_id = request.params.conversation_id; - try { - const userEntity = await userInfo.getUserInfo( - await httpAuth.credentials(request), - ); - const user_id = userEntity.userEntityRef; - - logger.info( - `DELETE /conversations/:conversation_id receives call from user: ${user_id}`, - ); - - validateUserRequest(conversation_id, user_id); // will throw error and return 500 with error message if user_id does not match - - response.status(200).json(await deleteHistory(conversation_id)); - response.end(); - } catch (error) { - const errormsg = `${error}`; - logger.error(errormsg); - response.status(500).json({ error: errormsg }); - } - }, - ); - - router.post( - '/v1/query', - validateCompletionsRequest, - async (request, response) => { - const { conversation_id, model, query, serverURL }: QueryRequestBody = - request.body; - try { - const userEntity = await userInfo.getUserInfo( - await httpAuth.credentials(request), - ); - const user_id = userEntity.userEntityRef; - - logger.info(`/v1/query receives call from user: ${user_id}`); - validateUserRequest(conversation_id, user_id); // will throw error and return 500 with error message if user_id does not match - - // currently only supports single server - const apiToken = config - .getConfigArray('lightspeed.servers')[0] - .getOptionalString('token'); - - const openAIApi = new ChatOpenAI({ - apiKey: apiToken || 'sk-no-key-required', // set to sk-no-key-required if api token is not provided - model: model, - streaming: true, - streamUsage: false, - temperature: 0, - configuration: { - baseOptions: { - headers: { - ...(apiToken && { Authorization: `Bearer ${apiToken}` }), - }, - }, - baseURL: serverURL, - }, - }); - - const prompt = ChatPromptTemplate.fromMessages([ - [ - 'system', - 'You are a helpful assistant that can answer question in Red Hat Developer Hub.', - ], - new MessagesPlaceholder('messages'), - ]); - - let conversationHistory: BaseMessage[] = []; - try { - conversationHistory = ( - await loadHistory(conversation_id, DEFAULT_HISTORY_LENGTH) - ).history; - } catch (error) { - logger.error(`${error}`); - } - - const chain = prompt.pipe(openAIApi); - let content = ''; - const userMessageTimestamp = Date.now(); - let botMessageTimestamp; - for await (const chunk of await chain.stream({ - messages: [ - ...conversationHistory, - new HumanMessage({ - content: query, - response_metadata: { - created_at: userMessageTimestamp, - }, - }), - ], - })) { - if (!botMessageTimestamp) { - botMessageTimestamp = Date.now(); - } - chunk.response_metadata.created_at = botMessageTimestamp; - chunk.response_metadata.model = model; - const data = { - conversation_id: conversation_id, - response: chunk, - }; - const buf = Buffer.from(JSON.stringify(data)); - content += String(chunk.content); - response.write(buf); - } - response.end(); - - await saveHistory( - conversation_id, - Roles.HumanRole, - query, - userMessageTimestamp, - ); - await saveHistory( - conversation_id, - Roles.AIRole, - content, - botMessageTimestamp, - model, - ); - } catch (error) { - const errormsg = `Error fetching completions from ${serverURL}: ${error}`; - logger.error(errormsg); - response.status(500).json({ error: errormsg }); - } - }, - ); - - const middleware = MiddlewareFactory.create({ logger, config }); - - router.use(middleware.error()); - return router; -} diff --git a/plugins/lightspeed-backend/src/service/types.ts b/plugins/lightspeed-backend/src/service/types.ts deleted file mode 100644 index d5a5138e21..0000000000 --- a/plugins/lightspeed-backend/src/service/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { - HttpAuthService, - LoggerService, - UserInfoService, -} from '@backstage/backend-plugin-api'; -import type { Config } from '@backstage/config'; - -export type RouterOptions = { - logger: LoggerService; - config: Config; - httpAuth: HttpAuthService; - userInfo: UserInfoService; -}; - -/** - * Define the type for the request body of the /v1/query endpoint. - */ -export interface QueryRequestBody { - // AI model identifier - model: string; - - // Query message - query: string; - - // LLM server URL, expected to be the proxy endpoint - // for example: http://localhost:7007/api/proxy/lightspeed/api - serverURL: string; - - // A combination of user_id & session_id in the format of + - conversation_id: string; -} - -// For create AIMessage, HumanMessage, SystemMessage respectively -export const Roles = { - AIRole: 'ai', - HumanRole: 'human', - SystemRole: 'system', -} as const; - -// default number of message history being loaded -export const DEFAULT_HISTORY_LENGTH = 10; - -export type ConversationSummary = { - conversation_id: string; - summary: string; - lastMessageTimestamp: number; -}; diff --git a/plugins/lightspeed-backend/src/service/validation.ts b/plugins/lightspeed-backend/src/service/validation.ts deleted file mode 100644 index 7b6acee9b8..0000000000 --- a/plugins/lightspeed-backend/src/service/validation.ts +++ /dev/null @@ -1,61 +0,0 @@ -import type { NextFunction, Request, Response } from 'express'; - -import { QueryRequestBody } from './types'; - -export const validateCompletionsRequest = ( - req: Request, - res: Response, - next: NextFunction, -) => { - const reqData: QueryRequestBody = req.body; - - if ( - typeof reqData.conversation_id !== 'string' || - reqData.conversation_id.trim() === '' - ) { - return res.status(400).json({ - error: 'conversation_id is required and must be a non-empty string', - }); - } - - // TODO: Need to extract out the user_id from conversation_id, and verify with the login user entity - - if ( - typeof reqData.serverURL !== 'string' || - reqData.serverURL.trim() === '' - ) { - return res - .status(400) - .json({ error: 'serverURL is required and must be a non-empty string' }); - } - - if (typeof reqData.model !== 'string' || reqData.model.trim() === '') { - return res - .status(400) - .json({ error: 'model is required and must be a non-empty string' }); - } - - if (typeof reqData.query !== 'string' || reqData.query.trim() === '') { - return res - .status(400) - .json({ error: 'query is required and must be a non-empty string' }); - } - - return next(); -}; - -export const validateLoadHistoryRequest = ( - req: Request, - res: Response, - next: NextFunction, -) => { - const historyLength = Number(req.query.historyLength); - - if (historyLength && !Number.isInteger(historyLength)) { - return res.status(400).send('historyLength has to be a valid integer'); - } - - // TODO: Need to extract out the user_id from conversation_id, and verify with the login user entity - - return next(); -}; diff --git a/plugins/lightspeed-backend/src/setupTests.ts b/plugins/lightspeed-backend/src/setupTests.ts deleted file mode 100644 index cb0ff5c3b5..0000000000 --- a/plugins/lightspeed-backend/src/setupTests.ts +++ /dev/null @@ -1 +0,0 @@ -export {}; diff --git a/plugins/lightspeed-backend/tsconfig.json b/plugins/lightspeed-backend/tsconfig.json deleted file mode 100644 index 5b53ff6ffe..0000000000 --- a/plugins/lightspeed-backend/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src", "dev", "migrations"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "../../dist-types/plugins/lightspeed-backend", - "rootDir": "." - } -} diff --git a/plugins/lightspeed-backend/turbo.json b/plugins/lightspeed-backend/turbo.json deleted file mode 100644 index bcc47a1851..0000000000 --- a/plugins/lightspeed-backend/turbo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "tsc": { - "outputs": ["../../dist-types/plugins/lightspeed/**"] - } - } -} diff --git a/plugins/lightspeed/.eslintignore b/plugins/lightspeed/.eslintignore deleted file mode 100644 index 55289f4a23..0000000000 --- a/plugins/lightspeed/.eslintignore +++ /dev/null @@ -1,2 +0,0 @@ -dist-dynamic -dist-scalprum diff --git a/plugins/lightspeed/.eslintrc.js b/plugins/lightspeed/.eslintrc.js deleted file mode 100644 index e2a53a6ad2..0000000000 --- a/plugins/lightspeed/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/lightspeed/.lintstagedrc.json b/plugins/lightspeed/.lintstagedrc.json deleted file mode 100644 index 14b2263def..0000000000 --- a/plugins/lightspeed/.lintstagedrc.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "*": "prettier --ignore-unknown --write", - "*.{js,jsx,ts,tsx,mjs,cjs}": "backstage-cli package lint --fix" -} diff --git a/plugins/lightspeed/.prettierignore b/plugins/lightspeed/.prettierignore deleted file mode 100644 index fc8357d99e..0000000000 --- a/plugins/lightspeed/.prettierignore +++ /dev/null @@ -1,12 +0,0 @@ -dist -dist-types -coverage -.vscode -CHANGELOG.md -generated -templates -*.hbs -renovate.json -dist-dynamic -dist-scalprum -playwright-report diff --git a/plugins/lightspeed/.prettierrc.js b/plugins/lightspeed/.prettierrc.js deleted file mode 100644 index 84cbac65b5..0000000000 --- a/plugins/lightspeed/.prettierrc.js +++ /dev/null @@ -1,20 +0,0 @@ -// @ts-check - -/** @type {import("@ianvs/prettier-plugin-sort-imports").PrettierConfig} */ -module.exports = { - ...require('@spotify/prettier-config'), - plugins: ['@ianvs/prettier-plugin-sort-imports'], - importOrder: [ - '^react(.*)$', - '', - '^@backstage/(.*)$', - '', - '', - '', - '^@janus-idp/(.*)$', - '', - '', - '', - '^[.]', - ], -}; diff --git a/plugins/lightspeed/CHANGELOG.md b/plugins/lightspeed/CHANGELOG.md deleted file mode 100644 index fce54828c7..0000000000 --- a/plugins/lightspeed/CHANGELOG.md +++ /dev/null @@ -1,23 +0,0 @@ -## @janus-idp/backstage-plugin-lightspeed 1.0.0 (2024-08-07) - - -### Features - -* **lightspeed:** add a new lightspeed plugin with basic implementation of chat ([#1889](https://github.com/janus-idp/backstage-plugins/issues/1889)) ([cb80e38](https://github.com/janus-idp/backstage-plugins/commit/cb80e38d4d35a8097cd84b57c1b8eb12ec5af6b4)) -* **lightspeed:** add api client ([#2020](https://github.com/janus-idp/backstage-plugins/issues/2020)) ([ff09574](https://github.com/janus-idp/backstage-plugins/commit/ff095742c542869c7a330d391bd619e97473218c)) - -## @janus-idp/backstage-plugin-lightspeed 1.0.0 (2024-08-07) - - -### Features - -* **lightspeed:** add a new lightspeed plugin with basic implementation of chat ([#1889](https://github.com/janus-idp/backstage-plugins/issues/1889)) ([cb80e38](https://github.com/janus-idp/backstage-plugins/commit/cb80e38d4d35a8097cd84b57c1b8eb12ec5af6b4)) -* **lightspeed:** add api client ([#2020](https://github.com/janus-idp/backstage-plugins/issues/2020)) ([ff09574](https://github.com/janus-idp/backstage-plugins/commit/ff095742c542869c7a330d391bd619e97473218c)) - -## @janus-idp/backstage-plugin-lightspeed 1.0.0 (2024-08-07) - - -### Features - -* **lightspeed:** add a new lightspeed plugin with basic implementation of chat ([#1889](https://github.com/janus-idp/backstage-plugins/issues/1889)) ([cb80e38](https://github.com/janus-idp/backstage-plugins/commit/cb80e38d4d35a8097cd84b57c1b8eb12ec5af6b4)) -* **lightspeed:** add api client ([#2020](https://github.com/janus-idp/backstage-plugins/issues/2020)) ([ff09574](https://github.com/janus-idp/backstage-plugins/commit/ff095742c542869c7a330d391bd619e97473218c)) diff --git a/plugins/lightspeed/README.md b/plugins/lightspeed/README.md index 6a857ae5b1..1cbece7064 100644 --- a/plugins/lightspeed/README.md +++ b/plugins/lightspeed/README.md @@ -1,152 +1,3 @@ -# Lightspeed plugin for Backstage +# Deprecated -The Lightspeed plugin enables you to interact with any LLM server running a model with OpenAI's API compatibility. - -## For administrators - -### Prerequisites - -- Follow the lightspeed backend plugin [README](https://github.com/janus-idp/backstage-plugins/tree/main/plugins/lightspeed-backend) to integrate lightspeed backend in your Backstage instance. - -### Installation - -1. Install the Lightspeed plugin using the following command: - - ```console - yarn workspace app add @janus-idp/backstage-plugin-lightspeed - ``` - -### Configuration - -1. Add a new nav item **Lightspeed** in App `packages/app/src/App.tsx`: - - ```tsx title="packages/app/src/components/App.tsx" - /* highlight-add-next-line */ import { LightspeedPage } from '@janus-idp/backstage-plugin-lightspeed'; - - } />; - ``` - -2. Enable **Lightspeed** page in `packages/app/src/components/Root/Root.tsx`: - - ```tsx title="packages/app/src/components/Root/Root.tsx" - /* highlight-add-next-line */ import { LightspeedIcon } from '@janus-idp/backstage-plugin-lightspeed'; - - ; - ``` - -## For users - -### Using the Lightspeed plugin in Backstage - -Lightspeed is a front-end plugin that enables you to interact with any LLM server running a model with OpenAI's API compatibility. - -#### Prerequisites - -- Your Backstage application is installed and running. -- You have installed the Lightspeed plugin. For installation process, see [Installation](#installation). - -#### Procedure - -1. Open your Backstage application and select a Lightspeed nav item from the **Navigation**. -2. Ask you questions to the Lightspeed chatbot. - -## Loading as Dynamic Plugin - -#### To install Lightspeed plugin into Red Hat Developer Hub or Janus IDP via Helm use this configuration: - -- Load the lightspeed plugin from the npm registry - -``` -global: - dynamic: - includes: - - dynamic-plugins.default.yaml - plugins: - - package: '@janus-idp/backstage-plugin-lightspeed@0.1.2' - integrity: >- - sha512-bCKETjVhjZFLx7ImSFcptA3yvwJhFLFTFhMo/LvdVc0K5E76/SpEEkYBPup4aEQMivZBJKn0iVQFBuduChCDpA== - disabled: false - pluginConfig: - dynamicPlugins: - frontend: - janus-idp.backstage-plugin-lightspeed: - appIcons: - - name: LightspeedIcon - module: LightspeedPlugin - importName: LightspeedIcon - dynamicRoutes: - - path: /lightspeed - importName: LightspeedPage - module: LightspeedPlugin - menuItem: - icon: LightspeedIcon - text: Lightspeed -``` - -- add the lightspeed configuration in the `app-config.yaml` - -``` -lightspeed: - servers: - - id: - url: - token: -``` - ---- - -#### To install this plugin locally in [backstage-showcase](https://github.com/janus-idp/backstage-showcase) application as a dynamic plugin. - -Follow the below steps - - -- Export dynamic plugin assets. This will build and create the static assets for the plugin and put it inside dist-scalprum folder. - -`yarn install` - -`yarn tsc` - -`yarn build` - -`yarn export-dynamic` - -- Package and copy dist-scalprum folder assets to dynamic-plugins-root folder in [backstage-showcase](https://github.com/janus-idp/backstage-showcase) application. - -To Package the plugin, run the below commands. - -``` -pkg=../plugins/lightspeed -archive=$(npm pack $pkg) -tar -xzf "$archive" && rm "$archive" -mv package $(echo $archive | sed -e 's:\.tgz$::') -``` - -- Add the extension point inside the `app-config.yaml` or `app-config.local.yaml` file. - -``` - -lightspeed: - servers: - - id: - url: - token: # dummy token - -dynamicPlugins: - frontend: - janus-idp.backstage-plugin-lightspeed: - appIcons: - - name: LightspeedIcon - module: LightspeedPlugin - importName: LightspeedIcon - dynamicRoutes: - - path: /lightspeed - importName: LightspeedPage - module: LightspeedPlugin - menuItem: - icon: LightspeedIcon - text: Lightspeed - -``` +This package has been moved to the [red-hat-developer/rhdh-plugins](https://github.com/redhat-developer/rhdh-plugins) repository. Migrate to using `@red-hat-developer-hub/backstage-plugin-lightspeed` instead. diff --git a/plugins/lightspeed/app-config.janus-idp.yaml b/plugins/lightspeed/app-config.janus-idp.yaml deleted file mode 100644 index 15a6a9285c..0000000000 --- a/plugins/lightspeed/app-config.janus-idp.yaml +++ /dev/null @@ -1,14 +0,0 @@ -dynamicPlugins: - frontend: - janus-idp.backstage-plugin-lightspeed: - appIcons: - - name: LightspeedIcon - module: LightspeedPlugin - importName: LightspeedIcon - dynamicRoutes: - - path: /lightspeed - importName: LightspeedPage - module: LightspeedPlugin - menuItem: - icon: LightspeedIcon - text: Lightspeed diff --git a/plugins/lightspeed/config.d.ts b/plugins/lightspeed/config.d.ts deleted file mode 100644 index a3a434392b..0000000000 --- a/plugins/lightspeed/config.d.ts +++ /dev/null @@ -1,26 +0,0 @@ -export interface Config { - /** - * Configuration required for using lightspeed - * @visibility frontend - */ - lightspeed: { - /** - * @visibility frontend - */ - servers: Array; - }; -} diff --git a/plugins/lightspeed/dev/index.tsx b/plugins/lightspeed/dev/index.tsx deleted file mode 100644 index 93cdc7442a..0000000000 --- a/plugins/lightspeed/dev/index.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -import { createDevApp } from '@backstage/dev-utils'; - -import { LightspeedPage, lightspeedPlugin } from '../src/plugin'; - -createDevApp() - .registerPlugin(lightspeedPlugin) - .addPage({ - element: , - title: 'Lightspeed Page', - path: '/lightspeed', - }) - .render(); diff --git a/plugins/lightspeed/package.json b/plugins/lightspeed/package.json deleted file mode 100644 index 96d34d58c4..0000000000 --- a/plugins/lightspeed/package.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "name": "@janus-idp/backstage-plugin-lightspeed", - "version": "0.1.2", - "main": "src/index.ts", - "types": "src/index.ts", - "license": "Apache-2.0", - "private": true, - "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" - }, - "backstage": { - "role": "frontend-plugin", - "supported-versions": "1.32.5", - "pluginId": "lightspeed", - "pluginPackage": "@janus-idp/backstage-plugin-lightspeed", - "pluginPackages": [ - "@janus-idp/backstage-plugin-lightspeed", - "@janus-idp/backstage-plugin-lightspeed-backend" - ] - }, - "sideEffects": [ - "./**/*.css" - ], - "scripts": { - "build": "backstage-cli package build", - "clean": "backstage-cli package clean", - "lint:check": "backstage-cli package lint", - "lint:fix": "backstage-cli package lint --fix", - "postpack": "backstage-cli package postpack", - "prepack": "backstage-cli package prepack", - "start": "backstage-cli package start", - "test": "backstage-cli package test --passWithNoTests --coverage", - "tsc": "tsc", - "prettier:check": "prettier --ignore-unknown --check .", - "prettier:fix": "prettier --ignore-unknown --write ." - }, - "dependencies": { - "@backstage/core-components": "^0.15.1", - "@backstage/core-plugin-api": "^1.10.0", - "@backstage/theme": "^0.6.0", - "@material-ui/core": "^4.9.13", - "@material-ui/icons": "^4.11.3", - "@material-ui/lab": "^4.0.0-alpha.61", - "@mui/icons-material": "^5.15.18", - "@patternfly/react-core": "6.0.0-prerelease.21", - "@patternfly/virtual-assistant": "2.0.0-alpha.61", - "@tanstack/react-query": "^5.59.15", - "openai": "^4.52.6", - "react-markdown": "^9.0.1", - "react-use": "^17.2.4" - }, - "peerDependencies": { - "react": "16.13.1 || ^17.0.0 || ^18.0.0" - }, - "devDependencies": { - "@backstage/cli": "0.28.2", - "@backstage/core-app-api": "1.15.1", - "@backstage/dev-utils": "1.1.2", - "@backstage/test-utils": "1.7.0", - "@testing-library/jest-dom": "6.4.8", - "@testing-library/react": "^16.0.1", - "@testing-library/react-hooks": "8.0.1", - "@testing-library/user-event": "14.5.2", - "msw": "1.3.3", - "prettier": "3.3.3" - }, - "files": [ - "dist", - "config.d.ts", - "dist-scalprum", - "app-config.janus-idp.yaml" - ], - "scalprum": { - "name": "janus-idp.backstage-plugin-lightspeed", - "exposedModules": { - "LightspeedPlugin": "./src/index.ts" - } - }, - "configSchema": "config.d.ts", - "repository": { - "type": "git", - "url": "git+https://github.com/janus-idp/backstage-plugins.git", - "directory": "plugins/lightspeed" - }, - "keywords": [ - "lifecycle:active", - "backstage", - "plugin" - ], - "homepage": "https://red.ht/rhdh", - "bugs": "https://github.com/janus-idp/backstage-plugins/issues", - "maintainers": [ - "@janus-idp/rhtap" - ], - "author": "Red Hat" -} diff --git a/plugins/lightspeed/src/api/LightspeedApiClient.ts b/plugins/lightspeed/src/api/LightspeedApiClient.ts deleted file mode 100644 index 38ac54dcde..0000000000 --- a/plugins/lightspeed/src/api/LightspeedApiClient.ts +++ /dev/null @@ -1,137 +0,0 @@ -import { ConfigApi, FetchApi } from '@backstage/core-plugin-api'; - -import { LightspeedAPI } from './api'; - -export type Options = { - configApi: ConfigApi; - fetchApi: FetchApi; -}; - -export class LightspeedApiClient implements LightspeedAPI { - private readonly configApi: ConfigApi; - private readonly fetchApi: FetchApi; - - constructor(options: Options) { - this.configApi = options.configApi; - this.fetchApi = options.fetchApi; - } - - async getBaseUrl() { - return `${this.configApi.getString('backend.baseUrl')}/api/lightspeed`; - } - - getServerUrl() { - // Currently supports a single llm server - return `${this.configApi.getConfigArray('lightspeed.servers')[0].getOptionalString('url')}`; - } - - async createMessage( - prompt: string, - selectedModel: string, - conversation_id: string, - ) { - const baseUrl = await this.getBaseUrl(); - - const response = await this.fetchApi.fetch(`${baseUrl}/v1/query`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - conversation_id, - serverURL: this.getServerUrl(), - model: selectedModel, - query: prompt, - historyLength: 10, - }), - }); - - if (!response.body) { - throw new Error('Readable stream is not supported or there is no body.'); - } - - if (!response.ok) { - throw new Error( - `failed to fetch data, status ${response.status}: ${response.statusText}`, - ); - } - return response.body.getReader(); - } - - private async fetcher(url: string) { - const response = await this.fetchApi.fetch(url, { - headers: { - 'Content-Type': 'application/json', - }, - }); - if (!response.ok) { - throw new Error( - `failed to fetch data, status ${response.status}: ${response.statusText}`, - ); - } - return response; - } - - async getAllModels() { - const baseUrl = await this.getBaseUrl(); - const result = await this.fetcher(`${baseUrl}/v1/models`); - const response = await result.json(); - return response?.data ? response.data : []; - } - - async getConversationMessages(conversation_id: string) { - const baseUrl = await this.getBaseUrl(); - const result = await this.fetcher( - `${baseUrl}/conversations/${encodeURIComponent(conversation_id)}`, - ); - return await result.json(); - } - - async getConversations() { - const baseUrl = await this.getBaseUrl(); - const result = await this.fetcher(`${baseUrl}/conversations`); - return await result.json(); - } - - async createConversation() { - const baseUrl = await this.getBaseUrl(); - - const response = await this.fetchApi.fetch(`${baseUrl}/conversations`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({}), - }); - - if (!response.body) { - throw new Error('Something went wrong.'); - } - - if (!response.ok) { - throw new Error( - `failed to create conversation, status ${response.status}: ${response.statusText}`, - ); - } - return await response.json(); - } - - async deleteConversation(conversation_id: string) { - const baseUrl = await this.getBaseUrl(); - - const response = await this.fetchApi.fetch( - `${baseUrl}/conversations/${encodeURIComponent(conversation_id)}`, - { - method: 'DELETE', - headers: {}, - }, - ); - - if (!response.ok) { - throw new Error( - `failed to delete conversation, status ${response.status}: ${response.statusText}`, - ); - } - return { success: true }; - } -} diff --git a/plugins/lightspeed/src/api/api.ts b/plugins/lightspeed/src/api/api.ts deleted file mode 100644 index eb6b2487e7..0000000000 --- a/plugins/lightspeed/src/api/api.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createApiRef } from '@backstage/core-plugin-api'; - -import OpenAI from 'openai'; - -import { BaseMessage, ConversationList } from '../types'; - -export type LightspeedAPI = { - getAllModels: () => Promise; - getConversationMessages: (conversation_id: string) => Promise; - createConversation: () => Promise<{ conversation_id: string }>; - createMessage: ( - prompt: string, - selectedModel: string, - conversation_id: string, - ) => Promise; - deleteConversation: ( - conversation_id: string, - ) => Promise<{ success: boolean }>; - getConversations: () => Promise; -}; - -export const lightspeedApiRef = createApiRef({ - id: 'plugin.lightspeed.service', -}); diff --git a/plugins/lightspeed/src/components/LightSpeedChat.tsx b/plugins/lightspeed/src/components/LightSpeedChat.tsx deleted file mode 100644 index 14d18eed26..0000000000 --- a/plugins/lightspeed/src/components/LightSpeedChat.tsx +++ /dev/null @@ -1,298 +0,0 @@ -import React from 'react'; - -import { makeStyles } from '@material-ui/core'; -import { DropdownItem, Title } from '@patternfly/react-core'; -import { - Chatbot, - ChatbotContent, - ChatbotDisplayMode, - ChatbotFooter, - ChatbotFootnote, - ChatbotHeader, - ChatbotHeaderMain, - ChatbotHeaderMenu, - ChatbotHeaderTitle, - MessageBar, - MessageProps, -} from '@patternfly/virtual-assistant'; -import ChatbotConversationHistoryNav from '@patternfly/virtual-assistant/dist/dynamic/ChatbotConversationHistoryNav'; -import { useQueryClient } from '@tanstack/react-query'; - -import { useBackstageUserIdentity } from '../hooks/useBackstageUserIdentity'; -import { useConversationMessages } from '../hooks/useConversationMessages'; -import { useConversations } from '../hooks/useConversations'; -import { useCreateConversation } from '../hooks/useCreateConversation'; -import { useDeleteConversation } from '../hooks/useDeleteConversation'; -import { ConversationSummary } from '../types'; -import { - getCategorizeMessages, - getFootnoteProps, -} from '../utils/lightspeed-chatbox-utils'; -import { LightspeedChatBox } from './LightspeedChatBox'; -import { LightspeedChatBoxHeader } from './LightspeedChatBoxHeader'; - -const useStyles = makeStyles(theme => ({ - content: { - '&.pf-chatbot__content': { - padding: 0, - }, - }, - header: { - padding: `${theme.spacing(3)}px !important`, - }, - headerTitle: { - justifyContent: 'left !important', - }, - footer: { - '&.pf-chatbot__footer': { - padding: - '0 var(--pf-t--global--spacer--lg) var(--pf-t--global--spacer--lg) var(--pf-t--global--spacer--lg)', - }, - }, -})); - -type LightspeedChatProps = { - selectedModel: string; - userName?: string; - avatar?: string; - profileLoading: boolean; - handleSelectedModel: (item: string) => void; - models: { label: string; value: string }[]; -}; - -export const LightspeedChat = ({ - selectedModel, - userName, - avatar, - profileLoading, - handleSelectedModel, - models, -}: LightspeedChatProps) => { - const classes = useStyles(); - const user = useBackstageUserIdentity(); - const [filterValue, setFilterValue] = React.useState(''); - const [announcement, setAnnouncement] = React.useState(''); - const [conversationId, setConversationId] = React.useState(''); - const [isDrawerOpen, setIsDrawerOpen] = React.useState(true); - const [newChatCreated, setNewChatCreated] = React.useState(true); - const [isSendButtonDisabled, setIsSendButtonDisabled] = - React.useState(false); - - const queryClient = useQueryClient(); - - const { data: conversations = [] } = useConversations(); - const { mutateAsync: createConversation } = useCreateConversation(); - const { mutateAsync: deleteConversation } = useDeleteConversation(); - - React.useEffect(() => { - if (user) { - createConversation() - .then(({ conversation_id }) => { - setConversationId(conversation_id); - }) - .catch(e => { - // eslint-disable-next-line - console.warn(e); - }); - } - }, [user, setConversationId, createConversation]); - - const onComplete = (message: string) => { - setIsSendButtonDisabled(false); - setAnnouncement(`Message from Bot: ${message}`); - queryClient.invalidateQueries({ - queryKey: ['conversations'], - }); - }; - - const { conversationMessages, handleInputPrompt, scrollToBottomRef } = - useConversationMessages( - conversationId, - userName, - selectedModel, - avatar, - onComplete, - ); - - const [messages, setMessages] = - React.useState(conversationMessages); - - // Auto-scrolls to the latest message - React.useEffect(() => { - if (messages.length > 2) { - setTimeout(() => { - scrollToBottomRef.current?.scrollIntoView({ behavior: 'auto' }); - }, 10); - } - // eslint-disable-next-line - }, [messages, scrollToBottomRef.current]); - - const sendMessage = (message: string) => { - setNewChatCreated(false); - setAnnouncement( - `Message from User: ${prompt}. Message from Bot is loading.`, - ); - handleInputPrompt(message); - setIsSendButtonDisabled(true); - }; - - const onNewChat = React.useCallback(() => { - (async () => { - setMessages([]); - const { conversation_id } = await createConversation(); - setConversationId(conversation_id); - setNewChatCreated(true); - })(); - }, [createConversation, setConversationId, setMessages]); - - const additionalMessageProps = React.useCallback( - (conversationSummary: ConversationSummary) => ({ - menuItems: ( - { - try { - await deleteConversation({ - conversation_id: conversationSummary.conversation_id, - invalidateCache: false, - }); - onNewChat(); - } catch (error) { - // eslint-disable-next-line no-console - console.warn({ error }); - } - }} - > - Delete - - ), - }), - [deleteConversation, onNewChat], - ); - const categorizedMessages = getCategorizeMessages( - conversations, - additionalMessageProps, - ); - - const filterConversations = React.useCallback( - (targetValue: string) => { - const filteredConversations = Object.entries(categorizedMessages).reduce( - (acc, [key, items]) => { - const filteredItems = items.filter(item => - item.text.toLowerCase().includes(targetValue.toLowerCase()), - ); - if (filteredItems.length > 0) { - acc[key] = filteredItems; - } - return acc; - }, - {} as any, - ); - return filteredConversations; - }, - [categorizedMessages], - ); - - React.useEffect(() => { - setMessages(conversationMessages); - }, [conversationMessages]); - - const onSelectActiveItem = React.useCallback( - ( - _: React.MouseEvent | undefined, - selectedItem: string | number | undefined, - ) => { - setNewChatCreated(false); - setConversationId((c_id: string) => { - if (c_id !== selectedItem) { - return String(selectedItem); - } - return c_id; - }); - }, - [setConversationId], - ); - - const welcomePrompts = newChatCreated - ? [ - { - title: 'Topic 1', - message: 'Helpful prompt for Topic 1', - onClick: () => sendMessage('Helpful prompt for Topic 1'), - }, - { - title: 'Topic 2', - message: 'Helpful prompt for Topic 2', - onClick: () => sendMessage('Helpful prompt for Topic 2'), - }, - ] - : []; - - const handleFilter = React.useCallback((value: string) => { - setFilterValue(value); - }, []); - - const onDrawerToggle = React.useCallback(() => { - setIsDrawerOpen(isOpen => !isOpen); - }, []); - - return ( - <> - - - - - setIsDrawerOpen(!isDrawerOpen)} - /> - - - Developer Hub Lightspeed - - - - - handleSelectedModel(item)} - models={models} - /> - - - - - - - - - - - } - /> - - - ); -}; diff --git a/plugins/lightspeed/src/components/LightspeedChatBox.tsx b/plugins/lightspeed/src/components/LightspeedChatBox.tsx deleted file mode 100644 index 46607026f6..0000000000 --- a/plugins/lightspeed/src/components/LightspeedChatBox.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import React from 'react'; - -import { makeStyles } from '@material-ui/core'; -import { - ChatbotWelcomePrompt, - Message, - MessageBox, - MessageProps, - WelcomePrompt, -} from '@patternfly/virtual-assistant'; - -const useStyles = makeStyles(theme => ({ - prompt: { - 'justify-content': 'flex-end', - }, - userMessageText: { - '& div.pf-chatbot__message--user': { - '& div.pf-chatbot__message-text': { - '& p': { - color: theme.palette.common.white, - }, - }, - }, - }, -})); - -type LightspeedChatBoxProps = { - userName?: string; - messages: MessageProps[]; - profileLoading: boolean; - announcement: string | undefined; - welcomePrompts: WelcomePrompt[]; -}; - -export const LightspeedChatBox = React.forwardRef( - ( - { - userName, - messages, - announcement, - profileLoading, - welcomePrompts, - }: LightspeedChatBoxProps, - ref: React.ForwardedRef, - ) => { - const [cmessages, setCMessages] = React.useState(messages); - const classes = useStyles(); - - React.useEffect(() => { - setCMessages(messages); - }, [messages]); - - return ( - - {welcomePrompts.length ? ( - - ) : ( -
- )} - {cmessages.map((message, index) => { - if (index === cmessages.length - 1) { - return ( - - -
- - ); - } - return ; - })} - - ); - }, -); diff --git a/plugins/lightspeed/src/components/LightspeedChatBoxHeader.tsx b/plugins/lightspeed/src/components/LightspeedChatBoxHeader.tsx deleted file mode 100644 index eacc652684..0000000000 --- a/plugins/lightspeed/src/components/LightspeedChatBoxHeader.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import React from 'react'; - -import { - Dropdown, - DropdownItem, - DropdownList, - MenuToggle, - MenuToggleElement, -} from '@patternfly/react-core'; -import { ChatbotHeaderActions } from '@patternfly/virtual-assistant'; - -type LightspeedChatBoxHeaderProps = { - selectedModel: string; - handleSelectedModel: (item: string) => void; - models: { label: string; value: string }[]; -}; - -export const LightspeedChatBoxHeader = ({ - selectedModel, - handleSelectedModel, - models, -}: LightspeedChatBoxHeaderProps) => { - const [isOptionsMenuOpen, setIsOptionsMenuOpen] = React.useState(false); - - const toggle = (toggleRef: React.Ref) => ( - setIsOptionsMenuOpen(!isOptionsMenuOpen)} - > - {selectedModel} - - ); - - return ( - - { - handleSelectedModel(value as string); - setIsOptionsMenuOpen(false); - }} - onOpenChange={isOpen => setIsOptionsMenuOpen(isOpen)} - popperProps={{ position: 'right' }} - shouldFocusToggleOnSelect - shouldFocusFirstItemOnOpen={false} - toggle={toggle} - > - - {models.map(m => ( - - {m.label} - - ))} - - - - ); -}; diff --git a/plugins/lightspeed/src/components/LightspeedIcon.tsx b/plugins/lightspeed/src/components/LightspeedIcon.tsx deleted file mode 100644 index 9384f8aebf..0000000000 --- a/plugins/lightspeed/src/components/LightspeedIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import * as React from 'react'; - -import logo from '../images/logo.svg'; - -export const LightspeedIcon = () => { - return ( - lightspeed icon - ); -}; diff --git a/plugins/lightspeed/src/components/LightspeedPage.tsx b/plugins/lightspeed/src/components/LightspeedPage.tsx deleted file mode 100644 index fffbfbe5ae..0000000000 --- a/plugins/lightspeed/src/components/LightspeedPage.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import React from 'react'; -import { useAsync } from 'react-use'; - -import { Content, Page } from '@backstage/core-components'; -import { identityApiRef, useApi } from '@backstage/core-plugin-api'; - -import { createStyles, makeStyles, useTheme } from '@material-ui/core/styles'; -import { QueryClientProvider } from '@tanstack/react-query'; - -import { useAllModels } from '../hooks/useAllModels'; -import queryClient from '../utils/queryClient'; -import { LightspeedChat } from './LightSpeedChat'; - -const useStyles = makeStyles(() => - createStyles({ - container: { - padding: '0px', - }, - }), -); - -const THEME_DARK = 'dark'; -const THEME_DARK_CLASS = 'pf-v6-theme-dark'; - -const LightspeedPageInner = () => { - const classes = useStyles(); - const { - palette: { type }, - } = useTheme(); - - const identityApi = useApi(identityApiRef); - - const { data: models } = useAllModels(); - - const { value: profile, loading: profileLoading } = useAsync( - async () => await identityApi.getProfileInfo(), - ); - - const [selectedModel, setSelectedModel] = React.useState(''); - - const modelsItems = React.useMemo( - () => (models ? models.map(m => ({ label: m.id, value: m.id })) : []), - [models], - ); - - React.useEffect(() => { - const htmlTagElement = document.documentElement; - if (type === THEME_DARK) { - htmlTagElement.classList.add(THEME_DARK_CLASS); - } else { - htmlTagElement.classList.remove(THEME_DARK_CLASS); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [type]); - - React.useEffect(() => { - if (modelsItems.length > 0) setSelectedModel(modelsItems[0].value); - }, [modelsItems]); - - return ( - - - { - setSelectedModel(item); - }} - models={modelsItems} - userName={profile?.displayName} - avatar={profile?.picture} - profileLoading={profileLoading} - /> - - - ); -}; - -export const LightspeedPage = () => { - return ( - - - - ); -}; diff --git a/plugins/lightspeed/src/globals.d.ts b/plugins/lightspeed/src/globals.d.ts deleted file mode 100644 index 006534e235..0000000000 --- a/plugins/lightspeed/src/globals.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -declare module '*.svg' { - const content: React.FunctionComponent>; - export default content; -} diff --git a/plugins/lightspeed/src/hooks/__tests__/useBackstageUserIdentity.test.ts b/plugins/lightspeed/src/hooks/__tests__/useBackstageUserIdentity.test.ts deleted file mode 100644 index 828e8c3346..0000000000 --- a/plugins/lightspeed/src/hooks/__tests__/useBackstageUserIdentity.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { renderHook, waitFor } from '@testing-library/react'; - -import { useBackstageUserIdentity } from '../useBackstageUserIdentity'; - -jest.mock('@backstage/core-plugin-api', () => ({ - ...jest.requireActual('@backstage/core-plugin-api'), - useApi: jest.fn(), -})); - -const identityMock = jest.fn(); -const identityApi = { - getBackstageIdentity: identityMock, -}; - -(useApi as jest.Mock).mockReturnValue(identityApi); - -describe('useBackstageUserIdentity', () => { - it('should return undefined when there is no user data available', async () => { - const { result } = renderHook(() => useBackstageUserIdentity()); - - await waitFor(() => { - expect(result.current).toBeUndefined(); - }); - }); - - it('should return user identity', async () => { - identityMock.mockReturnValue({ - userEntityRef: 'user:default/guest', - }); - const { result } = renderHook(() => useBackstageUserIdentity()); - - await waitFor(() => { - expect(result.current).not.toBeUndefined(); - }); - }); -}); diff --git a/plugins/lightspeed/src/hooks/__tests__/useConversationMessages.test.tsx b/plugins/lightspeed/src/hooks/__tests__/useConversationMessages.test.tsx deleted file mode 100644 index 4b4e9e20f8..0000000000 --- a/plugins/lightspeed/src/hooks/__tests__/useConversationMessages.test.tsx +++ /dev/null @@ -1,429 +0,0 @@ -import React from 'react'; - -import { useApi } from '@backstage/core-plugin-api'; - -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { act, renderHook, waitFor } from '@testing-library/react'; - -import { getTimestamp } from '../../utils/lightspeed-chatbox-utils'; -import { - useConversationMessages, - useFetchConversationMessages, -} from '../useConversationMessages'; - -jest.mock('../../utils/lightspeed-chatbox-utils', () => ({ - ...jest.requireActual('../../utils/lightspeed-chatbox-utils'), - getTimestamp: jest.fn(), -})); - -jest.mock('@backstage/core-plugin-api', () => ({ - ...jest.requireActual('@backstage/core-plugin-api'), - useApi: jest.fn(), -})); - -const mockLightspeedApi = { - getConversations: jest.fn(), - getConversationMessages: jest.fn(), -}; - -(useApi as jest.Mock).mockReturnValue(mockLightspeedApi); - -// Create a query client with no retries for test purposes -const queryClient = new QueryClient({ - defaultOptions: { - queries: { - retry: false, - experimental_prefetchInRender: true, - }, - }, -}); - -const wrapper = ({ - children, -}: { - conversationId?: string; - children?: React.ReactNode; -}): any => ( - {children} -); - -describe('useFetchConversations', () => { - afterEach(() => { - jest.clearAllMocks(); - queryClient.clear(); - }); - - it('should return conversations data when fetch is successful', async () => { - const mockData = [{ id: '1', content: 'Hello' }]; - mockLightspeedApi.getConversationMessages.mockResolvedValueOnce(mockData); - - const { result } = renderHook(() => useFetchConversationMessages('123'), { - wrapper, - }); - - // Wait for the query to resolve - await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(result.current.data).toEqual(mockData); - expect(mockLightspeedApi.getConversationMessages).toHaveBeenCalledWith( - '123', - ); - }); - - it('should handle loading state', async () => { - mockLightspeedApi.getConversationMessages.mockImplementation( - () => new Promise(() => {}), - ); - - const { result } = renderHook(() => useFetchConversationMessages('123'), { - wrapper, - }); - - await waitFor(() => { - expect(result.current.isLoading).toBe(true); - }); - }); - - it('should handle errors', async () => { - mockLightspeedApi.getConversationMessages.mockImplementation( - () => new Promise(() => {}), - ); - const errorMessage = 'Failed to fetch conversations'; - mockLightspeedApi.getConversationMessages.mockRejectedValueOnce( - new Error(errorMessage), - ); - - const { result } = renderHook(() => useFetchConversationMessages('123'), { - wrapper, - }); - - await waitFor(() => expect(result.current.isError).toBe(true)); - expect(result.current.error).toBeDefined(); - expect(result.current.error?.message ?? '').toBe(errorMessage); - }); -}); - -describe('useConversationMesages', () => { - beforeEach(() => { - jest.resetAllMocks(); - }); - - it('should initialize conversations with the given conversationId', () => { - const { result } = renderHook( - () => - useConversationMessages( - 'testConversationId', - 'test-user', - 'gpt-3', - 'avatar.png', - ), - { - wrapper, - }, - ); - - expect(result.current.conversations).toEqual({ - testConversationId: [], - }); - }); - - it('should update current conversation when conversationId changes', () => { - const { result, rerender } = renderHook( - ({ conversationId }: { conversationId: any }) => - useConversationMessages( - conversationId, - 'test-user-id', - 'gpt-3', - 'avatar.png', - ), - { - wrapper, - initialProps: { conversationId: 'initialConversationId' }, - }, - ); - - expect(result.current.conversations).toEqual({ - initialConversationId: [], - }); - - rerender({ conversationId: 'updatedConversationId' }); - - expect(result.current.conversations).toEqual({ - updatedConversationId: [], - }); - }); - - it('should call onComplete when streaming is done', async () => { - const onComplete = jest.fn(); - - const lightSpeedApi = { - createMessage: jest.fn().mockResolvedValue({ - read: jest - .fn() - .mockResolvedValueOnce({ - done: false, - value: new TextEncoder().encode( - '{"response":{"kwargs":{"content":"Hi test-user!"}}}', - ), - }) - .mockResolvedValueOnce({ done: true, value: null }), - }), - }; - - (useApi as jest.Mock).mockReturnValue(lightSpeedApi); - - const { result } = renderHook( - () => - useConversationMessages( - 'initialConversationId', - 'test-user', - 'gpt-3', - 'user.png', - onComplete, - ), - { - wrapper, - }, - ); - - await act(async () => { - await result.current.handleInputPrompt('Hello there!'); - }); - - expect(onComplete).toHaveBeenCalledWith('Hi test-user!'); - }); - - it('should handle the invalid json error', async () => { - const onComplete = jest.fn(); - - const mockApi = { - createMessage: jest.fn().mockResolvedValue({ - read: jest - .fn() - .mockResolvedValueOnce({ - done: false, - value: new TextEncoder().encode( - '{"response":{"kwargs":{"content":"{{"key": "value"} }}}', - ), - }) - .mockResolvedValueOnce({ done: true, value: null }), - }), - }; - - (useApi as jest.Mock).mockReturnValue(mockApi); - - const { result } = renderHook( - () => - useConversationMessages( - 'testId', - 'test-user', - 'gpt-3', - 'user.png', - onComplete, - ), - { - wrapper, - }, - ); - - await act(async () => { - await result.current.handleInputPrompt('Hello there!'); - }); - - expect(onComplete).toHaveBeenCalledWith('Invalid JSON received'); - }); - - it('should handle input prompt and update conversations with user and bot messages', async () => { - const mockApi = { - createMessage: jest.fn().mockResolvedValue({ - read: jest.fn().mockResolvedValue({ done: true, value: null }), - }), - }; - (useApi as jest.Mock).mockReturnValue(mockApi); - - const { result } = renderHook( - () => - useConversationMessages( - 'testConversationId', - 'test-user', - 'gpt-3', - 'avatar.png', - ), - { - wrapper, - }, - ); - - const prompt = 'Hello, how are you?'; - const userTimestamp = '01/01/2024, 10:00:00'; - const botTimestamp = '01/01/2024, 10:00:01'; - - (getTimestamp as jest.Mock) - .mockReturnValueOnce(userTimestamp) - .mockReturnValueOnce(botTimestamp); - - await act(async () => { - await result.current.handleInputPrompt(prompt); - }); - - expect(result.current.conversations.testConversationId).toEqual([ - { - role: 'user', - content: prompt, - timestamp: userTimestamp, - avatar: 'avatar.png', - name: 'test-user', - isLoading: false, - }, - { - role: 'bot', - content: '', - timestamp: '', - avatar: 'logo.svg', - name: 'gpt-3', - isLoading: true, - }, - ]); - - expect(mockApi.createMessage).toHaveBeenCalledWith( - prompt, - 'gpt-3', - 'testConversationId', - ); - }); - - it('should update last bot message in conversation after API response', async () => { - const mockApi = { - createMessage: jest.fn().mockResolvedValue({ - read: jest - .fn() - .mockResolvedValueOnce({ - done: false, - value: new TextEncoder().encode( - '{"response":{"kwargs":{"content":"Hi test-user!"}}}', - ), - }) - .mockResolvedValueOnce({ done: true, value: null }), - }), - }; - - (useApi as jest.Mock).mockReturnValue(mockApi); - - const { result } = renderHook( - () => - useConversationMessages( - 'testConversationId', - 'test-user', - 'gpt-3', - 'avatar.png', - ), - { wrapper }, - ); - - const prompt = 'Hello, how are you?'; - - await act(async () => { - await result.current.handleInputPrompt(prompt); - }); - - await waitFor(() => { - expect(result.current.conversations.testConversationId).toEqual([ - expect.objectContaining({ - role: 'user', - content: prompt, - }), - expect.objectContaining({ - role: 'bot', - content: 'Hi test-user!', - isLoading: false, - }), - ]); - }); - }); - - it('should surface API error if last bot message failed', async () => { - const mockLsApiClient = { - createMessage: jest - .fn() - .mockRejectedValue(new Error('Failed to create message')), - }; - - (useApi as jest.Mock).mockReturnValue(mockLsApiClient); - - const { result } = renderHook( - () => - useConversationMessages( - 'testConversationID', - 'test-user', - 'gpt-3', - 'avatar.png', - ), - { wrapper }, - ); - - const prompt = 'what is json?'; - - await act(async () => { - await result.current.handleInputPrompt(prompt); - }); - - await waitFor(() => { - expect(result.current.conversations.testConversationID).toEqual([ - expect.objectContaining({ - role: 'user', - content: prompt, - }), - expect.objectContaining({ - role: 'bot', - content: 'Error: Failed to create message', - isLoading: false, - }), - ]); - }); - }); - - it('should have scrollToBottomRef defined', async () => { - const mockData = [ - { - id: ['HumanMessage'], - kwargs: { - content: 'Hello!', - response_metadata: { - created_at: 1609459200000, - }, - }, - }, - { - id: ['AiMessage'], - kwargs: { - content: 'Hi, How are you!', - response_metadata: { - created_at: 1609459200000, - }, - }, - }, - ]; - - const mockLsApi = { - getConversationMessages: jest.fn().mockResolvedValue(mockData), - }; - - (useApi as jest.Mock).mockReturnValue(mockLsApi); - - const { result } = renderHook( - () => - useConversationMessages( - 'testConversationId', - 'test-user', - 'gpt-3', - 'avatar.png', - ), - { - wrapper, - }, - ); - await waitFor(() => { - expect(result.current.conversations.testConversationId).toHaveLength(2); - expect(result.current.scrollToBottomRef).toBeDefined(); - }); - }); -}); diff --git a/plugins/lightspeed/src/hooks/__tests__/useDeleteConversation.test.tsx b/plugins/lightspeed/src/hooks/__tests__/useDeleteConversation.test.tsx deleted file mode 100644 index d08d980770..0000000000 --- a/plugins/lightspeed/src/hooks/__tests__/useDeleteConversation.test.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import React from 'react'; - -import { useApi } from '@backstage/core-plugin-api'; - -import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; -import { act, renderHook, waitFor } from '@testing-library/react'; - -import { useDeleteConversation } from '../useDeleteConversation'; - -// Mocking the useApi and lightspeed API -jest.mock('@backstage/core-plugin-api', () => ({ - ...jest.requireActual('@backstage/core-plugin-api'), - useApi: jest.fn(), -})); - -const mockDeleteConversation = jest.fn().mockResolvedValue({}); -const mockGetQueryData = jest.fn(); -const mockSetQueryData = jest.fn(); -const mockCancelQueries = jest.fn(); -const mockInvalidateQueries = jest.fn(); - -// Provide a query client with mock methods for cache manipulation -const queryClient = new QueryClient({ - defaultOptions: { - queries: { retry: false }, - }, -}); - -queryClient.getQueryData = mockGetQueryData; -queryClient.setQueryData = mockSetQueryData; -queryClient.cancelQueries = mockCancelQueries; -queryClient.invalidateQueries = mockInvalidateQueries; - -const wrapper = ({ - children, -}: { - conversationId?: string; - children?: React.ReactNode; -}): any => ( - {children} -); - -const mockLightspeedApi = { - getConversations: jest.fn(), - getConversationMessages: jest.fn(), -}; - -(useApi as jest.Mock).mockReturnValue(mockLightspeedApi); -describe('useDeleteConversation', () => { - beforeEach(() => { - queryClient.clear(); - mockDeleteConversation.mockClear(); - mockGetQueryData.mockClear(); - mockSetQueryData.mockClear(); - mockCancelQueries.mockClear(); - mockInvalidateQueries.mockClear(); - - // Mocking lightspeedApiRef and queryClient methods - (useApi as jest.Mock).mockReturnValue({ - deleteConversation: mockDeleteConversation, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('calls deleteConversation API and updates cache correctly', async () => { - const conversationId = 'test-id'; - const previousConversations = [{ conversation_id: conversationId }]; - - mockGetQueryData.mockReturnValue(previousConversations); - - const { result } = renderHook(() => useDeleteConversation(), { wrapper }); - - await act(async () => { - await result.current.mutate({ conversation_id: conversationId }); - }); - - expect(mockDeleteConversation).toHaveBeenCalledWith(conversationId); - expect(mockCancelQueries).toHaveBeenCalledWith({ - queryKey: ['conversations'], - }); - expect(mockSetQueryData).toHaveBeenCalledWith( - ['conversations'], - expect.any(Function), - ); - }); - - it('invalidates cache on success if invalidateCache is true', async () => { - const conversationId = 'test-conv-id'; - - const { result } = renderHook(() => useDeleteConversation(), { wrapper }); - - await act(async () => { - await result.current.mutate({ - conversation_id: conversationId, - invalidateCache: true, - }); - }); - - await waitFor(() => { - expect(mockInvalidateQueries).toHaveBeenCalledWith({ - queryKey: ['conversations'], - }); - }); - }); - - it('reverts cache on error', async () => { - const conversationId = 'test-cache-error-id'; - const previousConversations = [{ conversation_id: conversationId }]; - - mockGetQueryData.mockReturnValue(previousConversations); - mockDeleteConversation.mockRejectedValue(new Error('Delete failed')); - - const { result } = renderHook(() => useDeleteConversation(), { wrapper }); - - await act(async () => { - await result.current.mutate({ conversation_id: conversationId }); - }); - - expect(mockSetQueryData).toHaveBeenCalledWith( - ['conversations'], - previousConversations, - ); - }); -}); diff --git a/plugins/lightspeed/src/hooks/useAllModels.ts b/plugins/lightspeed/src/hooks/useAllModels.ts deleted file mode 100644 index 7468f6dd25..0000000000 --- a/plugins/lightspeed/src/hooks/useAllModels.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { useQuery } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; - -// Fetch all models -export const useAllModels = () => { - const lightspeedApi = useApi(lightspeedApiRef); - return useQuery({ - queryKey: ['models'], - queryFn: async () => { - const response = await lightspeedApi.getAllModels(); - return response; - }, - staleTime: 1000 * 60 * 5, // 5 minutes - }); -}; diff --git a/plugins/lightspeed/src/hooks/useBackstageUserIdentity.ts b/plugins/lightspeed/src/hooks/useBackstageUserIdentity.ts deleted file mode 100644 index 69aa2237ce..0000000000 --- a/plugins/lightspeed/src/hooks/useBackstageUserIdentity.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useAsync } from 'react-use'; - -import { identityApiRef, useApi } from '@backstage/core-plugin-api'; - -export const useBackstageUserIdentity = () => { - const identityApi = useApi(identityApiRef); - const { value: user } = useAsync(async () => { - const identityRef = await identityApi.getBackstageIdentity(); - return identityRef.userEntityRef; - }); - return user; -}; diff --git a/plugins/lightspeed/src/hooks/useConversationMessages.ts b/plugins/lightspeed/src/hooks/useConversationMessages.ts deleted file mode 100644 index 0f98466c86..0000000000 --- a/plugins/lightspeed/src/hooks/useConversationMessages.ts +++ /dev/null @@ -1,264 +0,0 @@ -import React from 'react'; - -import { useApi } from '@backstage/core-plugin-api'; - -import { MessageProps } from '@patternfly/virtual-assistant'; -import { useQuery } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; -import logo from '../images/logo.svg'; -import { - createBotMessage, - createUserMessage, - getMessageData, - getTimestamp, - splitJsonStrings, -} from '../utils/lightspeed-chatbox-utils'; -import { useCreateConversationMessage } from './useCreateCoversationMessage'; - -// Fetch all conversation messages -export const useFetchConversationMessages = (currentConversation: string) => { - const lightspeedApi = useApi(lightspeedApiRef); - return useQuery({ - queryKey: ['conversationMessages', currentConversation], - queryFn: currentConversation - ? async () => { - const response = - await lightspeedApi.getConversationMessages(currentConversation); - - return response; - } - : undefined, - retry: false, - }); -}; - -type Conversations = { [_key: string]: MessageProps[] }; - -/** - * Fetches all the messages for given conversation_id - * @param conversationId - * @param userName - * @param selectedModel - * @param avatar - * - */ -export const useConversationMessages = ( - conversationId: string, - userName: string | undefined, - selectedModel: string, - avatar: string | undefined, - onComplete?: (message: string) => void, -) => { - const { mutateAsync: createMessage } = useCreateConversationMessage(); - const scrollToBottomRef = React.useRef(null); - - const [currentConversation, setCurrentConversation] = - React.useState(conversationId); - const [conversations, setConversations] = React.useState({ - [currentConversation]: [], - }); - - React.useEffect(() => { - if (currentConversation !== conversationId) { - setCurrentConversation(conversationId); - setConversations({ - [conversationId]: [], - }); - } - }, [currentConversation, conversationId]); - - const { data: conversationsData = [], ...queryProps } = - useFetchConversationMessages(currentConversation); - - React.useEffect(() => { - if (!Array.isArray(conversationsData) || conversationsData.length === 0) - return; - - const newConvoIndex: number[] = []; - - if (conversations) { - const _conversations: { [key: string]: any[] } = { - [currentConversation]: [], - }; - - let index = 0; - for (let i = 0; i < conversationsData.length; i += 2) { - const userMessage = conversationsData[i]; - const aiMessage = conversationsData[i + 1]; - - const { content: humanMessage, timestamp: userTimestamp } = - getMessageData(userMessage); - const { - model, - content: botMessage, - timestamp: botTimestamp, - } = getMessageData(aiMessage); - - _conversations[currentConversation].push( - ...[ - createUserMessage({ - avatar, - name: userName, - content: humanMessage, - timestamp: userTimestamp, - }), - createBotMessage({ - avatar: logo, - isLoading: false, - name: model ?? selectedModel, - content: botMessage, - timestamp: botTimestamp, - }), - ], - ); - - newConvoIndex.push(index); - index++; - } - setConversations(_conversations); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [conversationsData, userName, avatar, currentConversation, selectedModel]); - - const handleInputPrompt = React.useCallback( - async (prompt: string) => { - setConversations((prevConv: Conversations) => { - return { - ...prevConv, - [currentConversation]: [ - ...(prevConv?.[currentConversation] ?? []), - createUserMessage({ - avatar, - name: userName, - content: prompt, - timestamp: getTimestamp(Date.now()) ?? '', - }), - createBotMessage({ - avatar: logo, - isLoading: true, - name: selectedModel, - content: '', - timestamp: '', - }), - ], - }; - }); - - setTimeout(() => { - scrollToBottomRef.current?.scrollIntoView({ behavior: 'auto' }); - }, 0); - const finalMessages: string[] = []; - - try { - const reader = await createMessage({ - prompt, - selectedModel, - currentConversation, - }); - - const decoder = new TextDecoder('utf-8'); - const keepGoing = true; - while (keepGoing) { - const { done, value } = await reader.read(); - if (done) break; - - const chunk = decoder.decode(value, { stream: true }); - - const data = splitJsonStrings(chunk) ?? []; - data?.forEach(line => { - const trimmedLine = line.trim(); - // Ignore empty lines - if (!trimmedLine) return; - try { - const jsonData = JSON.parse(line); - const content = jsonData?.response?.kwargs?.content || ''; - finalMessages.push(content); - setConversations(prevConversations => { - const conversation = prevConversations[currentConversation]; - - if (!conversation) { - return prevConversations; - } - const lastMessageIndex = conversation.length - 1; - const lastMessage = { ...conversation[lastMessageIndex] }; - - lastMessage.isLoading = false; - lastMessage.content += content; - lastMessage.name = - jsonData?.response?.kwargs?.response_metadata?.model; - lastMessage.timestamp = getTimestamp( - jsonData?.response?.kwargs?.response_metadata?.created_at || - Date.now(), - ); - - const updatedConversation = [ - ...conversation.slice(0, lastMessageIndex), - lastMessage, - ]; - - return { - ...prevConversations, - [currentConversation]: updatedConversation, - }; - }); - } catch (error) { - // eslint-disable-next-line no-console - console.warn('Error parsing JSON:', error); - if (typeof onComplete === 'function') { - onComplete('Invalid JSON received'); - } - } - }); - } - } catch (e) { - setConversations(prevConversations => { - const conversation = prevConversations[currentConversation]; - - if (!conversation) { - return prevConversations; - } - const lastMessageIndex = conversation.length - 1; - const lastMessage = { ...conversation[lastMessageIndex] }; - - lastMessage.isLoading = false; - lastMessage.content += e; - lastMessage.timestamp = getTimestamp(Date.now()); - - const updatedConversation = [ - ...conversation.slice(0, lastMessageIndex), - lastMessage, - ]; - - finalMessages.push(`${e}`); - - return { - ...prevConversations, - [currentConversation]: updatedConversation, - }; - }); - } - - if (typeof onComplete === 'function') { - onComplete(finalMessages.join('')); - } - }, - - [ - avatar, - userName, - onComplete, - selectedModel, - createMessage, - currentConversation, - ], - ); - - return { - conversationMessages: conversations[currentConversation] ?? [], - handleInputPrompt, - conversations, - scrollToBottomRef, - ...queryProps, - }; -}; diff --git a/plugins/lightspeed/src/hooks/useConversations.ts b/plugins/lightspeed/src/hooks/useConversations.ts deleted file mode 100644 index 0ac3d8fd86..0000000000 --- a/plugins/lightspeed/src/hooks/useConversations.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { useQuery } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; - -// Fetch all conversations -export const useConversations = () => { - const lightspeedApi = useApi(lightspeedApiRef); - return useQuery({ - queryKey: ['conversations'], - queryFn: async () => { - const response = await lightspeedApi.getConversations(); - return response; - }, - staleTime: 1000 * 60 * 5, // 5 minutes - }); -}; diff --git a/plugins/lightspeed/src/hooks/useCreateConversation.ts b/plugins/lightspeed/src/hooks/useCreateConversation.ts deleted file mode 100644 index 2ee1d56987..0000000000 --- a/plugins/lightspeed/src/hooks/useCreateConversation.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; - -export const useCreateConversation = () => { - const lightspeedApi = useApi(lightspeedApiRef); - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: () => { - return lightspeedApi.createConversation(); - }, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['conversations'] }); - }, - }); -}; diff --git a/plugins/lightspeed/src/hooks/useCreateCoversationMessage.ts b/plugins/lightspeed/src/hooks/useCreateCoversationMessage.ts deleted file mode 100644 index 5d90e43a05..0000000000 --- a/plugins/lightspeed/src/hooks/useCreateCoversationMessage.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { useMutation } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; - -export const useCreateConversationMessage = () => { - const lightspeedApi = useApi(lightspeedApiRef); - - return useMutation({ - mutationFn: async ({ - prompt, - selectedModel, - currentConversation, - }: { - prompt: string; - selectedModel: string; - currentConversation: string; - }) => { - if (!currentConversation) { - throw new Error('Failed to generate AI response'); - } - - return await lightspeedApi.createMessage( - `${prompt}`, - selectedModel, - currentConversation, - ); - }, - onError: error => { - // eslint-disable-next-line - console.warn(error); - }, - }); -}; diff --git a/plugins/lightspeed/src/hooks/useDeleteConversation.ts b/plugins/lightspeed/src/hooks/useDeleteConversation.ts deleted file mode 100644 index 3d00bcf04e..0000000000 --- a/plugins/lightspeed/src/hooks/useDeleteConversation.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useApi } from '@backstage/core-plugin-api'; - -import { useMutation, useQueryClient } from '@tanstack/react-query'; - -import { lightspeedApiRef } from '../api/api'; -import { ConversationList } from '../types'; - -export const useDeleteConversation = () => { - const lightspeedApi = useApi(lightspeedApiRef); - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async (props: { - conversation_id: string; - invalidateCache?: boolean; - }) => { - await lightspeedApi.deleteConversation(props.conversation_id); - }, - onMutate: async props => { - await queryClient.cancelQueries({ queryKey: ['conversations'] }); - - const previousConversations: ConversationList | undefined = - queryClient.getQueryData(['conversations']); - - queryClient.setQueryData(['conversations'], (old: ConversationList) => - old.filter(c => c.conversation_id !== props.conversation_id), - ); - - return { previousConversations }; - }, - onSuccess: (_, props) => { - if (props.invalidateCache) { - queryClient.invalidateQueries({ queryKey: ['conversations'] }); - } - }, - onError: (_, __, context) => { - queryClient.setQueryData( - ['conversations'], - context?.previousConversations, - ); - return { success: false }; - }, - }); -}; diff --git a/plugins/lightspeed/src/images/logo.svg b/plugins/lightspeed/src/images/logo.svg deleted file mode 100644 index 422ea7664c..0000000000 --- a/plugins/lightspeed/src/images/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/lightspeed/src/index.ts b/plugins/lightspeed/src/index.ts deleted file mode 100644 index 225b567ab3..0000000000 --- a/plugins/lightspeed/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { lightspeedPlugin, LightspeedPage } from './plugin'; -export { LightspeedIcon } from './components/LightspeedIcon'; diff --git a/plugins/lightspeed/src/plugin.test.ts b/plugins/lightspeed/src/plugin.test.ts deleted file mode 100644 index 18dec3f4c8..0000000000 --- a/plugins/lightspeed/src/plugin.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { lightspeedPlugin } from './plugin'; - -describe('lightspeed', () => { - it('should export plugin', () => { - expect(lightspeedPlugin).toBeDefined(); - }); -}); diff --git a/plugins/lightspeed/src/plugin.ts b/plugins/lightspeed/src/plugin.ts deleted file mode 100644 index 80a0fba738..0000000000 --- a/plugins/lightspeed/src/plugin.ts +++ /dev/null @@ -1,41 +0,0 @@ -import '@patternfly/react-core/dist/styles/base.css'; -import '@patternfly/virtual-assistant/dist/css/main.css'; - -import { - configApiRef, - createApiFactory, - createPlugin, - createRoutableExtension, - fetchApiRef, -} from '@backstage/core-plugin-api'; - -import { lightspeedApiRef } from './api/api'; -import { LightspeedApiClient } from './api/LightspeedApiClient'; -import { rootRouteRef } from './routes'; - -export const lightspeedPlugin = createPlugin({ - id: 'lightspeed', - routes: { - root: rootRouteRef, - }, - apis: [ - createApiFactory({ - api: lightspeedApiRef, - deps: { - configApi: configApiRef, - fetchApi: fetchApiRef, - }, - factory: ({ configApi, fetchApi }) => - new LightspeedApiClient({ configApi, fetchApi }), - }), - ], -}); - -export const LightspeedPage = lightspeedPlugin.provide( - createRoutableExtension({ - name: 'LightspeedPage', - component: () => - import('./components/LightspeedPage').then(m => m.LightspeedPage), - mountPoint: rootRouteRef, - }), -); diff --git a/plugins/lightspeed/src/routes.ts b/plugins/lightspeed/src/routes.ts deleted file mode 100644 index 06742ac2dd..0000000000 --- a/plugins/lightspeed/src/routes.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createRouteRef } from '@backstage/core-plugin-api'; - -export const rootRouteRef = createRouteRef({ - id: 'lightspeed', -}); diff --git a/plugins/lightspeed/src/setupTests.ts b/plugins/lightspeed/src/setupTests.ts deleted file mode 100644 index ff4b2f5cc1..0000000000 --- a/plugins/lightspeed/src/setupTests.ts +++ /dev/null @@ -1,13 +0,0 @@ -import '@testing-library/jest-dom'; - -// eslint-disable-next-line no-restricted-imports -import { TextDecoder, TextEncoder } from 'util'; - -// Also used in browser-based APIs for hashing. -Object.defineProperty(global.self, 'TextEncoder', { - value: TextEncoder, -}); - -Object.defineProperty(global.self, 'TextDecoder', { - value: TextDecoder, -}); diff --git a/plugins/lightspeed/src/types.ts b/plugins/lightspeed/src/types.ts deleted file mode 100644 index 21027ad0ce..0000000000 --- a/plugins/lightspeed/src/types.ts +++ /dev/null @@ -1,34 +0,0 @@ -export type Conversations = { - [key: string]: { - user: string; - bot: string; - model: string; - loading: boolean; - timestamp: string; - botTimestamp: string; - }; -}; - -export interface BaseMessage { - lc: number; - type: string; - id: string[]; - kwargs: { - content: string; - response_metadata: { - model?: string; - created_at: number; - role: string; - }; - additional_kwargs: { - [_key: string]: any; - }; - }; -} -export type ConversationSummary = { - conversation_id: string; - lastMessageTimestamp: number; - summary: string; -}; - -export type ConversationList = ConversationSummary[]; diff --git a/plugins/lightspeed/src/utils/__tests__/lightspeed-chatbot-utils.test.ts b/plugins/lightspeed/src/utils/__tests__/lightspeed-chatbot-utils.test.ts deleted file mode 100644 index 60723a86ba..0000000000 --- a/plugins/lightspeed/src/utils/__tests__/lightspeed-chatbot-utils.test.ts +++ /dev/null @@ -1,284 +0,0 @@ -import { ConversationList, ConversationSummary } from '../../types'; -import { - createBotMessage, - createMessage, - createUserMessage, - getCategorizeMessages, - getMessageData, - getTimestamp, - getTimestampVariablesString, - splitJsonStrings, -} from '../lightspeed-chatbox-utils'; - -describe('getTimestampVariablesString', () => { - it('should add a leading zero if the number is less than 10', () => { - expect(getTimestampVariablesString(5)).toBe('05'); - }); - - it('should return the number as a string if it is 10 or greater', () => { - expect(getTimestampVariablesString(10)).toBe('10'); - expect(getTimestampVariablesString(23)).toBe('23'); - }); -}); - -describe('getTimestamp', () => { - it('should format a given timestamp correctly', () => { - const unixTimestamp = 1609459200000; // 01 Jan 2021 00:00:00 UTC - const result = getTimestamp(unixTimestamp); - expect(result).toBe('01/01/2021, 00:00:00'); - }); - - it('should handle single-digit day, month, hour, minute, and second', () => { - const unixTimestamp = new Date(2024, 0, 5, 3, 7, 9).getTime(); // 05 Jan 2024 03:07:09 - const result = getTimestamp(unixTimestamp); - expect(result).toBe('05/01/2024, 03:07:09'); - }); - - it('should handle end-of-year timestamps correctly', () => { - const unixTimestamp = new Date(2024, 11, 31, 23, 59, 59).getTime(); // 31 Dec 2024 23:59:59 - const result = getTimestamp(unixTimestamp); - expect(result).toBe('31/12/2024, 23:59:59'); - }); - - it('should handle the beginning of the epoch (0 timestamp)', () => { - const unixTimestamp = 0; // 01 Jan 1970 00:00:00 UTC - const result = getTimestamp(unixTimestamp); - expect(result).toBe('01/01/1970, 00:00:00'); - }); - - it('should handle timestamps with daylight saving time shifts', () => { - const unixTimestamp = new Date(2024, 2, 14, 2, 30, 0).getTime(); // 14 Mar 2024 02:30:00 (DST transition for some regions) - const result = getTimestamp(unixTimestamp); - expect(result).toBe('14/03/2024, 02:30:00'); - }); -}); - -describe('splitJsonStrings', () => { - it('should return the entire string in an array if no `}{` pattern is found', () => { - const jsonString = '{"key1":"value1","key2":"value2"}'; - const result = splitJsonStrings(jsonString); - expect(result).toEqual([jsonString]); - }); - - it('should split a concatenated JSON string into individual JSON strings', () => { - const jsonString = '{"key1":"value1"}{"key2":"value2"}{"key3":"value3"}'; - const result = splitJsonStrings(jsonString); - expect(result).toEqual([ - '{"key1":"value1"}', - '{"key2":"value2"}', - '{"key3":"value3"}', - ]); - }); - - it('should handle a JSON string with multiple concatenated objects correctly', () => { - const jsonString = - '{"key1":"value1"}{"key2":"value2"}{"key3":"value3"}{"key4":"value4"}'; - const result = splitJsonStrings(jsonString); - expect(result).toEqual([ - '{"key1":"value1"}', - '{"key2":"value2"}', - '{"key3":"value3"}', - '{"key4":"value4"}', - ]); - }); - - it('should handle a JSON string with edge case of empty objects', () => { - const jsonString = '{}{}'; - const result = splitJsonStrings(jsonString); - expect(result).toEqual(['{}', '{}']); - }); - - it('should handle a JSON string with nested braces correctly', () => { - const jsonString = '{"key1":{"subKey":"subValue"}}{"key2":"value2"}'; - const result = splitJsonStrings(jsonString); - expect(result).toEqual([ - '{"key1":{"subKey":"subValue"}}', - '{"key2":"value2"}', - ]); - }); -}); - -describe('createMessage', () => { - it('should create a user message with default values', () => { - const message = createMessage({ - role: 'user', - content: 'Hello', - timestamp: '2024-10-30T10:00:00Z', - }); - expect(message).toEqual({ - role: 'user', - name: 'Guest', - avatar: undefined, - isLoading: false, - content: 'Hello', - timestamp: '2024-10-30T10:00:00Z', - }); - }); - - it('should create a bot message with custom values', () => { - const message = createMessage({ - role: 'bot', - name: 'Bot', - avatar: 'bot-avatar.png', - isLoading: true, - content: 'Hello from bot', - timestamp: '2024-10-30T11:00:00Z', - }); - expect(message).toEqual({ - role: 'bot', - name: 'Bot', - avatar: 'bot-avatar.png', - isLoading: true, - content: 'Hello from bot', - timestamp: '2024-10-30T11:00:00Z', - }); - }); -}); - -describe('createUserMessage', () => { - it('should create a user message with default name if name is not provided', () => { - const message = createUserMessage({ - content: 'User message', - timestamp: '2024-10-30T12:00:00Z', - }); - expect(message).toEqual({ - role: 'user', - name: 'Guest', - avatar: undefined, - isLoading: false, - content: 'User message', - timestamp: '2024-10-30T12:00:00Z', - }); - }); - - it('should create a user message with provided name', () => { - const message = createUserMessage({ - name: 'John', - avatar: 'alice-avatar.png', - content: 'Hello, this is John', - timestamp: '2024-10-30T13:00:00Z', - }); - expect(message).toEqual({ - role: 'user', - name: 'John', - avatar: 'alice-avatar.png', - isLoading: false, - content: 'Hello, this is John', - timestamp: '2024-10-30T13:00:00Z', - }); - }); -}); - -describe('createBotMessage', () => { - it('should create a bot message with provided properties', () => { - const message = createBotMessage({ - name: 'BotMaster', - avatar: 'bot-avatar.png', - content: 'Bot message content', - timestamp: '2024-10-30T14:00:00Z', - isLoading: true, - }); - expect(message).toEqual({ - role: 'bot', - name: 'BotMaster', - avatar: 'bot-avatar.png', - isLoading: true, - content: 'Bot message content', - timestamp: '2024-10-30T14:00:00Z', - }); - }); -}); - -describe('getMessageData', () => { - it('should return content and timestamp from message.kwargs', () => { - const message = { - kwargs: { - content: 'This is the message content', - response_metadata: { - created_at: 1730121598867, - }, - }, - }; - const result = getMessageData(message as any); - expect(result).toEqual({ - content: 'This is the message content', - timestamp: '28/10/2024, 13:19:58', - }); - }); - - it('should handle missing kwargs properties gracefully', () => { - const message = {}; - const result = getMessageData(message as any); - expect(result).toEqual({ - content: '', - timestamp: '', - }); - }); -}); - -describe('getCategorizeMessages', () => { - const addProps = (c: ConversationSummary) => ({ - customProp: `prop-${c.conversation_id}`, - }); - - const messages: ConversationList = [ - { - conversation_id: '1', - lastMessageTimestamp: new Date().toISOString(), - summary: 'Today message', - }, - { - conversation_id: '2', - lastMessageTimestamp: new Date( - Date.now() - 24 * 60 * 60 * 1000, - ).toISOString() as any, - summary: 'Yesterday message', - }, - { - conversation_id: '3', - lastMessageTimestamp: new Date( - Date.now() - 5 * 24 * 60 * 60 * 1000, - ).toISOString(), - summary: '5 days ago', - }, - { - conversation_id: '4', - lastMessageTimestamp: new Date( - Date.now() - 15 * 24 * 60 * 60 * 1000, - ).toISOString(), - summary: '15 days ago', - }, - { - conversation_id: '5', - lastMessageTimestamp: new Date( - Date.now() - 45 * 24 * 60 * 60 * 1000, - ).toISOString(), - summary: '45 days ago', - }, - ]; - - it('categorizes messages correctly', () => { - const result = getCategorizeMessages(messages, addProps); - - expect(result.Today).toHaveLength(1); - expect(result.Today[0].text).toBe('Today message'); - - expect(result.Yesterday).toHaveLength(1); - expect(result.Yesterday[0].text).toBe('Yesterday message'); - - expect(result['Previous 7 Days']).toHaveLength(1); - expect(result['Previous 7 Days'][0].text).toBe('5 days ago'); - - expect(result['Previous 30 Days']).toHaveLength(1); - expect(result['Previous 30 Days'][0].text).toBe('15 days ago'); - - const monthYearKey = new Date( - messages[4].lastMessageTimestamp, - ).toLocaleString('default', { - month: 'long', - year: 'numeric', - }); - expect(result[monthYearKey]).toHaveLength(1); - expect(result[monthYearKey][0].text).toBe('45 days ago'); - }); -}); diff --git a/plugins/lightspeed/src/utils/lightspeed-chatbox-utils.ts b/plugins/lightspeed/src/utils/lightspeed-chatbox-utils.ts deleted file mode 100644 index 8daa5db18a..0000000000 --- a/plugins/lightspeed/src/utils/lightspeed-chatbox-utils.ts +++ /dev/null @@ -1,182 +0,0 @@ -import { Conversation } from '@patternfly/virtual-assistant'; - -import { BaseMessage, ConversationList, ConversationSummary } from '../types'; - -export const getFootnoteProps = () => ({ - label: 'Lightspeed uses AI. Check for mistakes.', - popover: { - title: 'Verify accuracy', - description: `While Lightspeed strives for accuracy, there's always a possibility of errors. It's a good practice to verify critical information from reliable sources, especially if it's crucial for decision-making or actions.`, - bannerImage: { - src: 'https://cdn.dribbble.com/userupload/10651749/file/original-8a07b8e39d9e8bf002358c66fce1223e.gif', - alt: 'Example image for footnote popover', - }, - cta: { - label: 'Got it', - onClick: () => {}, - }, - link: { - label: 'Learn more', - url: 'https://www.redhat.com/', - }, - }, -}); - -export const getTimestampVariablesString = (v: number) => { - if (v < 10) { - return `0${v}`; - } - return `${v}`; -}; - -export const getTimestamp = (unix_timestamp: number) => { - if (typeof unix_timestamp !== 'number' || isNaN(unix_timestamp)) { - // eslint-disable-next-line no-console - console.error('Invalid Unix timestamp provided'); - return ''; - } - - const a = new Date(unix_timestamp); - const month = getTimestampVariablesString(a.getMonth() + 1); - const year = a.getFullYear(); - const date = getTimestampVariablesString(a.getDate()); - const hour = getTimestampVariablesString(a.getHours()); - const min = getTimestampVariablesString(a.getMinutes()); - const sec = getTimestampVariablesString(a.getSeconds()); - const time = `${date}/${month}/${year}, ${hour}:${min}:${sec}`; - return time; -}; - -export const splitJsonStrings = (jsonString: string): string[] => { - const chunks = jsonString.split('}{'); - - if (chunks.length <= 1) { - return [jsonString]; - } - - return chunks.map((chunk, index, arr) => { - if (index === 0) { - return `${chunk}}`; - } else if (index === arr.length - 1) { - return `{${chunk}`; - } - return `{${chunk}}`; - }); -}; - -type MessageProps = { - content: string; - timestamp: string; - name?: string; - avatar?: string | any; - isLoading?: boolean; -}; - -export const createMessage = ({ - role, - name = 'Guest', - avatar, - isLoading = false, - content, - timestamp, -}: MessageProps & { role: 'user' | 'bot' }) => ({ - role, - name, - avatar, - isLoading, - content, - timestamp, -}); - -export const createUserMessage = (props: MessageProps) => - createMessage({ - ...props, - role: 'user', - name: props.name ?? 'Guest', - }); - -export const createBotMessage = (props: MessageProps) => - createMessage({ - ...props, - role: 'bot', - }); - -export const getMessageData = (message: BaseMessage) => { - return { - model: message?.kwargs?.response_metadata?.model, - content: message?.kwargs?.content || '', - timestamp: getTimestamp(message?.kwargs?.response_metadata?.created_at), - }; -}; - -export const getDayDifference = (sourceTime: number, targetTime: number) => { - const sourceDate = new Date(sourceTime); - const targetDate = new Date(targetTime); - - sourceDate.setHours(0, 0, 0, 0); - targetDate.setHours(0, 0, 0, 0); - - const timeDifference = sourceDate.getTime() - targetDate.getTime(); - - return Math.floor(timeDifference / (1000 * 60 * 60 * 24)); -}; - -export const getCategorizeMessages = ( - messages: ConversationList, - addProps: (c: ConversationSummary) => { [k: string]: any }, -): { [k: string]: Conversation[] } => { - const now: any = new Date(); - const today = now.toDateString(); - - const categorizedMessages: { [k: string]: Conversation[] } = { - Today: [], - Yesterday: [], - 'Previous 7 Days': [], - 'Previous 30 Days': [], - }; - - messages.forEach(c => { - const messageDate = new Date(c.lastMessageTimestamp); - const messageDayString = messageDate.toDateString(); - const dayDifference = getDayDifference(now, c.lastMessageTimestamp); - - const message: Conversation = { - id: c.conversation_id, - text: c.summary, - label: 'Options', - ...addProps(c), - }; - - if (messageDayString === today) { - categorizedMessages.Today.push(message); - } else if (dayDifference === 1) { - categorizedMessages.Yesterday.push(message); - } else if (dayDifference <= 7) { - categorizedMessages['Previous 7 Days'].push(message); - } else if (dayDifference <= 30) { - categorizedMessages['Previous 30 Days'].push(message); - } else { - // handle month-wise grouping - const monthYear = messageDate.toLocaleString('default', { - month: 'long', - year: 'numeric', - }); - if (!categorizedMessages[monthYear]) { - categorizedMessages[monthYear] = []; - } - categorizedMessages[monthYear].push(message); - } - }); - - const filteredCategories = Object.keys(categorizedMessages).reduce( - (result, category) => { - if (categorizedMessages[category].length > 0) { - result[category] = categorizedMessages[category]; - } - return result; - }, - {} as any, - ); - - return filteredCategories; -}; diff --git a/plugins/lightspeed/src/utils/queryClient.ts b/plugins/lightspeed/src/utils/queryClient.ts deleted file mode 100644 index 4d69c83eda..0000000000 --- a/plugins/lightspeed/src/utils/queryClient.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { QueryClient } from '@tanstack/react-query'; - -const queryClient = new QueryClient(); -export default queryClient; diff --git a/plugins/lightspeed/tsconfig.json b/plugins/lightspeed/tsconfig.json deleted file mode 100644 index fc57735d89..0000000000 --- a/plugins/lightspeed/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "extends": "@backstage/cli/config/tsconfig.json", - "include": ["src", "dev"], - "exclude": ["node_modules"], - "compilerOptions": { - "outDir": "../../dist-types/plugins/lightspeed", - "rootDir": "." - } -} diff --git a/plugins/lightspeed/turbo.json b/plugins/lightspeed/turbo.json deleted file mode 100644 index bcc47a1851..0000000000 --- a/plugins/lightspeed/turbo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "tsc": { - "outputs": ["../../dist-types/plugins/lightspeed/**"] - } - } -} diff --git a/yarn.lock b/yarn.lock index ec9573b453..ed55db817b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5561,34 +5561,6 @@ __metadata: languageName: node linkType: hard -"@bundled-es-modules/cookie@npm:^2.0.0": - version: 2.0.0 - resolution: "@bundled-es-modules/cookie@npm:2.0.0" - dependencies: - cookie: ^0.5.0 - checksum: 53114eabbedda20ba6c63f45dcea35c568616d22adf5d1882cef9761f65ae636bf47e0c66325572cc8e3a335e0257caf5f76ff1287990d9e9265be7bc9767a87 - languageName: node - linkType: hard - -"@bundled-es-modules/statuses@npm:^1.0.1": - version: 1.0.1 - resolution: "@bundled-es-modules/statuses@npm:1.0.1" - dependencies: - statuses: ^2.0.1 - checksum: bcaa7de192e73056950b5fd20e75140d8d09074b1adc4437924b2051bb02b4dbf568c96e67d53b220fb7d735c3446e2ba746599cb1793ab2d23dd2ef230a8622 - languageName: node - linkType: hard - -"@bundled-es-modules/tough-cookie@npm:^0.1.6": - version: 0.1.6 - resolution: "@bundled-es-modules/tough-cookie@npm:0.1.6" - dependencies: - "@types/tough-cookie": ^4.0.5 - tough-cookie: ^4.1.4 - checksum: e31c1262cbc044373e757117b1b152acc86ba5d088124153b3d1ae83e0de0a2b4d2362758cec3e1a49cf15c39a4447587cc2672e4f5a961754c91ef9ca3221e1 - languageName: node - linkType: hard - "@changesets/apply-release-plan@npm:^7.0.5": version: 7.0.5 resolution: "@changesets/apply-release-plan@npm:7.0.5" @@ -6051,15 +6023,6 @@ __metadata: languageName: node linkType: hard -"@emotion/is-prop-valid@npm:^0.7.3": - version: 0.7.3 - resolution: "@emotion/is-prop-valid@npm:0.7.3" - dependencies: - "@emotion/memoize": 0.7.1 - checksum: 76c2cb5043b0a81dd5c1a8d76baa7c273e9cb5d177efaa482406b0e170ca2ce4f9274f299769e5d5489b319ba2fd94dfd85b912752242c23b159098606da68a9 - languageName: node - linkType: hard - "@emotion/is-prop-valid@npm:^0.8.2": version: 0.8.8 resolution: "@emotion/is-prop-valid@npm:0.8.8" @@ -6078,13 +6041,6 @@ __metadata: languageName: node linkType: hard -"@emotion/memoize@npm:0.7.1": - version: 0.7.1 - resolution: "@emotion/memoize@npm:0.7.1" - checksum: fec25e74c3a4af920bfdb0f552c16f648c8f4343d51cb073af85fcec1a382ce041a4e082f458a999dc3599e9d768c0dd28e5accd6066169e01364b270b7036cf - languageName: node - linkType: hard - "@emotion/memoize@npm:0.7.4": version: 0.7.4 resolution: "@emotion/memoize@npm:0.7.4" @@ -7741,61 +7697,6 @@ __metadata: languageName: node linkType: hard -"@inquirer/confirm@npm:^3.0.0": - version: 3.2.0 - resolution: "@inquirer/confirm@npm:3.2.0" - dependencies: - "@inquirer/core": ^9.1.0 - "@inquirer/type": ^1.5.3 - checksum: 6b032a26c64075dc14769558720b17f09bc6784a223bbf2c85ec42e491be6ce4c4b83518433c47e05d7e8836ba680ab1b2f6b9c553410d4326582308a1fd2259 - languageName: node - linkType: hard - -"@inquirer/core@npm:^9.1.0": - version: 9.2.1 - resolution: "@inquirer/core@npm:9.2.1" - dependencies: - "@inquirer/figures": ^1.0.6 - "@inquirer/type": ^2.0.0 - "@types/mute-stream": ^0.0.4 - "@types/node": ^22.5.5 - "@types/wrap-ansi": ^3.0.0 - ansi-escapes: ^4.3.2 - cli-width: ^4.1.0 - mute-stream: ^1.0.0 - signal-exit: ^4.1.0 - strip-ansi: ^6.0.1 - wrap-ansi: ^6.2.0 - yoctocolors-cjs: ^2.1.2 - checksum: 681339aac03b6998e7fee1c5a0a15951e9268b7be20fd7532f180b408893f9e7203020a57d40dc78352d281f19b66222df83c68f73a125f6e1beadfa0df3920c - languageName: node - linkType: hard - -"@inquirer/figures@npm:^1.0.6": - version: 1.0.7 - resolution: "@inquirer/figures@npm:1.0.7" - checksum: 82edc998d0ace2f147eb332177f451c02e6a4a6e829d47817f5a4b3341c12cd0850b92ee3187d483328cce5824b870ed75e868850b6ac819447b9d56501f01cb - languageName: node - linkType: hard - -"@inquirer/type@npm:^1.5.3": - version: 1.5.5 - resolution: "@inquirer/type@npm:1.5.5" - dependencies: - mute-stream: ^1.0.0 - checksum: 6cada82bb14519f3c71f455b08dc03c1064046fe0469aa5fce44c7ebf88a3a3d67a0cf852b0a7339476fec3b02874167f46d2c5b0964218d00273ab27ff861c5 - languageName: node - linkType: hard - -"@inquirer/type@npm:^2.0.0": - version: 2.0.0 - resolution: "@inquirer/type@npm:2.0.0" - dependencies: - mute-stream: ^1.0.0 - checksum: 0f78f84c182ef3879109a651c7b4afdc7480bc5365948cbdad7e0aa5b9713ef052761b6914b8c76c3ffeef5a8f61ef1d6d44c6c3914800e17506ecb1a8e99844 - languageName: node - linkType: hard - "@ioredis/commands@npm:^1.1.1": version: 1.2.0 resolution: "@ioredis/commands@npm:1.2.0" @@ -7967,59 +7868,6 @@ __metadata: languageName: unknown linkType: soft -"@janus-idp/backstage-plugin-lightspeed-backend@workspace:plugins/lightspeed-backend": - version: 0.0.0-use.local - resolution: "@janus-idp/backstage-plugin-lightspeed-backend@workspace:plugins/lightspeed-backend" - dependencies: - "@backstage/backend-defaults": ^0.5.2 - "@backstage/backend-plugin-api": ^1.0.1 - "@backstage/backend-test-utils": 1.0.2 - "@backstage/cli": 0.28.2 - "@backstage/config": 1.2.0 - "@langchain/core": ^0.2.30 - "@langchain/openai": ^0.2.8 - "@types/express": 4.17.21 - "@types/supertest": 2.0.16 - express: ^4.18.2 - http-proxy-middleware: ^3.0.2 - msw: 2.4.0 - prettier: 3.3.3 - supertest: 6.3.4 - languageName: unknown - linkType: soft - -"@janus-idp/backstage-plugin-lightspeed@workspace:plugins/lightspeed": - version: 0.0.0-use.local - resolution: "@janus-idp/backstage-plugin-lightspeed@workspace:plugins/lightspeed" - dependencies: - "@backstage/cli": 0.28.2 - "@backstage/core-app-api": 1.15.1 - "@backstage/core-components": ^0.15.1 - "@backstage/core-plugin-api": ^1.10.0 - "@backstage/dev-utils": 1.1.2 - "@backstage/test-utils": 1.7.0 - "@backstage/theme": ^0.6.0 - "@material-ui/core": ^4.9.13 - "@material-ui/icons": ^4.11.3 - "@material-ui/lab": ^4.0.0-alpha.61 - "@mui/icons-material": ^5.15.18 - "@patternfly/react-core": 6.0.0-prerelease.21 - "@patternfly/virtual-assistant": 2.0.0-alpha.61 - "@tanstack/react-query": ^5.59.15 - "@testing-library/jest-dom": 6.4.8 - "@testing-library/react": ^16.0.1 - "@testing-library/react-hooks": 8.0.1 - "@testing-library/user-event": 14.5.2 - msw: 1.3.3 - openai: ^4.52.6 - prettier: 3.3.3 - react-markdown: ^9.0.1 - react-use: ^17.2.4 - peerDependencies: - react: 16.13.1 || ^17.0.0 || ^18.0.0 - languageName: unknown - linkType: soft - "@janus-idp/backstage-plugin-orchestrator-backend@workspace:plugins/orchestrator-backend": version: 0.0.0-use.local resolution: "@janus-idp/backstage-plugin-orchestrator-backend@workspace:plugins/orchestrator-backend" @@ -9323,38 +9171,6 @@ __metadata: languageName: node linkType: hard -"@langchain/core@npm:>=0.2.26 <0.3.0, @langchain/core@npm:^0.2.30": - version: 0.2.36 - resolution: "@langchain/core@npm:0.2.36" - dependencies: - ansi-styles: ^5.0.0 - camelcase: 6 - decamelize: 1.2.0 - js-tiktoken: ^1.0.12 - langsmith: ^0.1.56-rc.1 - mustache: ^4.2.0 - p-queue: ^6.6.2 - p-retry: 4 - uuid: ^10.0.0 - zod: ^3.22.4 - zod-to-json-schema: ^3.22.3 - checksum: 963aca44a8422dbbe8259d063e65867bc1ad0735e805c7b46048d48e12822ae669f64f46f077a31dd5a9aa0c43b2f1ed7a5e57369392902e8d419dd9fa3611f3 - languageName: node - linkType: hard - -"@langchain/openai@npm:^0.2.8": - version: 0.2.11 - resolution: "@langchain/openai@npm:0.2.11" - dependencies: - "@langchain/core": ">=0.2.26 <0.3.0" - js-tiktoken: ^1.0.12 - openai: ^4.57.3 - zod: ^3.22.4 - zod-to-json-schema: ^3.22.3 - checksum: afd192901ff5996008f5d2988ceba09847701b9832503ca0b75c4ba919d3df1be547571c78c8e024aef29ba592e4d4c6f7e16bcb487f21077667ec311da7cd55 - languageName: node - linkType: hard - "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.5 resolution: "@leichtgewicht/ip-codec@npm:2.0.5" @@ -9928,20 +9744,6 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.29.0": - version: 0.29.1 - resolution: "@mswjs/interceptors@npm:0.29.1" - dependencies: - "@open-draft/deferred-promise": ^2.2.0 - "@open-draft/logger": ^0.3.0 - "@open-draft/until": ^2.0.0 - is-node-process: ^1.2.0 - outvariant: ^1.2.1 - strict-event-emitter: ^0.5.1 - checksum: c217f922c68024f6a8b526fb7df00bbfccb71e432bfb270322976dd40a9d312698e40bfd105b74df7aeb5a46276531a56ca5b8e3e9b0112f1577eb0d8d289e1f - languageName: node - linkType: hard - "@mui/base@npm:5.0.0-beta.40": version: 5.0.0-beta.40 resolution: "@mui/base@npm:5.0.0-beta.40" @@ -10003,7 +9805,7 @@ __metadata: languageName: node linkType: hard -"@mui/icons-material@npm:^5.15.18, @mui/icons-material@npm:^5.15.8": +"@mui/icons-material@npm:^5.15.8": version: 5.16.7 resolution: "@mui/icons-material@npm:5.16.7" dependencies: @@ -11000,23 +10802,6 @@ __metadata: languageName: node linkType: hard -"@open-draft/deferred-promise@npm:^2.2.0": - version: 2.2.0 - resolution: "@open-draft/deferred-promise@npm:2.2.0" - checksum: 7f29d39725bb8ab5b62f89d88a4202ce2439ac740860979f9e3d0015dfe4bc3daddcfa5727fa4eed482fdbee770aa591b1136b98b0a0f0569a65294f35bdf56a - languageName: node - linkType: hard - -"@open-draft/logger@npm:^0.3.0": - version: 0.3.0 - resolution: "@open-draft/logger@npm:0.3.0" - dependencies: - is-node-process: ^1.2.0 - outvariant: ^1.4.0 - checksum: 7adfe3d0ed8ca32333ce2a77f9a93d561ebc89c989eaa9722f1dc8a2d2854f5de1bef6fa6894cdf58e16fa4dd9cfa99444ea1f5cac6eb1518e9247911ed042d5 - languageName: node - linkType: hard - "@open-draft/until@npm:^1.0.3": version: 1.0.3 resolution: "@open-draft/until@npm:1.0.3" @@ -11024,13 +10809,6 @@ __metadata: languageName: node linkType: hard -"@open-draft/until@npm:^2.0.0, @open-draft/until@npm:^2.1.0": - version: 2.1.0 - resolution: "@open-draft/until@npm:2.1.0" - checksum: 140ea3b16f4a3a6a729c1256050e20a93d408d7aa1e125648ce2665b3c526ed452510c6e4a6f4b15d95fb5e41203fb51510eb8fbc8812d5e5a91880293d66471 - languageName: node - linkType: hard - "@openapi-contrib/openapi-schema-to-json-schema@npm:~3.2.0": version: 3.2.0 resolution: "@openapi-contrib/openapi-schema-to-json-schema@npm:3.2.0" @@ -11127,40 +10905,6 @@ __metadata: languageName: node linkType: hard -"@patternfly/react-code-editor@npm:6.0.0-prerelease.21": - version: 6.0.0-prerelease.21 - resolution: "@patternfly/react-code-editor@npm:6.0.0-prerelease.21" - dependencies: - "@monaco-editor/react": ^4.6.0 - "@patternfly/react-core": ^6.0.0-prerelease.21 - "@patternfly/react-icons": ^6.0.0-prerelease.7 - "@patternfly/react-styles": ^6.0.0-prerelease.6 - react-dropzone: 14.2.3 - tslib: ^2.7.0 - peerDependencies: - react: ^17 || ^18 - react-dom: ^17 || ^18 - checksum: 815b5601da46f219ca5fa35791743b5c8b31a0e99e4aea0371501fe4494d11fb7c3e010b608430965015a8de8e098be959f7c8b4f82900617c3cf3576382a8d7 - languageName: node - linkType: hard - -"@patternfly/react-core@npm:6.0.0-prerelease.21, @patternfly/react-core@npm:^6.0.0-prerelease.21": - version: 6.0.0-prerelease.21 - resolution: "@patternfly/react-core@npm:6.0.0-prerelease.21" - dependencies: - "@patternfly/react-icons": ^6.0.0-prerelease.7 - "@patternfly/react-styles": ^6.0.0-prerelease.6 - "@patternfly/react-tokens": ^6.0.0-prerelease.7 - focus-trap: 7.6.0 - react-dropzone: ^14.2.3 - tslib: ^2.7.0 - peerDependencies: - react: ^17 || ^18 - react-dom: ^17 || ^18 - checksum: 4fd63e89fb13c109421f66d074666dc3645f1bb9076c55b5cc06cc3611f166eea82a78f1bcc8c12f4918898db0602194fce32424443b7b15d53da5f5e9e98adf - languageName: node - linkType: hard - "@patternfly/react-core@npm:^4.276.6": version: 4.278.1 resolution: "@patternfly/react-core@npm:4.278.1" @@ -11196,16 +10940,6 @@ __metadata: languageName: node linkType: hard -"@patternfly/react-icons@npm:6.0.0-prerelease.7, @patternfly/react-icons@npm:^6.0.0-prerelease.7": - version: 6.0.0-prerelease.7 - resolution: "@patternfly/react-icons@npm:6.0.0-prerelease.7" - peerDependencies: - react: ^17 || ^18 - react-dom: ^17 || ^18 - checksum: 7723195c064d5e8a55bd8012295330d99bf9dee2c8ddaf3d5561e4a479dd599b3cb89c88aac725eb7720cea5e17eda11163475c14606a18533e40d80795ba6c4 - languageName: node - linkType: hard - "@patternfly/react-icons@npm:^4.93.6, @patternfly/react-icons@npm:^4.93.7": version: 4.93.7 resolution: "@patternfly/react-icons@npm:4.93.7" @@ -11240,13 +10974,6 @@ __metadata: languageName: node linkType: hard -"@patternfly/react-styles@npm:^6.0.0-prerelease.6": - version: 6.0.0-prerelease.6 - resolution: "@patternfly/react-styles@npm:6.0.0-prerelease.6" - checksum: 6a4455dbd15d9137f6d756391b2d1e6d4e804f5640a7d2c8e82bdf1e61cc5e66e4e6091a9013e1cc85b65d46d2712a8079aea8571f55b6fbf96d35485d8f5a74 - languageName: node - linkType: hard - "@patternfly/react-tokens@npm:^4.94.7": version: 4.94.7 resolution: "@patternfly/react-tokens@npm:4.94.7" @@ -11261,13 +10988,6 @@ __metadata: languageName: node linkType: hard -"@patternfly/react-tokens@npm:^6.0.0-prerelease.7": - version: 6.0.0-prerelease.7 - resolution: "@patternfly/react-tokens@npm:6.0.0-prerelease.7" - checksum: 1609afb85c8c607d922ef08c811f25bc6b9300fe7bf1da27789985709d5b9e1a9e043d7a2990c3092e2c10dac823419814d1b5a2d52667cfe8686a239f3a359b - languageName: node - linkType: hard - "@patternfly/react-topology@npm:5.3.0": version: 5.3.0 resolution: "@patternfly/react-topology@npm:5.3.0" @@ -11295,28 +11015,6 @@ __metadata: languageName: node linkType: hard -"@patternfly/virtual-assistant@npm:2.0.0-alpha.61": - version: 2.0.0-alpha.61 - resolution: "@patternfly/virtual-assistant@npm:2.0.0-alpha.61" - dependencies: - "@patternfly/react-code-editor": 6.0.0-prerelease.21 - "@patternfly/react-core": 6.0.0-prerelease.21 - "@patternfly/react-icons": 6.0.0-prerelease.7 - clsx: ^2.1.0 - framer-motion: ^11.3.28 - path-browserify: ^1.0.1 - react-jss: ^10.10.0 - react-markdown: ^9.0.1 - react-syntax-highlighter: ^15.5.0 - react-textarea-auto-witdth-height: ^1.0.3 - remark-gfm: ^4.0.0 - peerDependencies: - react: ^17 || ^18 - react-dom: ^17 || ^18 - checksum: a23503b9db6c0556efb4d0b7234dbb868e24f0c171f81c855eac01e0643896829795075b01376e0650958c22a45368a82dea8b7c9b93346d1e9d720199b4dbc7 - languageName: node - linkType: hard - "@pkgjs/parseargs@npm:^0.11.0": version: 0.11.0 resolution: "@pkgjs/parseargs@npm:0.11.0" @@ -16073,24 +15771,6 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:5.59.16": - version: 5.59.16 - resolution: "@tanstack/query-core@npm:5.59.16" - checksum: d01fb27718985a69fd45df178496fc1afab25324aa355ce1e1fa2068bc8ffadba68954914a5ae886c7e92d6d7b998ceae9c7a2741081c320a2482a31a62fcfda - languageName: node - linkType: hard - -"@tanstack/react-query@npm:^5.59.15": - version: 5.59.16 - resolution: "@tanstack/react-query@npm:5.59.16" - dependencies: - "@tanstack/query-core": 5.59.16 - peerDependencies: - react: ^18 || ^19 - checksum: ad5d3579496cfd26593f4b93b0e412ceab2effc97cf6fc4f029b1e381eb3bb544ec850bb3116e511f12e45271aaeeb4f74201a69c97ad3afe1dec179f9a12759 - languageName: node - linkType: hard - "@tanstack/react-virtual@npm:^3.0.0-beta.60": version: 3.10.8 resolution: "@tanstack/react-virtual@npm:3.10.8" @@ -16142,28 +15822,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react-hooks@npm:8.0.1": - version: 8.0.1 - resolution: "@testing-library/react-hooks@npm:8.0.1" - dependencies: - "@babel/runtime": ^7.12.5 - react-error-boundary: ^3.1.0 - peerDependencies: - "@types/react": ^16.9.0 || ^17.0.0 - react: ^16.9.0 || ^17.0.0 - react-dom: ^16.9.0 || ^17.0.0 - react-test-renderer: ^16.9.0 || ^17.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - react-dom: - optional: true - react-test-renderer: - optional: true - checksum: 7fe44352e920deb5cb1876f80d64e48615232072c9d5382f1e0284b3aab46bb1c659a040b774c45cdf084a5257b8fe463f7e08695ad8480d8a15635d4d3d1f6d - languageName: node - linkType: hard - "@testing-library/react@npm:14.3.1": version: 14.3.1 resolution: "@testing-library/react@npm:14.3.1" @@ -16178,26 +15836,6 @@ __metadata: languageName: node linkType: hard -"@testing-library/react@npm:^16.0.1": - version: 16.0.1 - resolution: "@testing-library/react@npm:16.0.1" - dependencies: - "@babel/runtime": ^7.12.5 - peerDependencies: - "@testing-library/dom": ^10.0.0 - "@types/react": ^18.0.0 - "@types/react-dom": ^18.0.0 - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - "@types/react-dom": - optional: true - checksum: 1837db473ea018cf2b5d0cbfffb7a30d0d759e5a7f23aad431441c77bcc3d2533250cd003a61878fd908267df47404cedcb5914f12d79e413002c659652b37fd - languageName: node - linkType: hard - "@testing-library/user-event@npm:14.5.2, @testing-library/user-event@npm:^14.4.0": version: 14.5.2 resolution: "@testing-library/user-event@npm:14.5.2" @@ -16447,13 +16085,6 @@ __metadata: languageName: node linkType: hard -"@types/cookie@npm:^0.6.0": - version: 0.6.0 - resolution: "@types/cookie@npm:0.6.0" - checksum: 5edce7995775b0b196b142883e4d4f71fd93c294eaec973670f1fa2540b70ea7390408ed513ddefef5fcb12a578100c76596e8f2a714b0c2ae9f70ee773f4510 - languageName: node - linkType: hard - "@types/cookiejar@npm:^2.1.5": version: 2.1.5 resolution: "@types/cookiejar@npm:2.1.5" @@ -16858,15 +16489,6 @@ __metadata: languageName: node linkType: hard -"@types/estree-jsx@npm:^1.0.0": - version: 1.0.5 - resolution: "@types/estree-jsx@npm:1.0.5" - dependencies: - "@types/estree": "*" - checksum: a028ab0cd7b2950168a05c6a86026eb3a36a54a4adfae57f13911d7b49dffe573d9c2b28421b2d029b49b3d02fcd686611be2622dc3dad6d9791166c083f6008 - languageName: node - linkType: hard - "@types/estree@npm:*, @types/estree@npm:1.0.6, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5": version: 1.0.6 resolution: "@types/estree@npm:1.0.6" @@ -16997,15 +16619,6 @@ __metadata: languageName: node linkType: hard -"@types/hast@npm:^3.0.0": - version: 3.0.4 - resolution: "@types/hast@npm:3.0.4" - dependencies: - "@types/unist": "*" - checksum: 7a973e8d16fcdf3936090fa2280f408fb2b6a4f13b42edeb5fbd614efe042b82eac68e298e556d50f6b4ad585a3a93c353e9c826feccdc77af59de8dd400d044 - languageName: node - linkType: hard - "@types/hoist-non-react-statics@npm:^3.3.0": version: 3.3.5 resolution: "@types/hoist-non-react-statics@npm:3.3.5" @@ -17037,7 +16650,7 @@ __metadata: languageName: node linkType: hard -"@types/http-proxy@npm:^1.17.15, @types/http-proxy@npm:^1.17.8": +"@types/http-proxy@npm:^1.17.8": version: 1.17.15 resolution: "@types/http-proxy@npm:1.17.15" dependencies: @@ -17207,15 +16820,6 @@ __metadata: languageName: node linkType: hard -"@types/mdast@npm:^4.0.0": - version: 4.0.4 - resolution: "@types/mdast@npm:4.0.4" - dependencies: - "@types/unist": "*" - checksum: 20c4e9574cc409db662a35cba52b068b91eb696b3049e94321219d47d34c8ccc99a142be5c76c80a538b612457b03586bc2f6b727a3e9e7530f4c8568f6282ee - languageName: node - linkType: hard - "@types/mdx@npm:^2.0.0": version: 2.0.13 resolution: "@types/mdx@npm:2.0.13" @@ -17276,15 +16880,6 @@ __metadata: languageName: node linkType: hard -"@types/mute-stream@npm:^0.0.4": - version: 0.0.4 - resolution: "@types/mute-stream@npm:0.0.4" - dependencies: - "@types/node": "*" - checksum: af8d83ad7b68ea05d9357985daf81b6c9b73af4feacb2f5c2693c7fd3e13e5135ef1bd083ce8d5bdc8e97acd28563b61bb32dec4e4508a8067fcd31b8a098632 - languageName: node - linkType: hard - "@types/node-fetch@npm:^2.6.4": version: 2.6.11 resolution: "@types/node-fetch@npm:2.6.11" @@ -17304,7 +16899,7 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^22.0.0, @types/node@npm:^22.5.5": +"@types/node@npm:*, @types/node@npm:>=13.7.0, @types/node@npm:^22.0.0": version: 22.7.8 resolution: "@types/node@npm:22.7.8" dependencies: @@ -17711,13 +17306,6 @@ __metadata: languageName: node linkType: hard -"@types/statuses@npm:^2.0.4": - version: 2.0.5 - resolution: "@types/statuses@npm:2.0.5" - checksum: 3f2609f660b45a878c6782f2fb2cef9f08bbd4e89194bf7512e747b8a73b056839be1ad6f64b1353765528cd8a5e93adeffc471cde24d0d9f7b528264e7154e5 - languageName: node - linkType: hard - "@types/styled-jsx@npm:^2.2.8": version: 2.2.9 resolution: "@types/styled-jsx@npm:2.2.9" @@ -17757,7 +17345,7 @@ __metadata: languageName: node linkType: hard -"@types/tough-cookie@npm:*, @types/tough-cookie@npm:^4.0.5": +"@types/tough-cookie@npm:*": version: 4.0.5 resolution: "@types/tough-cookie@npm:4.0.5" checksum: f19409d0190b179331586365912920d192733112a195e870c7f18d20ac8adb7ad0b0ff69dad430dba8bc2be09593453a719cfea92dc3bda19748fd158fe1498d @@ -17778,13 +17366,6 @@ __metadata: languageName: node linkType: hard -"@types/unist@npm:*, @types/unist@npm:^3.0.0": - version: 3.0.3 - resolution: "@types/unist@npm:3.0.3" - checksum: 96e6453da9e075aaef1dc22482463898198acdc1eeb99b465e65e34303e2ec1e3b1ed4469a9118275ec284dc98019f63c3f5d49422f0e4ac707e5ab90fb3b71a - languageName: node - linkType: hard - "@types/unist@npm:^2, @types/unist@npm:^2.0.0": version: 2.0.11 resolution: "@types/unist@npm:2.0.11" @@ -17806,13 +17387,6 @@ __metadata: languageName: node linkType: hard -"@types/uuid@npm:^10.0.0": - version: 10.0.0 - resolution: "@types/uuid@npm:10.0.0" - checksum: e3958f8b0fe551c86c14431f5940c3470127293280830684154b91dc7eb3514aeb79fe3216968833cf79d4d1c67f580f054b5be2cd562bebf4f728913e73e944 - languageName: node - linkType: hard - "@types/uuid@npm:^9.0.1": version: 9.0.8 resolution: "@types/uuid@npm:9.0.8" @@ -17827,13 +17401,6 @@ __metadata: languageName: node linkType: hard -"@types/wrap-ansi@npm:^3.0.0": - version: 3.0.0 - resolution: "@types/wrap-ansi@npm:3.0.0" - checksum: 492f0610093b5802f45ca292777679bb9b381f1f32ae939956dd9e00bf81dba7cc99979687620a2817d9a7d8b59928207698166c47a0861c6a2e5c30d4aaf1e9 - languageName: node - linkType: hard - "@types/ws@npm:*, @types/ws@npm:^8.0.0, @types/ws@npm:^8.5.10, @types/ws@npm:^8.5.3, @types/ws@npm:^8.5.5": version: 8.5.12 resolution: "@types/ws@npm:8.5.12" @@ -18184,7 +17751,7 @@ __metadata: languageName: node linkType: hard -"@ungap/structured-clone@npm:^1.0.0, @ungap/structured-clone@npm:^1.2.0": +"@ungap/structured-clone@npm:^1.2.0": version: 1.2.0 resolution: "@ungap/structured-clone@npm:1.2.0" checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 @@ -18655,7 +18222,7 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.1.4, agentkeepalive@npm:^4.2.1": +"agentkeepalive@npm:^4.1.4": version: 4.5.0 resolution: "agentkeepalive@npm:4.5.0" dependencies: @@ -18783,7 +18350,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.2": +"ansi-escapes@npm:^4.2.1": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -20648,13 +20215,6 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:6, camelcase@npm:^6.2.0": - version: 6.3.0 - resolution: "camelcase@npm:6.3.0" - checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d - languageName: node - linkType: hard - "camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" @@ -20662,6 +20222,13 @@ __metadata: languageName: node linkType: hard +"camelcase@npm:^6.2.0": + version: 6.3.0 + resolution: "camelcase@npm:6.3.0" + checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d + languageName: node + linkType: hard + "caniuse-api@npm:^3.0.0": version: 3.0.0 resolution: "caniuse-api@npm:3.0.0" @@ -20760,13 +20327,6 @@ __metadata: languageName: node linkType: hard -"character-entities-html4@npm:^2.0.0": - version: 2.1.0 - resolution: "character-entities-html4@npm:2.1.0" - checksum: 7034aa7c7fa90309667f6dd50499c8a760c3d3a6fb159adb4e0bada0107d194551cdbad0714302f62d06ce4ed68565c8c2e15fdef2e8f8764eb63fa92b34b11d - languageName: node - linkType: hard - "character-entities-legacy@npm:^1.0.0": version: 1.1.4 resolution: "character-entities-legacy@npm:1.1.4" @@ -20774,13 +20334,6 @@ __metadata: languageName: node linkType: hard -"character-entities-legacy@npm:^3.0.0": - version: 3.0.0 - resolution: "character-entities-legacy@npm:3.0.0" - checksum: 7582af055cb488b626d364b7d7a4e46b06abd526fb63c0e4eb35bcb9c9799cc4f76b39f34fdccef2d1174ac95e53e9ab355aae83227c1a2505877893fce77731 - languageName: node - linkType: hard - "character-entities@npm:^1.0.0": version: 1.2.4 resolution: "character-entities@npm:1.2.4" @@ -20802,13 +20355,6 @@ __metadata: languageName: node linkType: hard -"character-reference-invalid@npm:^2.0.0": - version: 2.0.1 - resolution: "character-reference-invalid@npm:2.0.1" - checksum: 98d3b1a52ae510b7329e6ee7f6210df14f1e318c5415975d4c9e7ee0ef4c07875d47c6e74230c64551f12f556b4a8ccc24d9f3691a2aa197019e72a95e9297ee - languageName: node - linkType: hard - "chardet@npm:^0.7.0": version: 0.7.0 resolution: "chardet@npm:0.7.0" @@ -21031,13 +20577,6 @@ __metadata: languageName: node linkType: hard -"cli-width@npm:^4.1.0": - version: 4.1.0 - resolution: "cli-width@npm:4.1.0" - checksum: 0a79cff2dbf89ef530bcd54c713703ba94461457b11e5634bd024c78796ed21401e32349c004995954e06f442d82609287e7aabf6a5f02c919a1cf3b9b6854ff - languageName: node - linkType: hard - "client-only@npm:^0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" @@ -22132,17 +21671,6 @@ __metadata: languageName: node linkType: hard -"css-jss@npm:10.10.0": - version: 10.10.0 - resolution: "css-jss@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: ^10.10.0 - jss-preset-default: ^10.10.0 - checksum: 9b0e0251875f7f40dc3f15ceeae74b752719b7861f7b674974dcae9f47d1231d01ddcb36521d2ac3f8b70e12d5eb09b8b6b84fc808691bdb52bf553fba9c5554 - languageName: node - linkType: hard - "css-loader@npm:6.11.0, css-loader@npm:^6.5.1, css-loader@npm:^6.7.1": version: 6.11.0 resolution: "css-loader@npm:6.11.0" @@ -22937,7 +22465,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.7, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:^4.3.6, debug@npm:~4.3.4": +"debug@npm:4, debug@npm:4.3.7, debug@npm:^4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.4": version: 4.3.7 resolution: "debug@npm:4.3.7" dependencies: @@ -22970,13 +22498,6 @@ __metadata: languageName: node linkType: hard -"decamelize@npm:1.2.0": - version: 1.2.0 - resolution: "decamelize@npm:1.2.0" - checksum: ad8c51a7e7e0720c70ec2eeb1163b66da03e7616d7b98c9ef43cce2416395e84c1e9548dd94f5f6ffecfee9f8b94251fc57121a8b021f2ff2469b2bae247b8aa - languageName: node - linkType: hard - "decimal.js@npm:^10.2.1, decimal.js@npm:^10.4.2, decimal.js@npm:^10.4.3": version: 10.4.3 resolution: "decimal.js@npm:10.4.3" @@ -23417,15 +22938,6 @@ __metadata: languageName: node linkType: hard -"devlop@npm:^1.0.0, devlop@npm:^1.1.0": - version: 1.1.0 - resolution: "devlop@npm:1.1.0" - dependencies: - dequal: ^2.0.0 - checksum: d2ff650bac0bb6ef08c48f3ba98640bb5fec5cce81e9957eb620408d1bab1204d382a45b785c6b3314dc867bb0684936b84c6867820da6db97cbb5d3c15dd185 - languageName: node - linkType: hard - "dezalgo@npm:^1.0.4": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -25219,13 +24731,6 @@ __metadata: languageName: node linkType: hard -"estree-util-is-identifier-name@npm:^3.0.0": - version: 3.0.0 - resolution: "estree-util-is-identifier-name@npm:3.0.0" - checksum: ea3909f0188ea164af0aadeca87c087e3e5da78d76da5ae9c7954ff1340ea3e4679c4653bbf4299ffb70caa9b322218cc1128db2541f3d2976eb9704f9857787 - languageName: node - linkType: hard - "estree-walker@npm:^0.6.1": version: 0.6.1 resolution: "estree-walker@npm:0.6.1" @@ -26358,13 +25863,6 @@ __metadata: languageName: node linkType: hard -"form-data-encoder@npm:1.7.2": - version: 1.7.2 - resolution: "form-data-encoder@npm:1.7.2" - checksum: aeebd87a1cb009e13cbb5e4e4008e6202ed5f6551eb6d9582ba8a062005178907b90f4887899d3c993de879159b6c0c940af8196725b428b4248cec5af3acf5f - languageName: node - linkType: hard - "form-data@npm:^2.3.2, form-data@npm:^2.5.0": version: 2.5.2 resolution: "form-data@npm:2.5.2" @@ -26417,16 +25915,6 @@ __metadata: languageName: node linkType: hard -"formdata-node@npm:^4.3.2": - version: 4.4.1 - resolution: "formdata-node@npm:4.4.1" - dependencies: - node-domexception: 1.0.0 - web-streams-polyfill: 4.0.0-beta.3 - checksum: d91d4f667cfed74827fc281594102c0dabddd03c9f8b426fc97123eedbf73f5060ee43205d89284d6854e2fc5827e030cd352ef68b93beda8decc2d72128c576 - languageName: node - linkType: hard - "formidable@npm:^2.1.2": version: 2.1.2 resolution: "formidable@npm:2.1.2" @@ -26453,26 +25941,6 @@ __metadata: languageName: node linkType: hard -"framer-motion@npm:^11.3.28": - version: 11.11.9 - resolution: "framer-motion@npm:11.11.9" - dependencies: - tslib: ^2.4.0 - peerDependencies: - "@emotion/is-prop-valid": "*" - react: ^18.0.0 - react-dom: ^18.0.0 - peerDependenciesMeta: - "@emotion/is-prop-valid": - optional: true - react: - optional: true - react-dom: - optional: true - checksum: d2918aa3de0746cadb60af7f940c72d17b0d2869b66992f47d6a187abc3599cbf6520188ea8131c9fd7fe65cd87783fbfce3d2115f833b1488a8d73509e5789a - languageName: node - linkType: hard - "framer-motion@npm:^6.5.1": version: 6.5.1 resolution: "framer-motion@npm:6.5.1" @@ -27530,29 +26998,6 @@ __metadata: languageName: node linkType: hard -"hast-util-to-jsx-runtime@npm:^2.0.0": - version: 2.3.2 - resolution: "hast-util-to-jsx-runtime@npm:2.3.2" - dependencies: - "@types/estree": ^1.0.0 - "@types/hast": ^3.0.0 - "@types/unist": ^3.0.0 - comma-separated-tokens: ^2.0.0 - devlop: ^1.0.0 - estree-util-is-identifier-name: ^3.0.0 - hast-util-whitespace: ^3.0.0 - mdast-util-mdx-expression: ^2.0.0 - mdast-util-mdx-jsx: ^3.0.0 - mdast-util-mdxjs-esm: ^2.0.0 - property-information: ^6.0.0 - space-separated-tokens: ^2.0.0 - style-to-object: ^1.0.0 - unist-util-position: ^5.0.0 - vfile-message: ^4.0.0 - checksum: 223cc3e2ea622d14529e2aa070bd88f6ca7255084bd5e6e28015dad435cda22b1ddd98064bba6a4753d546d882dcd3f8067af1ea27c253986f6f303869544075 - languageName: node - linkType: hard - "hast-util-whitespace@npm:^2.0.0": version: 2.0.1 resolution: "hast-util-whitespace@npm:2.0.1" @@ -27560,15 +27005,6 @@ __metadata: languageName: node linkType: hard -"hast-util-whitespace@npm:^3.0.0": - version: 3.0.0 - resolution: "hast-util-whitespace@npm:3.0.0" - dependencies: - "@types/hast": ^3.0.0 - checksum: 41d93ccce218ba935dc3c12acdf586193c35069489c8c8f50c2aa824c00dec94a3c78b03d1db40fa75381942a189161922e4b7bca700b3a2cc779634c351a1e4 - languageName: node - linkType: hard - "hastscript@npm:^6.0.0": version: 6.0.0 resolution: "hastscript@npm:6.0.0" @@ -27598,13 +27034,6 @@ __metadata: languageName: node linkType: hard -"headers-polyfill@npm:^4.0.2": - version: 4.0.3 - resolution: "headers-polyfill@npm:4.0.3" - checksum: 382efe88575362f9f343f813a9df5131cec23129121111c55fb1151fb6dc87d963a820412fc95ff9cbc3016149de0714211dfa5d5914020ed92a69f014f66600 - languageName: node - linkType: hard - "helmet@npm:^6.0.0": version: 6.2.0 resolution: "helmet@npm:6.2.0" @@ -27660,7 +27089,7 @@ __metadata: languageName: node linkType: hard -"hoist-non-react-statics@npm:^3.2.0, hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": +"hoist-non-react-statics@npm:^3.3.0, hoist-non-react-statics@npm:^3.3.1, hoist-non-react-statics@npm:^3.3.2": version: 3.3.2 resolution: "hoist-non-react-statics@npm:3.3.2" dependencies: @@ -27767,13 +27196,6 @@ __metadata: languageName: node linkType: hard -"html-url-attributes@npm:^3.0.0": - version: 3.0.1 - resolution: "html-url-attributes@npm:3.0.1" - checksum: 1ecbf9cae0c438d2802386710177b7bbf7e30cc61327e9f125eb32fca7302cd1e3ab45c441859cb1e7646109be322fc1163592ad4dfde9b14d09416d101a6573 - languageName: node - linkType: hard - "html-webpack-plugin@npm:5.6.0": version: 5.6.0 resolution: "html-webpack-plugin@npm:5.6.0" @@ -27958,20 +27380,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:^3.0.2": - version: 3.0.3 - resolution: "http-proxy-middleware@npm:3.0.3" - dependencies: - "@types/http-proxy": ^1.17.15 - debug: ^4.3.6 - http-proxy: ^1.18.1 - is-glob: ^4.0.3 - is-plain-object: ^5.0.0 - micromatch: ^4.0.8 - checksum: 7486cd36f8123aaeb9e5412ecdf1bbfb69a9c7a5cd51cdb202653fe7201fc6073849f981d814b429527e42bb825d57af09ba7b8ba9d95e194dbd7dd83bf51643 - languageName: node - linkType: hard - "http-proxy@npm:^1.18.1": version: 1.18.1 resolution: "http-proxy@npm:1.18.1" @@ -28337,13 +27745,6 @@ __metadata: languageName: node linkType: hard -"inline-style-parser@npm:0.2.4": - version: 0.2.4 - resolution: "inline-style-parser@npm:0.2.4" - checksum: 5df20a21dd8d67104faaae29774bb50dc9690c75bc5c45dac107559670a5530104ead72c4cf54f390026e617e7014c65b3d68fb0bb573a37c4d1f94e9c36e1ca - languageName: node - linkType: hard - "inline-style-prefixer@npm:^7.0.1": version: 7.0.1 resolution: "inline-style-prefixer@npm:7.0.1" @@ -28479,13 +27880,6 @@ __metadata: languageName: node linkType: hard -"is-alphabetical@npm:^2.0.0": - version: 2.0.1 - resolution: "is-alphabetical@npm:2.0.1" - checksum: 56207db8d9de0850f0cd30f4966bf731eb82cedfe496cbc2e97e7c3bacaf66fc54a972d2d08c0d93bb679cb84976a05d24c5ad63de56fabbfc60aadae312edaa - languageName: node - linkType: hard - "is-alphanumerical@npm:^1.0.0": version: 1.0.4 resolution: "is-alphanumerical@npm:1.0.4" @@ -28496,16 +27890,6 @@ __metadata: languageName: node linkType: hard -"is-alphanumerical@npm:^2.0.0": - version: 2.0.1 - resolution: "is-alphanumerical@npm:2.0.1" - dependencies: - is-alphabetical: ^2.0.0 - is-decimal: ^2.0.0 - checksum: 87acc068008d4c9c4e9f5bd5e251041d42e7a50995c77b1499cf6ed248f971aadeddb11f239cabf09f7975ee58cac7a48ffc170b7890076d8d227b24a68663c9 - languageName: node - linkType: hard - "is-arguments@npm:^1.0.4, is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" @@ -28634,13 +28018,6 @@ __metadata: languageName: node linkType: hard -"is-decimal@npm:^2.0.0": - version: 2.0.1 - resolution: "is-decimal@npm:2.0.1" - checksum: 97132de7acdce77caa7b797632970a2ecd649a88e715db0e4dbc00ab0708b5e7574ba5903962c860cd4894a14fd12b100c0c4ac8aed445cf6f55c6cf747a4158 - languageName: node - linkType: hard - "is-deflate@npm:^1.0.0": version: 1.0.0 resolution: "is-deflate@npm:1.0.0" @@ -28751,13 +28128,6 @@ __metadata: languageName: node linkType: hard -"is-hexadecimal@npm:^2.0.0": - version: 2.0.1 - resolution: "is-hexadecimal@npm:2.0.1" - checksum: 66a2ea85994c622858f063f23eda506db29d92b52580709eb6f4c19550552d4dcf3fb81952e52f7cf972097237959e00adc7bb8c9400cd12886e15bf06145321 - languageName: node - linkType: hard - "is-in-browser@npm:^1.0.2, is-in-browser@npm:^1.1.3": version: 1.1.3 resolution: "is-in-browser@npm:1.1.3" @@ -29972,15 +29342,6 @@ __metadata: languageName: node linkType: hard -"js-tiktoken@npm:^1.0.12": - version: 1.0.15 - resolution: "js-tiktoken@npm:1.0.15" - dependencies: - base64-js: ^1.5.1 - checksum: fb37641fcbec0386276e99459a4c94c9e790b3fe59143191e06e20a8069695999afe7da00fddeb591a731a68afcf068803a75d77825b2d697541bb165e4795eb - languageName: node - linkType: hard - "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -30478,7 +29839,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-camel-case@npm:10.10.0, jss-plugin-camel-case@npm:^10.10.0, jss-plugin-camel-case@npm:^10.5.1": +"jss-plugin-camel-case@npm:^10.10.0, jss-plugin-camel-case@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-camel-case@npm:10.10.0" dependencies: @@ -30489,18 +29850,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-compose@npm:10.10.0": - version: 10.10.0 - resolution: "jss-plugin-compose@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - tiny-warning: ^1.0.2 - checksum: 4d9c7adf34d3dcbc7b321c980b21b396fe9b97d93d3622e242f4e267ac278753cc7575a003f545138e211fd3302a66e9b75926b4f8e3e7cd7ba21b402a4b5f17 - languageName: node - linkType: hard - -"jss-plugin-default-unit@npm:10.10.0, jss-plugin-default-unit@npm:^10.10.0, jss-plugin-default-unit@npm:^10.5.1": +"jss-plugin-default-unit@npm:^10.10.0, jss-plugin-default-unit@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-default-unit@npm:10.10.0" dependencies: @@ -30510,28 +29860,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-expand@npm:10.10.0": - version: 10.10.0 - resolution: "jss-plugin-expand@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - checksum: 264f198c472b5ef2e11e8897546edcf65f017352426990773f781eaf9921faae132e9611991e5b761ee9a083475e948efd4b6e28a01e25880c2b55c7a044cb9a - languageName: node - linkType: hard - -"jss-plugin-extend@npm:10.10.0": - version: 10.10.0 - resolution: "jss-plugin-extend@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - tiny-warning: ^1.0.2 - checksum: e163ac6e5f91c6cfc0eda2388b7f3cd8a38d1651fce6ea69a6b8bbaf26266f22a3bdfbe4e56100f12beb98a5f93aaa4dbcfc399ffaf906424cab0fdace645b6b - languageName: node - linkType: hard - -"jss-plugin-global@npm:10.10.0, jss-plugin-global@npm:^10.10.0, jss-plugin-global@npm:^10.5.1": +"jss-plugin-global@npm:^10.10.0, jss-plugin-global@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-global@npm:10.10.0" dependencies: @@ -30541,7 +29870,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-nested@npm:10.10.0, jss-plugin-nested@npm:^10.10.0, jss-plugin-nested@npm:^10.5.1": +"jss-plugin-nested@npm:^10.10.0, jss-plugin-nested@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-nested@npm:10.10.0" dependencies: @@ -30552,7 +29881,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-props-sort@npm:10.10.0, jss-plugin-props-sort@npm:^10.10.0, jss-plugin-props-sort@npm:^10.5.1": +"jss-plugin-props-sort@npm:^10.10.0, jss-plugin-props-sort@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-props-sort@npm:10.10.0" dependencies: @@ -30562,7 +29891,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-rule-value-function@npm:10.10.0, jss-plugin-rule-value-function@npm:^10.10.0, jss-plugin-rule-value-function@npm:^10.5.1": +"jss-plugin-rule-value-function@npm:^10.10.0, jss-plugin-rule-value-function@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-rule-value-function@npm:10.10.0" dependencies: @@ -30573,29 +29902,7 @@ __metadata: languageName: node linkType: hard -"jss-plugin-rule-value-observable@npm:10.10.0": - version: 10.10.0 - resolution: "jss-plugin-rule-value-observable@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - symbol-observable: ^1.2.0 - checksum: ec549b1ce8d8a356145378eac22bee8afdfe8cc4ee94237e9009a9b0d5dc228ed04cf1924b6628a625cf44f27aa34593dce482a14cd1e04a4c1e9cc3b80e5db4 - languageName: node - linkType: hard - -"jss-plugin-template@npm:10.10.0": - version: 10.10.0 - resolution: "jss-plugin-template@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - tiny-warning: ^1.0.2 - checksum: 695e0b1d60679736134d70cf886d5ba001412e863624cbae08bf6bdf33188a670b3083bb229f1253cf6555288e7929b33ff622ab3b6d7ec68a72ab335f580223 - languageName: node - linkType: hard - -"jss-plugin-vendor-prefixer@npm:10.10.0, jss-plugin-vendor-prefixer@npm:^10.10.0, jss-plugin-vendor-prefixer@npm:^10.5.1": +"jss-plugin-vendor-prefixer@npm:^10.10.0, jss-plugin-vendor-prefixer@npm:^10.5.1": version: 10.10.0 resolution: "jss-plugin-vendor-prefixer@npm:10.10.0" dependencies: @@ -30606,28 +29913,6 @@ __metadata: languageName: node linkType: hard -"jss-preset-default@npm:10.10.0, jss-preset-default@npm:^10.10.0": - version: 10.10.0 - resolution: "jss-preset-default@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - jss: 10.10.0 - jss-plugin-camel-case: 10.10.0 - jss-plugin-compose: 10.10.0 - jss-plugin-default-unit: 10.10.0 - jss-plugin-expand: 10.10.0 - jss-plugin-extend: 10.10.0 - jss-plugin-global: 10.10.0 - jss-plugin-nested: 10.10.0 - jss-plugin-props-sort: 10.10.0 - jss-plugin-rule-value-function: 10.10.0 - jss-plugin-rule-value-observable: 10.10.0 - jss-plugin-template: 10.10.0 - jss-plugin-vendor-prefixer: 10.10.0 - checksum: 128c37333566868f008b315a051a76781f4d78f5e1f9eea25d69841057c659a4d3d0937cc665e9ac9a80a12ea6028cec4a8efa2a3f942b4b831f47244fbf32e3 - languageName: node - linkType: hard - "jss@npm:10.10.0, jss@npm:^10.10.0, jss@npm:^10.5.1, jss@npm:~10.10.0": version: 10.10.0 resolution: "jss@npm:10.10.0" @@ -30867,25 +30152,6 @@ __metadata: languageName: node linkType: hard -"langsmith@npm:^0.1.56-rc.1": - version: 0.1.66 - resolution: "langsmith@npm:0.1.66" - dependencies: - "@types/uuid": ^10.0.0 - commander: ^10.0.1 - p-queue: ^6.6.2 - p-retry: 4 - semver: ^7.6.3 - uuid: ^10.0.0 - peerDependencies: - openai: "*" - peerDependenciesMeta: - openai: - optional: true - checksum: 9c0cb365e2278a255dc0d045402fbec67cf3a6314e7858b398a267b1e97d3f24e5b4ba5286c80a3fe6c13443ac1fca8fd1555f3a0990b74b7c6be8c608581394 - languageName: node - linkType: hard - "language-subtag-registry@npm:^0.3.20": version: 0.3.23 resolution: "language-subtag-registry@npm:0.3.23" @@ -31850,18 +31116,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-find-and-replace@npm:^3.0.0": - version: 3.0.1 - resolution: "mdast-util-find-and-replace@npm:3.0.1" - dependencies: - "@types/mdast": ^4.0.0 - escape-string-regexp: ^5.0.0 - unist-util-is: ^6.0.0 - unist-util-visit-parents: ^6.0.0 - checksum: 05d5c4ff02e31db2f8a685a13bcb6c3f44e040bd9dfa54c19a232af8de5268334c8755d79cb456ed4cced1300c4fb83e88444c7ae8ee9ff16869a580f29d08cd - languageName: node - linkType: hard - "mdast-util-from-markdown@npm:^1.0.0": version: 1.3.1 resolution: "mdast-util-from-markdown@npm:1.3.1" @@ -31882,26 +31136,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-from-markdown@npm:^2.0.0": - version: 2.0.1 - resolution: "mdast-util-from-markdown@npm:2.0.1" - dependencies: - "@types/mdast": ^4.0.0 - "@types/unist": ^3.0.0 - decode-named-character-reference: ^1.0.0 - devlop: ^1.0.0 - mdast-util-to-string: ^4.0.0 - micromark: ^4.0.0 - micromark-util-decode-numeric-character-reference: ^2.0.0 - micromark-util-decode-string: ^2.0.0 - micromark-util-normalize-identifier: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - unist-util-stringify-position: ^4.0.0 - checksum: 2e50be71272a1503558c599cd5766cf2743935a021f82e32bc2ae5da44f6c7dcabb9da3a6eee76ede0ec8ad2b122d1192f4fe89890aac90c76463f049f8a835d - languageName: node - linkType: hard - "mdast-util-gfm-autolink-literal@npm:^1.0.0": version: 1.0.3 resolution: "mdast-util-gfm-autolink-literal@npm:1.0.3" @@ -31914,19 +31148,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-autolink-literal@npm:^2.0.0": - version: 2.0.1 - resolution: "mdast-util-gfm-autolink-literal@npm:2.0.1" - dependencies: - "@types/mdast": ^4.0.0 - ccount: ^2.0.0 - devlop: ^1.0.0 - mdast-util-find-and-replace: ^3.0.0 - micromark-util-character: ^2.0.0 - checksum: 5630b12e072d7004cb132231c94f667fb5813486779cb0dfb0a196d7ae0e048897a43b0b37e080017adda618ddfcbea1d7bf23c0fa31c87bfc683e0898ea1cfe - languageName: node - linkType: hard - "mdast-util-gfm-footnote@npm:^1.0.0": version: 1.0.2 resolution: "mdast-util-gfm-footnote@npm:1.0.2" @@ -31938,19 +31159,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-footnote@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-gfm-footnote@npm:2.0.0" - dependencies: - "@types/mdast": ^4.0.0 - devlop: ^1.1.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - micromark-util-normalize-identifier: ^2.0.0 - checksum: 45d26b40e7a093712e023105791129d76e164e2168d5268e113298a22de30c018162683fb7893cdc04ab246dac0087eed708b2a136d1d18ed2b32b3e0cae4a79 - languageName: node - linkType: hard - "mdast-util-gfm-strikethrough@npm:^1.0.0": version: 1.0.3 resolution: "mdast-util-gfm-strikethrough@npm:1.0.3" @@ -31961,17 +31169,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-strikethrough@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-gfm-strikethrough@npm:2.0.0" - dependencies: - "@types/mdast": ^4.0.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: fe9b1d0eba9b791ff9001c008744eafe3dd7a81b085f2bf521595ce4a8e8b1b44764ad9361761ad4533af3e5d913d8ad053abec38172031d9ee32a8ebd1c7dbd - languageName: node - linkType: hard - "mdast-util-gfm-table@npm:^1.0.0": version: 1.0.7 resolution: "mdast-util-gfm-table@npm:1.0.7" @@ -31984,19 +31181,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-table@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-gfm-table@npm:2.0.0" - dependencies: - "@types/mdast": ^4.0.0 - devlop: ^1.0.0 - markdown-table: ^3.0.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: 063a627fd0993548fd63ca0c24c437baf91ba7d51d0a38820bd459bc20bf3d13d7365ef8d28dca99176dd5eb26058f7dde51190479c186dfe6af2e11202957c9 - languageName: node - linkType: hard - "mdast-util-gfm-task-list-item@npm:^1.0.0": version: 1.0.2 resolution: "mdast-util-gfm-task-list-item@npm:1.0.2" @@ -32007,18 +31191,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-task-list-item@npm:^2.0.0": - version: 2.0.0 - resolution: "mdast-util-gfm-task-list-item@npm:2.0.0" - dependencies: - "@types/mdast": ^4.0.0 - devlop: ^1.0.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: 37db90c59b15330fc54d790404abf5ef9f2f83e8961c53666fe7de4aab8dd5e6b3c296b6be19797456711a89a27840291d8871ff0438e9b4e15c89d170efe072 - languageName: node - linkType: hard - "mdast-util-gfm@npm:^2.0.0": version: 2.0.2 resolution: "mdast-util-gfm@npm:2.0.2" @@ -32034,69 +31206,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm@npm:^3.0.0": - version: 3.0.0 - resolution: "mdast-util-gfm@npm:3.0.0" - dependencies: - mdast-util-from-markdown: ^2.0.0 - mdast-util-gfm-autolink-literal: ^2.0.0 - mdast-util-gfm-footnote: ^2.0.0 - mdast-util-gfm-strikethrough: ^2.0.0 - mdast-util-gfm-table: ^2.0.0 - mdast-util-gfm-task-list-item: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: 62039d2f682ae3821ea1c999454863d31faf94d67eb9b746589c7e136076d7fb35fabc67e02f025c7c26fd7919331a0ee1aabfae24f565d9a6a9ebab3371c626 - languageName: node - linkType: hard - -"mdast-util-mdx-expression@npm:^2.0.0": - version: 2.0.1 - resolution: "mdast-util-mdx-expression@npm:2.0.1" - dependencies: - "@types/estree-jsx": ^1.0.0 - "@types/hast": ^3.0.0 - "@types/mdast": ^4.0.0 - devlop: ^1.0.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: 6af56b06bde3ab971129db9855dcf0d31806c70b3b052d7a90a5499a366b57ffd0c2efca67d281c448c557298ba7e3e61bd07133733b735440840dd339b28e19 - languageName: node - linkType: hard - -"mdast-util-mdx-jsx@npm:^3.0.0": - version: 3.1.3 - resolution: "mdast-util-mdx-jsx@npm:3.1.3" - dependencies: - "@types/estree-jsx": ^1.0.0 - "@types/hast": ^3.0.0 - "@types/mdast": ^4.0.0 - "@types/unist": ^3.0.0 - ccount: ^2.0.0 - devlop: ^1.1.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - parse-entities: ^4.0.0 - stringify-entities: ^4.0.0 - unist-util-stringify-position: ^4.0.0 - vfile-message: ^4.0.0 - checksum: 638644420090163fc08d01150e10550a21e914b85dd3a37178d3b949173c5aee2d7fee536f864ac25800e0cebde8357a5808427ffb7e9975a669e4382ae479ab - languageName: node - linkType: hard - -"mdast-util-mdxjs-esm@npm:^2.0.0": - version: 2.0.1 - resolution: "mdast-util-mdxjs-esm@npm:2.0.1" - dependencies: - "@types/estree-jsx": ^1.0.0 - "@types/hast": ^3.0.0 - "@types/mdast": ^4.0.0 - devlop: ^1.0.0 - mdast-util-from-markdown: ^2.0.0 - mdast-util-to-markdown: ^2.0.0 - checksum: 1f9dad04d31d59005332e9157ea9510dc1d03092aadbc607a10475c7eec1c158b475aa0601a3a4f74e13097ca735deb8c2d9d37928ddef25d3029fd7c9e14dc3 - languageName: node - linkType: hard - "mdast-util-phrasing@npm:^3.0.0": version: 3.0.1 resolution: "mdast-util-phrasing@npm:3.0.1" @@ -32107,16 +31216,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-phrasing@npm:^4.0.0": - version: 4.1.0 - resolution: "mdast-util-phrasing@npm:4.1.0" - dependencies: - "@types/mdast": ^4.0.0 - unist-util-is: ^6.0.0 - checksum: 3a97533e8ad104a422f8bebb34b3dde4f17167b8ed3a721cf9263c7416bd3447d2364e6d012a594aada40cac9e949db28a060bb71a982231693609034ed5324e - languageName: node - linkType: hard - "mdast-util-to-hast@npm:^12.1.0": version: 12.3.0 resolution: "mdast-util-to-hast@npm:12.3.0" @@ -32133,23 +31232,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-hast@npm:^13.0.0": - version: 13.2.0 - resolution: "mdast-util-to-hast@npm:13.2.0" - dependencies: - "@types/hast": ^3.0.0 - "@types/mdast": ^4.0.0 - "@ungap/structured-clone": ^1.0.0 - devlop: ^1.0.0 - micromark-util-sanitize-uri: ^2.0.0 - trim-lines: ^3.0.0 - unist-util-position: ^5.0.0 - unist-util-visit: ^5.0.0 - vfile: ^6.0.0 - checksum: 7e5231ff3d4e35e1421908437577fd5098141f64918ff5cc8a0f7a8a76c5407f7a3ee88d75f7a1f7afb763989c9f357475fa0ba8296c00aaff1e940098fe86a6 - languageName: node - linkType: hard - "mdast-util-to-markdown@npm:^1.0.0, mdast-util-to-markdown@npm:^1.3.0": version: 1.5.0 resolution: "mdast-util-to-markdown@npm:1.5.0" @@ -32166,22 +31248,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-markdown@npm:^2.0.0": - version: 2.1.0 - resolution: "mdast-util-to-markdown@npm:2.1.0" - dependencies: - "@types/mdast": ^4.0.0 - "@types/unist": ^3.0.0 - longest-streak: ^3.0.0 - mdast-util-phrasing: ^4.0.0 - mdast-util-to-string: ^4.0.0 - micromark-util-decode-string: ^2.0.0 - unist-util-visit: ^5.0.0 - zwitch: ^2.0.0 - checksum: 3a2cf3957e23b34e2e092e6e76ae72ee0b8745955bd811baba6814cf3a3d916c3fd52264b4b58f3bb3d512a428f84a1e998b6fc7e28434e388a9ae8fb6a9c173 - languageName: node - linkType: hard - "mdast-util-to-string@npm:^1.0.0": version: 1.1.0 resolution: "mdast-util-to-string@npm:1.1.0" @@ -32198,15 +31264,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-string@npm:^4.0.0": - version: 4.0.0 - resolution: "mdast-util-to-string@npm:4.0.0" - dependencies: - "@types/mdast": ^4.0.0 - checksum: 35489fb5710d58cbc2d6c8b6547df161a3f81e0f28f320dfb3548a9393555daf07c310c0c497708e67ed4dfea4a06e5655799e7d631ca91420c288b4525d6c29 - languageName: node - linkType: hard - "mdn-data@npm:2.0.14": version: 2.0.14 resolution: "mdn-data@npm:2.0.14" @@ -32378,30 +31435,6 @@ __metadata: languageName: node linkType: hard -"micromark-core-commonmark@npm:^2.0.0": - version: 2.0.1 - resolution: "micromark-core-commonmark@npm:2.0.1" - dependencies: - decode-named-character-reference: ^1.0.0 - devlop: ^1.0.0 - micromark-factory-destination: ^2.0.0 - micromark-factory-label: ^2.0.0 - micromark-factory-space: ^2.0.0 - micromark-factory-title: ^2.0.0 - micromark-factory-whitespace: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-chunked: ^2.0.0 - micromark-util-classify-character: ^2.0.0 - micromark-util-html-tag-name: ^2.0.0 - micromark-util-normalize-identifier: ^2.0.0 - micromark-util-resolve-all: ^2.0.0 - micromark-util-subtokenize: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 6a9891cc883a531e090dc8dab6669945f3df9448e84216a8f2a91f9258281e6abea5ae3940fde2bd77a57dc3e0d67f2add6762aed63a378f37b09eaf7e7426c4 - languageName: node - linkType: hard - "micromark-extension-gfm-autolink-literal@npm:^1.0.0": version: 1.0.5 resolution: "micromark-extension-gfm-autolink-literal@npm:1.0.5" @@ -32414,18 +31447,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-autolink-literal@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-extension-gfm-autolink-literal@npm:2.1.0" - dependencies: - micromark-util-character: ^2.0.0 - micromark-util-sanitize-uri: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: e00a570c70c837b9cbbe94b2c23b787f44e781cd19b72f1828e3453abca2a9fb600fa539cdc75229fa3919db384491063645086e02249481e6ff3ec2c18f767c - languageName: node - linkType: hard - "micromark-extension-gfm-footnote@npm:^1.0.0": version: 1.1.2 resolution: "micromark-extension-gfm-footnote@npm:1.1.2" @@ -32442,22 +31463,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-footnote@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-extension-gfm-footnote@npm:2.1.0" - dependencies: - devlop: ^1.0.0 - micromark-core-commonmark: ^2.0.0 - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-normalize-identifier: ^2.0.0 - micromark-util-sanitize-uri: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: ac6fb039e98395d37b71ebff7c7a249aef52678b5cf554c89c4f716111d4be62ef99a5d715a5bd5d68fa549778c977d85cb671d1d8506dc8a3a1b46e867ae52f - languageName: node - linkType: hard - "micromark-extension-gfm-strikethrough@npm:^1.0.0": version: 1.0.7 resolution: "micromark-extension-gfm-strikethrough@npm:1.0.7" @@ -32472,20 +31477,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-strikethrough@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-extension-gfm-strikethrough@npm:2.1.0" - dependencies: - devlop: ^1.0.0 - micromark-util-chunked: ^2.0.0 - micromark-util-classify-character: ^2.0.0 - micromark-util-resolve-all: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: cdb7a38dd6eefb6ceb6792a44a6796b10f951e8e3e45b8579f599f43e7ae26ccd048c0aa7e441b3c29dd0c54656944fe6eb0098de2bc4b5106fbc0a42e9e016c - languageName: node - linkType: hard - "micromark-extension-gfm-table@npm:^1.0.0": version: 1.0.7 resolution: "micromark-extension-gfm-table@npm:1.0.7" @@ -32499,19 +31490,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-table@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-extension-gfm-table@npm:2.1.0" - dependencies: - devlop: ^1.0.0 - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 249d695f5f8bd222a0d8a774ec78ea2a2d624cb50a4d008092a54aa87dad1f9d540e151d29696cf849eb1cee380113c4df722aebb3b425a214832a2de5dea1d7 - languageName: node - linkType: hard - "micromark-extension-gfm-tagfilter@npm:^1.0.0": version: 1.0.2 resolution: "micromark-extension-gfm-tagfilter@npm:1.0.2" @@ -32521,15 +31499,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-tagfilter@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-extension-gfm-tagfilter@npm:2.0.0" - dependencies: - micromark-util-types: ^2.0.0 - checksum: cf21552f4a63592bfd6c96ae5d64a5f22bda4e77814e3f0501bfe80e7a49378ad140f827007f36044666f176b3a0d5fea7c2e8e7973ce4b4579b77789f01ae95 - languageName: node - linkType: hard - "micromark-extension-gfm-task-list-item@npm:^1.0.0": version: 1.0.5 resolution: "micromark-extension-gfm-task-list-item@npm:1.0.5" @@ -32543,19 +31512,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-task-list-item@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-extension-gfm-task-list-item@npm:2.1.0" - dependencies: - devlop: ^1.0.0 - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: b1ad86a4e9d68d9ad536d94fb25a5182acbc85cc79318f4a6316034342f6a71d67983cc13f12911d0290fd09b2bda43cdabe8781a2d9cca2ebe0d421e8b2b8a4 - languageName: node - linkType: hard - "micromark-extension-gfm@npm:^2.0.0": version: 2.0.3 resolution: "micromark-extension-gfm@npm:2.0.3" @@ -32572,22 +31528,6 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm@npm:^3.0.0": - version: 3.0.0 - resolution: "micromark-extension-gfm@npm:3.0.0" - dependencies: - micromark-extension-gfm-autolink-literal: ^2.0.0 - micromark-extension-gfm-footnote: ^2.0.0 - micromark-extension-gfm-strikethrough: ^2.0.0 - micromark-extension-gfm-table: ^2.0.0 - micromark-extension-gfm-tagfilter: ^2.0.0 - micromark-extension-gfm-task-list-item: ^2.0.0 - micromark-util-combine-extensions: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 2060fa62666a09532d6b3a272d413bc1b25bbb262f921d7402795ac021e1362c8913727e33d7528d5b4ccaf26922ec51208c43f795a702964817bc986de886c9 - languageName: node - linkType: hard - "micromark-factory-destination@npm:^1.0.0": version: 1.1.0 resolution: "micromark-factory-destination@npm:1.1.0" @@ -32599,17 +31539,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-destination@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-factory-destination@npm:2.0.0" - dependencies: - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: d36e65ed1c072ff4148b016783148ba7c68a078991154625723e24bda3945160268fb91079fb28618e1613c2b6e70390a8ddc544c45410288aa27b413593071a - languageName: node - linkType: hard - "micromark-factory-label@npm:^1.0.0": version: 1.1.0 resolution: "micromark-factory-label@npm:1.1.0" @@ -32622,18 +31551,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-label@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-factory-label@npm:2.0.0" - dependencies: - devlop: ^1.0.0 - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: c021dbd0ed367610d35f2bae21209bc804d1a6d1286ffce458fd6a717f4d7fe581a7cba7d5c2d7a63757c44eb927c80d6a571d6ea7969fae1b48ab6461d109c4 - languageName: node - linkType: hard - "micromark-factory-space@npm:^1.0.0": version: 1.1.0 resolution: "micromark-factory-space@npm:1.1.0" @@ -32644,16 +31561,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-space@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-factory-space@npm:2.0.0" - dependencies: - micromark-util-character: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 4ffdcdc2f759887bbb356500cb460b3915ecddcb5d85c3618d7df68ad05d13ed02b1153ee1845677b7d8126df8f388288b84fcf0d943bd9c92bcc71cd7222e37 - languageName: node - linkType: hard - "micromark-factory-title@npm:^1.0.0": version: 1.1.0 resolution: "micromark-factory-title@npm:1.1.0" @@ -32666,18 +31573,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-title@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-factory-title@npm:2.0.0" - dependencies: - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 39e1ac23af3554e6e652e56065579bc7faf21ade7b8704b29c175871b4152b7109b790bb3cae0f7e088381139c6bac9553b8400772c3d322e4fa635f813a3578 - languageName: node - linkType: hard - "micromark-factory-whitespace@npm:^1.0.0": version: 1.1.0 resolution: "micromark-factory-whitespace@npm:1.1.0" @@ -32690,18 +31585,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-whitespace@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-factory-whitespace@npm:2.0.0" - dependencies: - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 9587c2546d1a58b4d5472b42adf05463f6212d0449455285662d63cd8eaed89c6b159ac82713fcee5f9dd88628c24307d9533cccd8971a2f3f4d48702f8f850a - languageName: node - linkType: hard - "micromark-util-character@npm:^1.0.0": version: 1.2.0 resolution: "micromark-util-character@npm:1.2.0" @@ -32712,16 +31595,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-character@npm:^2.0.0": - version: 2.1.0 - resolution: "micromark-util-character@npm:2.1.0" - dependencies: - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 36ee910f84077cf16626fa618cfe46ac25956b3242e3166b8e8e98c5a8c524af7e5bf3d70822264b1fd2d297a36104a7eb7e3462c19c28353eaca7b0d8717594 - languageName: node - linkType: hard - "micromark-util-chunked@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-chunked@npm:1.1.0" @@ -32731,15 +31604,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-chunked@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-chunked@npm:2.0.0" - dependencies: - micromark-util-symbol: ^2.0.0 - checksum: 324f95cccdae061332a8241936eaba6ef0782a1e355bac5c607ad2564fd3744929be7dc81651315a2921535747a33243e6a5606bcb64b7a56d49b6d74ea1a3d4 - languageName: node - linkType: hard - "micromark-util-classify-character@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-classify-character@npm:1.1.0" @@ -32751,17 +31615,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-classify-character@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-classify-character@npm:2.0.0" - dependencies: - micromark-util-character: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 086e52904deffebb793fb1c08c94aabb8901f76958142dfc3a6282890ebaa983b285e69bd602b9d507f1b758ed38e75a994d2ad9fbbefa7de2584f67a16af405 - languageName: node - linkType: hard - "micromark-util-combine-extensions@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-combine-extensions@npm:1.1.0" @@ -32772,16 +31625,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-combine-extensions@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-combine-extensions@npm:2.0.0" - dependencies: - micromark-util-chunked: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 107c47700343f365b4ed81551e18bc3458b573c500e56ac052b2490bd548adc475216e41d2271633a8867fac66fc22ba3e0a2d74a31ed79b9870ca947eb4e3ba - languageName: node - linkType: hard - "micromark-util-decode-numeric-character-reference@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-decode-numeric-character-reference@npm:1.1.0" @@ -32791,15 +31634,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-decode-numeric-character-reference@npm:^2.0.0": - version: 2.0.1 - resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.1" - dependencies: - micromark-util-symbol: ^2.0.0 - checksum: 9512507722efd2033a9f08715eeef787fbfe27e23edf55db21423d46d82ab46f76c89b4f960be3f5e50a2d388d89658afc0647989cf256d051e9ea01277a1adb - languageName: node - linkType: hard - "micromark-util-decode-string@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-decode-string@npm:1.1.0" @@ -32812,18 +31646,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-decode-string@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-decode-string@npm:2.0.0" - dependencies: - decode-named-character-reference: ^1.0.0 - micromark-util-character: ^2.0.0 - micromark-util-decode-numeric-character-reference: ^2.0.0 - micromark-util-symbol: ^2.0.0 - checksum: a75daf32a4a6b549e9f19b4d833ebfeb09a32a9a1f9ce50f35dec6b6a3e4f9f121f49024ba7f9c91c55ebe792f7c7a332fc9604795181b6a612637df0df5b959 - languageName: node - linkType: hard - "micromark-util-encode@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-encode@npm:1.1.0" @@ -32831,13 +31653,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-encode@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-encode@npm:2.0.0" - checksum: 853a3f33fce72aaf4ffa60b7f2b6fcfca40b270b3466e1b96561b02185d2bd8c01dd7948bc31a24ac014f4cc854e545ca9a8e9cf7ea46262f9d24c9e88551c66 - languageName: node - linkType: hard - "micromark-util-html-tag-name@npm:^1.0.0": version: 1.2.0 resolution: "micromark-util-html-tag-name@npm:1.2.0" @@ -32845,13 +31660,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-html-tag-name@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-html-tag-name@npm:2.0.0" - checksum: d786d4486f93eb0ac5b628779809ca97c5dc60f3c9fc03eb565809831db181cf8cb7f05f9ac76852f3eb35461af0f89fa407b46f3a03f4f97a96754d8dc540d8 - languageName: node - linkType: hard - "micromark-util-normalize-identifier@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-normalize-identifier@npm:1.1.0" @@ -32861,15 +31669,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-normalize-identifier@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-normalize-identifier@npm:2.0.0" - dependencies: - micromark-util-symbol: ^2.0.0 - checksum: b36da2d3fd102053dadd953ce5c558328df12a63a8ac0e5aad13d4dda8e43b6a5d4a661baafe0a1cd8a260bead4b4a8e6e0e74193dd651e8484225bd4f4e68aa - languageName: node - linkType: hard - "micromark-util-resolve-all@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-resolve-all@npm:1.1.0" @@ -32879,15 +31678,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-resolve-all@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-resolve-all@npm:2.0.0" - dependencies: - micromark-util-types: ^2.0.0 - checksum: 31fe703b85572cb3f598ebe32750e59516925c7ff1f66cfe6afaebe0771a395a9eaa770787f2523d3c46082ea80e6c14f83643303740b3d650af7c96ebd30ccc - languageName: node - linkType: hard - "micromark-util-sanitize-uri@npm:^1.0.0, micromark-util-sanitize-uri@npm:^1.1.0": version: 1.2.0 resolution: "micromark-util-sanitize-uri@npm:1.2.0" @@ -32899,17 +31689,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-sanitize-uri@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-sanitize-uri@npm:2.0.0" - dependencies: - micromark-util-character: ^2.0.0 - micromark-util-encode: ^2.0.0 - micromark-util-symbol: ^2.0.0 - checksum: ea4c28bbffcf2430e9aff2d18554296789a8b0a1f54ac24020d1dde76624a7f93e8f2a83e88cd5a846b6d2c4287b71b1142d1b89fa7f1b0363a9b33711a141fe - languageName: node - linkType: hard - "micromark-util-subtokenize@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-subtokenize@npm:1.1.0" @@ -32922,18 +31701,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-subtokenize@npm:^2.0.0": - version: 2.0.1 - resolution: "micromark-util-subtokenize@npm:2.0.1" - dependencies: - devlop: ^1.0.0 - micromark-util-chunked: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: 5d338883ad8889c63f9b262b9cae0c02a42088201981d820ae7af7aa6d38fab6585b89fd4cf2206a46a7c4002e41ee6c70e1a3e0ceb3ad8b7adcffaf166b1511 - languageName: node - linkType: hard - "micromark-util-symbol@npm:^1.0.0": version: 1.1.0 resolution: "micromark-util-symbol@npm:1.1.0" @@ -32941,13 +31708,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-symbol@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-symbol@npm:2.0.0" - checksum: fa4a05bff575d9fbf0ad96a1013003e3bb6087ed6b34b609a141b6c0d2137b57df594aca409a95f4c5fda199f227b56a7d8b1f82cea0768df161d8a3a3660764 - languageName: node - linkType: hard - "micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1": version: 1.1.0 resolution: "micromark-util-types@npm:1.1.0" @@ -32955,13 +31715,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-types@npm:^2.0.0": - version: 2.0.0 - resolution: "micromark-util-types@npm:2.0.0" - checksum: 819fef3ab5770c37893d2a60381fb2694396c8d22803b6e103c830c3a1bc1490363c2b0470bb2acaaddad776dfbc2fc1fcfde39cb63c4f54d95121611672e3d0 - languageName: node - linkType: hard - "micromark@npm:^3.0.0": version: 3.2.0 resolution: "micromark@npm:3.2.0" @@ -32987,32 +31740,7 @@ __metadata: languageName: node linkType: hard -"micromark@npm:^4.0.0": - version: 4.0.0 - resolution: "micromark@npm:4.0.0" - dependencies: - "@types/debug": ^4.0.0 - debug: ^4.0.0 - decode-named-character-reference: ^1.0.0 - devlop: ^1.0.0 - micromark-core-commonmark: ^2.0.0 - micromark-factory-space: ^2.0.0 - micromark-util-character: ^2.0.0 - micromark-util-chunked: ^2.0.0 - micromark-util-combine-extensions: ^2.0.0 - micromark-util-decode-numeric-character-reference: ^2.0.0 - micromark-util-encode: ^2.0.0 - micromark-util-normalize-identifier: ^2.0.0 - micromark-util-resolve-all: ^2.0.0 - micromark-util-sanitize-uri: ^2.0.0 - micromark-util-subtokenize: ^2.0.0 - micromark-util-symbol: ^2.0.0 - micromark-util-types: ^2.0.0 - checksum: b84ab5ab1a0b28c063c52e9c2c9d7d44b954507235c10c9492d66e0b38f7de24bf298f914a1fbdf109f2a57a88cf0412de217c84cfac5fd60e3e42a74dbac085 - languageName: node - linkType: hard - -"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:^4.0.8, micromatch@npm:~4.0.7": +"micromatch@npm:^4.0.0, micromatch@npm:^4.0.2, micromatch@npm:^4.0.4, micromatch@npm:^4.0.5, micromatch@npm:~4.0.7": version: 4.0.8 resolution: "micromatch@npm:4.0.8" dependencies: @@ -33706,39 +32434,6 @@ __metadata: languageName: node linkType: hard -"msw@npm:2.4.0": - version: 2.4.0 - resolution: "msw@npm:2.4.0" - dependencies: - "@bundled-es-modules/cookie": ^2.0.0 - "@bundled-es-modules/statuses": ^1.0.1 - "@bundled-es-modules/tough-cookie": ^0.1.6 - "@inquirer/confirm": ^3.0.0 - "@mswjs/interceptors": ^0.29.0 - "@open-draft/until": ^2.1.0 - "@types/cookie": ^0.6.0 - "@types/statuses": ^2.0.4 - chalk: ^4.1.2 - headers-polyfill: ^4.0.2 - is-node-process: ^1.2.0 - outvariant: ^1.4.2 - path-to-regexp: ^6.2.0 - strict-event-emitter: ^0.5.1 - type-fest: ^4.9.0 - yargs: ^17.7.2 - peerDependencies: - typescript: ">= 4.7.x" - peerDependenciesMeta: - graphql: - optional: true - typescript: - optional: true - bin: - msw: cli/index.js - checksum: 0be990c56dc1f071bab37785807be99d8429abeb165f122ed47954a2513a5bcd2a0b1866390faed211cf8cd9232e93d9c5551075c59b3a1aa555cafa3a3233f6 - languageName: node - linkType: hard - "msw@npm:^1.0.0": version: 1.3.5 resolution: "msw@npm:1.3.5" @@ -33800,15 +32495,6 @@ __metadata: languageName: node linkType: hard -"mustache@npm:^4.2.0": - version: 4.2.0 - resolution: "mustache@npm:4.2.0" - bin: - mustache: bin/mustache - checksum: 928fcb63e3aa44a562bfe9b59ba202cccbe40a46da50be6f0dd831b495be1dd7e38ca4657f0ecab2c1a89dc7bccba0885eab7ee7c1b215830da765758c7e0506 - languageName: node - linkType: hard - "mute-stream@npm:0.0.8": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -33816,13 +32502,6 @@ __metadata: languageName: node linkType: hard -"mute-stream@npm:^1.0.0": - version: 1.0.0 - resolution: "mute-stream@npm:1.0.0" - checksum: 36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7 - languageName: node - linkType: hard - "mysql2@npm:^3.0.0": version: 3.11.3 resolution: "mysql2@npm:3.11.3" @@ -34068,7 +32747,7 @@ __metadata: languageName: node linkType: hard -"node-domexception@npm:1.0.0, node-domexception@npm:^1.0.0": +"node-domexception@npm:^1.0.0": version: 1.0.0 resolution: "node-domexception@npm:1.0.0" checksum: ee1d37dd2a4eb26a8a92cd6b64dfc29caec72bff5e1ed9aba80c294f57a31ba4895a60fd48347cf17dd6e766da0ae87d75657dfd1f384ebfa60462c2283f5c7f @@ -34695,28 +33374,6 @@ __metadata: languageName: node linkType: hard -"openai@npm:^4.52.6, openai@npm:^4.57.3": - version: 4.68.1 - resolution: "openai@npm:4.68.1" - dependencies: - "@types/node": ^18.11.18 - "@types/node-fetch": ^2.6.4 - abort-controller: ^3.0.0 - agentkeepalive: ^4.2.1 - form-data-encoder: 1.7.2 - formdata-node: ^4.3.2 - node-fetch: ^2.6.7 - peerDependencies: - zod: ^3.23.8 - peerDependenciesMeta: - zod: - optional: true - bin: - openai: bin/cli - checksum: 46b16c06b9e9698b02dcb222421bcd9f94b82436defb2412fbf0091cb0a7c68df06147e9f9cbec6ca036a6e96b4f139f03edff2f3de1289668cebea0a39d0d22 - languageName: node - linkType: hard - "openapi-backend@npm:^5.10.5": version: 5.11.0 resolution: "openapi-backend@npm:5.11.0" @@ -34887,7 +33544,7 @@ __metadata: languageName: node linkType: hard -"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0, outvariant@npm:^1.4.2": +"outvariant@npm:^1.2.1, outvariant@npm:^1.4.0": version: 1.4.3 resolution: "outvariant@npm:1.4.3" checksum: 4a3551fb2b45309e585eebf88bad094dbe56ac6d3a28d59dd2e4050b431aa2beb6097a0763fce3cd82ca0f077026f380a9b60fffc306aaf430141421e7a7b6ed @@ -35031,7 +33688,7 @@ __metadata: languageName: node linkType: hard -"p-retry@npm:4, p-retry@npm:^4.5.0": +"p-retry@npm:^4.5.0": version: 4.6.2 resolution: "p-retry@npm:4.6.2" dependencies: @@ -35176,22 +33833,6 @@ __metadata: languageName: node linkType: hard -"parse-entities@npm:^4.0.0": - version: 4.0.1 - resolution: "parse-entities@npm:4.0.1" - dependencies: - "@types/unist": ^2.0.0 - character-entities: ^2.0.0 - character-entities-legacy: ^3.0.0 - character-reference-invalid: ^2.0.0 - decode-named-character-reference: ^1.0.0 - is-alphanumerical: ^2.0.0 - is-decimal: ^2.0.0 - is-hexadecimal: ^2.0.0 - checksum: 32a6ff5b9acb9d2c4d71537308521fd265e685b9215691df73feedd9edfe041bb6da9f89bd0c35c4a2bc7d58e3e76e399bb6078c2fd7d2a343ff1dd46edbf1bd - languageName: node - linkType: hard - "parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" @@ -36640,7 +35281,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:15.8.1, prop-types@npm:15.x, prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.5.8, prop-types@npm:^15.6.0, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": +"prop-types@npm:15.8.1, prop-types@npm:15.x, prop-types@npm:^15.0.0, prop-types@npm:^15.5.10, prop-types@npm:^15.6.2, prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -37244,13 +35885,6 @@ __metadata: languageName: node linkType: hard -"react-display-name@npm:^0.2.4": - version: 0.2.5 - resolution: "react-display-name@npm:0.2.5" - checksum: ba27778c975e09afea2bfb58c4052b9e12121329a5115391564085ec64293f2b54d3408b841ad04600142c37d40493442676bf1124d0cc0e68f2f1e02762812b - languageName: node - linkType: hard - "react-docgen-typescript@npm:^2.2.2": version: 2.2.2 resolution: "react-docgen-typescript@npm:2.2.2" @@ -37314,19 +35948,6 @@ __metadata: languageName: node linkType: hard -"react-dropzone@npm:14.2.3": - version: 14.2.3 - resolution: "react-dropzone@npm:14.2.3" - dependencies: - attr-accept: ^2.2.2 - file-selector: ^0.6.0 - prop-types: ^15.8.1 - peerDependencies: - react: ">= 16.8 || 18.0.0" - checksum: 174b744d5ca898cf3d84ec1aeb6cef5211c446697e45dc8ece8287a03d291f8d07253206d5a1247ef156fd385d65e7de666d4d5c2986020b8543b8f2434e8b40 - languageName: node - linkType: hard - "react-dropzone@npm:9.0.0": version: 9.0.0 resolution: "react-dropzone@npm:9.0.0" @@ -37368,17 +35989,6 @@ __metadata: languageName: node linkType: hard -"react-error-boundary@npm:^3.1.0": - version: 3.1.4 - resolution: "react-error-boundary@npm:3.1.4" - dependencies: - "@babel/runtime": ^7.12.5 - peerDependencies: - react: ">=16.13.1" - checksum: f36270a5d775a25c8920f854c0d91649ceea417b15b5bc51e270a959b0476647bb79abb4da3be7dd9a4597b029214e8fe43ea914a7f16fa7543c91f784977f1b - languageName: node - linkType: hard - "react-error-overlay@npm:^6.0.11": version: 6.0.11 resolution: "react-error-overlay@npm:6.0.11" @@ -37500,27 +36110,6 @@ __metadata: languageName: node linkType: hard -"react-jss@npm:^10.10.0": - version: 10.10.0 - resolution: "react-jss@npm:10.10.0" - dependencies: - "@babel/runtime": ^7.3.1 - "@emotion/is-prop-valid": ^0.7.3 - css-jss: 10.10.0 - hoist-non-react-statics: ^3.2.0 - is-in-browser: ^1.1.3 - jss: 10.10.0 - jss-preset-default: 10.10.0 - prop-types: ^15.6.0 - shallow-equal: ^1.2.0 - theming: ^3.3.0 - tiny-warning: ^1.0.2 - peerDependencies: - react: ">=16.8.6" - checksum: d51ee9335a56c4881b8d36133c23336536cd4b200d17c04f8b3f53da2e8a5fc42707b63fe728c948c4d919a95bde0c1b9cdb600c7b89b1c30d0e0f611becad39 - languageName: node - linkType: hard - "react-lifecycles-compat@npm:^3.0.4": version: 3.0.4 resolution: "react-lifecycles-compat@npm:3.0.4" @@ -37554,27 +36143,6 @@ __metadata: languageName: node linkType: hard -"react-markdown@npm:^9.0.1": - version: 9.0.1 - resolution: "react-markdown@npm:9.0.1" - dependencies: - "@types/hast": ^3.0.0 - devlop: ^1.0.0 - hast-util-to-jsx-runtime: ^2.0.0 - html-url-attributes: ^3.0.0 - mdast-util-to-hast: ^13.0.0 - remark-parse: ^11.0.0 - remark-rehype: ^11.0.0 - unified: ^11.0.0 - unist-util-visit: ^5.0.0 - vfile: ^6.0.0 - peerDependencies: - "@types/react": ">=18" - react: ">=18" - checksum: ca1daa650d48b84a5a9771683cdb3f3d2d418247ce0faf73ede3207c65f2a21cdebb9df37afda67f6fc8f0f0a7b9ce00eb239781954a4d6c7ad88ea4df068add - languageName: node - linkType: hard - "react-measure@npm:^2.3.0": version: 2.5.2 resolution: "react-measure@npm:2.5.2" @@ -37799,15 +36367,6 @@ __metadata: languageName: node linkType: hard -"react-textarea-auto-witdth-height@npm:^1.0.3": - version: 1.0.3 - resolution: "react-textarea-auto-witdth-height@npm:1.0.3" - peerDependencies: - react: ">=16.8.0" - checksum: 28fb82496149c5a62eba31fc0665482bf82b1305da7e02f23915c0b35fa387b0ea0d8f66132b96e4f0121abce74f7c995e53817e1f844773c0e6c7ec4746d281 - languageName: node - linkType: hard - "react-textarea-autosize@npm:^8.3.2": version: 8.5.4 resolution: "react-textarea-autosize@npm:8.5.4" @@ -38281,20 +36840,6 @@ __metadata: languageName: node linkType: hard -"remark-gfm@npm:^4.0.0": - version: 4.0.0 - resolution: "remark-gfm@npm:4.0.0" - dependencies: - "@types/mdast": ^4.0.0 - mdast-util-gfm: ^3.0.0 - micromark-extension-gfm: ^3.0.0 - remark-parse: ^11.0.0 - remark-stringify: ^11.0.0 - unified: ^11.0.0 - checksum: 84bea84e388061fbbb697b4b666089f5c328aa04d19dc544c229b607446bc10902e46b67b9594415a1017bbbd7c811c1f0c30d36682c6d1a6718b66a1558261b - languageName: node - linkType: hard - "remark-parse@npm:^10.0.0": version: 10.0.2 resolution: "remark-parse@npm:10.0.2" @@ -38306,18 +36851,6 @@ __metadata: languageName: node linkType: hard -"remark-parse@npm:^11.0.0": - version: 11.0.0 - resolution: "remark-parse@npm:11.0.0" - dependencies: - "@types/mdast": ^4.0.0 - mdast-util-from-markdown: ^2.0.0 - micromark-util-types: ^2.0.0 - unified: ^11.0.0 - checksum: d83d245290fa84bb04fb3e78111f09c74f7417e7c012a64dd8dc04fccc3699036d828fbd8eeec8944f774b6c30cc1d925c98f8c46495ebcee7c595496342ab7f - languageName: node - linkType: hard - "remark-rehype@npm:^10.0.0": version: 10.1.0 resolution: "remark-rehype@npm:10.1.0" @@ -38330,19 +36863,6 @@ __metadata: languageName: node linkType: hard -"remark-rehype@npm:^11.0.0": - version: 11.1.1 - resolution: "remark-rehype@npm:11.1.1" - dependencies: - "@types/hast": ^3.0.0 - "@types/mdast": ^4.0.0 - mdast-util-to-hast: ^13.0.0 - unified: ^11.0.0 - vfile: ^6.0.0 - checksum: e199dff098ae6a560e13dd1778dec9c61440f979cc931c4ca4dcde0d58e51c0723243a901c1842379b189083cf4d74f224f57833738095ffa9535179d7cf3f01 - languageName: node - linkType: hard - "remark-slug@npm:^6.0.0": version: 6.1.0 resolution: "remark-slug@npm:6.1.0" @@ -38354,17 +36874,6 @@ __metadata: languageName: node linkType: hard -"remark-stringify@npm:^11.0.0": - version: 11.0.0 - resolution: "remark-stringify@npm:11.0.0" - dependencies: - "@types/mdast": ^4.0.0 - mdast-util-to-markdown: ^2.0.0 - unified: ^11.0.0 - checksum: 59e07460eb629d6c3b3c0f438b0b236e7e6858fd5ab770303078f5a556ec00354d9c7fb9ef6d5f745a4617ac7da1ab618b170fbb4dac120e183fecd9cc86bce6 - languageName: node - linkType: hard - "remarkable@npm:^2.0.1": version: 2.0.1 resolution: "remarkable@npm:2.0.1" @@ -39258,7 +37767,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:7.6.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0, semver@npm:^7.6.3": +"semver@npm:7.6.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": version: 7.6.3 resolution: "semver@npm:7.6.3" bin: @@ -39483,13 +37992,6 @@ __metadata: languageName: node linkType: hard -"shallow-equal@npm:^1.2.0": - version: 1.2.1 - resolution: "shallow-equal@npm:1.2.1" - checksum: 4f1645cc516e7754c4438db687e1da439a5f29a7dba2ba90c5f88e5708aeb17bc4355ba45cad805b0e95dc898e37d8bf6d77d854919c7512f89939986cff8cd1 - languageName: node - linkType: hard - "shebang-command@npm:^1.2.0": version: 1.2.0 resolution: "shebang-command@npm:1.2.0" @@ -40141,7 +38643,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:2.0.1, statuses@npm:^2.0.1": +"statuses@npm:2.0.1": version: 2.0.1 resolution: "statuses@npm:2.0.1" checksum: 18c7623fdb8f646fb213ca4051be4df7efb3484d4ab662937ca6fbef7ced9b9e12842709872eb3020cc3504b93bde88935c9f6417489627a7786f24f8031cbcb @@ -40375,13 +38877,6 @@ __metadata: languageName: node linkType: hard -"strict-event-emitter@npm:^0.5.1": - version: 0.5.1 - resolution: "strict-event-emitter@npm:0.5.1" - checksum: 350480431bc1c28fdb601ef4976c2f8155fc364b4740f9692dd03e5bdd48aafc99a5e021fe655fbd986d0b803e9f3fc5c4b018b35cb838c4690d60f2a26f1cf3 - languageName: node - linkType: hard - "strict-uri-encode@npm:^2.0.0": version: 2.0.0 resolution: "strict-uri-encode@npm:2.0.0" @@ -40546,16 +39041,6 @@ __metadata: languageName: node linkType: hard -"stringify-entities@npm:^4.0.0": - version: 4.0.4 - resolution: "stringify-entities@npm:4.0.4" - dependencies: - character-entities-html4: ^2.0.0 - character-entities-legacy: ^3.0.0 - checksum: ac1344ef211eacf6cf0a0a8feaf96f9c36083835b406560d2c6ff5a87406a41b13f2f0b4c570a3b391f465121c4fd6822b863ffb197e8c0601a64097862cc5b5 - languageName: node - linkType: hard - "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -40690,15 +39175,6 @@ __metadata: languageName: node linkType: hard -"style-to-object@npm:^1.0.0": - version: 1.0.8 - resolution: "style-to-object@npm:1.0.8" - dependencies: - inline-style-parser: 0.2.4 - checksum: 80ca4773fc728d7919edc552eb46bab11aa8cdd0b426528ee8b817ba6872ea7b9d38fbb97b6443fd2d4895a4c4b02ec32765387466a302d0b4d1b91deab1e1a0 - languageName: node - linkType: hard - "style-value-types@npm:5.0.0": version: 5.0.0 resolution: "style-value-types@npm:5.0.0" @@ -40949,7 +39425,7 @@ __metadata: languageName: node linkType: hard -"symbol-observable@npm:^1.0.4, symbol-observable@npm:^1.2.0": +"symbol-observable@npm:^1.0.4": version: 1.2.0 resolution: "symbol-observable@npm:1.2.0" checksum: 48ffbc22e3d75f9853b3ff2ae94a44d84f386415110aea5effc24d84c502e03a4a6b7a8f75ebaf7b585780bda34eb5d6da3121f826a6f93398429d30032971b6 @@ -41263,20 +39739,6 @@ __metadata: languageName: node linkType: hard -"theming@npm:^3.3.0": - version: 3.3.0 - resolution: "theming@npm:3.3.0" - dependencies: - hoist-non-react-statics: ^3.3.0 - prop-types: ^15.5.8 - react-display-name: ^0.2.4 - tiny-warning: ^1.0.2 - peerDependencies: - react: ">=16.3" - checksum: cb85008983d53460c26761edde2b264d825d15efc455ed32299a76c7a21425607bf53019f0d5d05699569ce808fb8ec1e4c3e56a41ebbf2c80c27358288c315f - languageName: node - linkType: hard - "thenify-all@npm:^1.0.0": version: 1.6.0 resolution: "thenify-all@npm:1.6.0" @@ -41490,7 +39952,7 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^4.0.0, tough-cookie@npm:^4.1.2, tough-cookie@npm:^4.1.4": +"tough-cookie@npm:^4.0.0, tough-cookie@npm:^4.1.2": version: 4.1.4 resolution: "tough-cookie@npm:4.1.4" dependencies: @@ -42013,7 +40475,7 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.4.0, type-fest@npm:^4.9.0": +"type-fest@npm:^4.4.0": version: 4.26.1 resolution: "type-fest@npm:4.26.1" checksum: 7188db3bca82afa62c69a8043fb7c5eb74e63c45e7e28efb986da1629d844286f7181bc5a8185f38989fffff0d6c96be66fd13529b01932d1b6ebe725181d31a @@ -42530,21 +40992,6 @@ __metadata: languageName: node linkType: hard -"unified@npm:^11.0.0": - version: 11.0.5 - resolution: "unified@npm:11.0.5" - dependencies: - "@types/unist": ^3.0.0 - bail: ^2.0.0 - devlop: ^1.0.0 - extend: ^3.0.0 - is-plain-obj: ^4.0.0 - trough: ^2.0.0 - vfile: ^6.0.0 - checksum: b3bf7fd6f568cc261e074dae21188483b0f2a8ab858d62e6e85b75b96cc655f59532906ae3c64d56a9b257408722d71f1d4135292b3d7ee02907c8b592fb3cf0 - languageName: node - linkType: hard - "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -42595,15 +41042,6 @@ __metadata: languageName: node linkType: hard -"unist-util-is@npm:^6.0.0": - version: 6.0.0 - resolution: "unist-util-is@npm:6.0.0" - dependencies: - "@types/unist": ^3.0.0 - checksum: f630a925126594af9993b091cf807b86811371e465b5049a6283e08537d3e6ba0f7e248e1e7dab52cfe33f9002606acef093441137181b327f6fe504884b20e2 - languageName: node - linkType: hard - "unist-util-position@npm:^4.0.0": version: 4.0.4 resolution: "unist-util-position@npm:4.0.4" @@ -42613,15 +41051,6 @@ __metadata: languageName: node linkType: hard -"unist-util-position@npm:^5.0.0": - version: 5.0.0 - resolution: "unist-util-position@npm:5.0.0" - dependencies: - "@types/unist": ^3.0.0 - checksum: f89b27989b19f07878de9579cd8db2aa0194c8360db69e2c99bd2124a480d79c08f04b73a64daf01a8fb3af7cba65ff4b45a0b978ca243226084ad5f5d441dde - languageName: node - linkType: hard - "unist-util-stringify-position@npm:^3.0.0": version: 3.0.3 resolution: "unist-util-stringify-position@npm:3.0.3" @@ -42631,15 +41060,6 @@ __metadata: languageName: node linkType: hard -"unist-util-stringify-position@npm:^4.0.0": - version: 4.0.0 - resolution: "unist-util-stringify-position@npm:4.0.0" - dependencies: - "@types/unist": ^3.0.0 - checksum: e2e7aee4b92ddb64d314b4ac89eef7a46e4c829cbd3ee4aee516d100772b490eb6b4974f653ba0717a0071ca6ea0770bf22b0a2ea62c65fcba1d071285e96324 - languageName: node - linkType: hard - "unist-util-visit-parents@npm:^3.0.0": version: 3.1.1 resolution: "unist-util-visit-parents@npm:3.1.1" @@ -42660,16 +41080,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit-parents@npm:^6.0.0": - version: 6.0.1 - resolution: "unist-util-visit-parents@npm:6.0.1" - dependencies: - "@types/unist": ^3.0.0 - unist-util-is: ^6.0.0 - checksum: 08927647c579f63b91aafcbec9966dc4a7d0af1e5e26fc69f4e3e6a01215084835a2321b06f3cbe7bf7914a852830fc1439f0fc3d7153d8804ac3ef851ddfa20 - languageName: node - linkType: hard - "unist-util-visit@npm:^2.0.0": version: 2.0.3 resolution: "unist-util-visit@npm:2.0.3" @@ -42692,17 +41102,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:^5.0.0": - version: 5.0.0 - resolution: "unist-util-visit@npm:5.0.0" - dependencies: - "@types/unist": ^3.0.0 - unist-util-is: ^6.0.0 - unist-util-visit-parents: ^6.0.0 - checksum: 9ec42e618e7e5d0202f3c191cd30791b51641285732767ee2e6bcd035931032e3c1b29093f4d7fd0c79175bbc1f26f24f26ee49770d32be76f8730a652a857e6 - languageName: node - linkType: hard - "universal-github-app-jwt@npm:^1.1.1, universal-github-app-jwt@npm:^1.1.2": version: 1.2.0 resolution: "universal-github-app-jwt@npm:1.2.0" @@ -43043,15 +41442,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^10.0.0": - version: 10.0.0 - resolution: "uuid@npm:10.0.0" - bin: - uuid: dist/bin/uuid - checksum: 4b81611ade2885d2313ddd8dc865d93d8dccc13ddf901745edca8f86d99bc46d7a330d678e7532e7ebf93ce616679fb19b2e3568873ac0c14c999032acb25869 - languageName: node - linkType: hard - "uuid@npm:^3.3.2, uuid@npm:^3.4.0": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -43210,16 +41600,6 @@ __metadata: languageName: node linkType: hard -"vfile-message@npm:^4.0.0": - version: 4.0.2 - resolution: "vfile-message@npm:4.0.2" - dependencies: - "@types/unist": ^3.0.0 - unist-util-stringify-position: ^4.0.0 - checksum: 964e7e119f4c0e0270fc269119c41c96da20afa01acb7c9809a88365c8e0c64aa692fafbd952669382b978002ecd7ad31ef4446d85e8a22cdb62f6df20186c2d - languageName: node - linkType: hard - "vfile@npm:^5.0.0": version: 5.3.7 resolution: "vfile@npm:5.3.7" @@ -43232,16 +41612,6 @@ __metadata: languageName: node linkType: hard -"vfile@npm:^6.0.0": - version: 6.0.3 - resolution: "vfile@npm:6.0.3" - dependencies: - "@types/unist": ^3.0.0 - vfile-message: ^4.0.0 - checksum: 152b6729be1af70df723efb65c1a1170fd483d41086557da3651eea69a1dd1f0c22ea4344834d56d30734b9185bcab63e22edc81d3f0e9bed8aa4660d61080af - languageName: node - linkType: hard - "victory-area@npm:^37.1.1": version: 37.2.0 resolution: "victory-area@npm:37.2.0" @@ -43824,13 +42194,6 @@ __metadata: languageName: node linkType: hard -"web-streams-polyfill@npm:4.0.0-beta.3": - version: 4.0.0-beta.3 - resolution: "web-streams-polyfill@npm:4.0.0-beta.3" - checksum: dfec1fbf52b9140e4183a941e380487b6c3d5d3838dd1259be81506c1c9f2abfcf5aeb670aeeecfd9dff4271a6d8fef931b193c7bedfb42542a3b05ff36c0d16 - languageName: node - linkType: hard - "web-streams-polyfill@npm:^3.0.3": version: 3.3.3 resolution: "web-streams-polyfill@npm:3.3.3" @@ -44467,7 +42830,7 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^6.0.1, wrap-ansi@npm:^6.2.0": +"wrap-ansi@npm:^6.0.1": version: 6.2.0 resolution: "wrap-ansi@npm:6.2.0" dependencies: @@ -44904,13 +43267,6 @@ __metadata: languageName: node linkType: hard -"yoctocolors-cjs@npm:^2.1.2": - version: 2.1.2 - resolution: "yoctocolors-cjs@npm:2.1.2" - checksum: 1c474d4b30a8c130e679279c5c2c33a0d48eba9684ffa0252cc64846c121fb56c3f25457fef902edbe1e2d7a7872130073a9fc8e795299d75e13fa3f5f548f1b - languageName: node - linkType: hard - "yup@npm:^0.32.11": version: 0.32.11 resolution: "yup@npm:0.32.11" @@ -44985,7 +43341,7 @@ __metadata: languageName: node linkType: hard -"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4, zod-to-json-schema@npm:^3.22.3": +"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": version: 3.23.3 resolution: "zod-to-json-schema@npm:3.23.3" peerDependencies: