From bb52c88727fac92f95defc3f50606d7e4ba3df79 Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Wed, 21 Aug 2024 12:25:34 -0700 Subject: [PATCH 001/138] feat(secrets): add commands to register, update, and remove OpenAI API keys with the VSCode SecretsStorage API --- package.json | 12 +++++++ src/commands/secrets/cmds.ts | 56 ++++++++++++++++++++++++++++++++ src/commands/secrets/registry.ts | 50 ++++++++++++++++++++++++++++ src/services/ZenExtension.ts | 2 ++ 4 files changed, 120 insertions(+) create mode 100644 src/commands/secrets/cmds.ts create mode 100644 src/commands/secrets/registry.ts diff --git a/package.json b/package.json index 2c9051e5..fe1d0a1c 100644 --- a/package.json +++ b/package.json @@ -297,6 +297,18 @@ "title": "Restart LSP Server", "icon": "$(debug-restart)", "category": "ZenML Environment" + }, + { + "command": "zenml.registerOpenAIAPIKey", + "title": "Register OpenAI API Key", + "icon": "$(add)", + "category": "ZenML Secrets" + }, + { + "command": "zenml.deleteOpenAIAPIKey", + "title": "Delete OpenAI API Key", + "icon": "$(trash)", + "category": "ZenML Secrets" } ], "viewsContainers": { diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts new file mode 100644 index 00000000..b68ad9b9 --- /dev/null +++ b/src/commands/secrets/cmds.ts @@ -0,0 +1,56 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. + +import * as vscode from 'vscode'; +import type { ExtensionContext } from 'vscode'; + +// TODO I don't think retrieval of an api key will live in here + +const registerOpenAIAPIKey = async (context: ExtensionContext) => { + let apiKey = await context.secrets.get('OPENAI_API_KEY'); + + if (apiKey) { + apiKey = await vscode.window.showInputBox({ + prompt: 'OpenAI API Key already exists, enter a new value to update.', + password: true, + }); + } else { + apiKey = await vscode.window.showInputBox({ + prompt: 'Please enter your OpenAI API key', + password: true, + }); + } + + if (apiKey === undefined) { + return undefined; + } + + await context.secrets.store('OPENAI_API_KEY', apiKey); + vscode.window.showInformationMessage('OpenAI API key stored successfully.'); +}; + +const deleteOpenAIAPIKey = async (context: ExtensionContext) => { + const apiKey = await context.secrets.get('OPENAI_API_KEY'); + + if (apiKey === undefined) { + vscode.window.showInformationMessage('No OpenAI API key exists.'); + return; + } + await context.secrets.delete('OPENAI_API_KEY'); + vscode.window.showInformationMessage('OpenAI API key successfully removed.'); +}; + +export const secretsCommands = { + registerOpenAIAPIKey, + deleteOpenAIAPIKey, +}; diff --git a/src/commands/secrets/registry.ts b/src/commands/secrets/registry.ts new file mode 100644 index 00000000..951599ec --- /dev/null +++ b/src/commands/secrets/registry.ts @@ -0,0 +1,50 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. + +// TODO the registration of secrets commands should go in here, which is then imported into the ZenExtension file +// In registry, the "register command thing is passed context (i.e. context.secrets) as an arg" + +import { secretsCommands } from './cmds'; +import { registerCommand } from '../../common/vscodeapi'; +import { ZenExtension } from '../../services/ZenExtension'; +import { ExtensionContext, commands } from 'vscode'; + +/** + * Registers pipeline-related commands for the extension. + * + * @param {ExtensionContext} context - The context in which the extension operates, used for registering commands and managing their lifecycle. + */ +export const registerSecretsCommands = (context: ExtensionContext) => { + try { + const registeredCommands = [ + registerCommand( + 'zenml.registerOpenAIAPIKey', + async () => await secretsCommands.registerOpenAIAPIKey(context) + ), + registerCommand( + 'zenml.deleteOpenAIAPIKey', + async () => await secretsCommands.deleteOpenAIAPIKey(context) + ), + ]; + + registeredCommands.forEach(cmd => { + context.subscriptions.push(cmd); + ZenExtension.commandDisposables.push(cmd); + }); + + commands.executeCommand('setContext', 'secretsCommandsRegistered', true); + } catch (error) { + console.error('Error registering pipeline commands:', error); + commands.executeCommand('setContext', 'secretsCommandsRegistered', false); + } +}; diff --git a/src/services/ZenExtension.ts b/src/services/ZenExtension.ts index 43a0f8e7..68e5f6eb 100644 --- a/src/services/ZenExtension.ts +++ b/src/services/ZenExtension.ts @@ -17,6 +17,7 @@ import * as vscode from 'vscode'; import { registerPipelineCommands } from '../commands/pipelines/registry'; import { registerServerCommands } from '../commands/server/registry'; import { registerStackCommands } from '../commands/stack/registry'; +import { registerSecretsCommands } from '../commands/secrets/registry'; import { EXTENSION_ROOT_DIR } from '../common/constants'; import { registerLogger, traceLog, traceVerbose } from '../common/log/logging'; import { @@ -73,6 +74,7 @@ export class ZenExtension { registerStackCommands, registerComponentCommands, registerPipelineCommands, + registerSecretsCommands, ]; /** From 5fc0003953afc339cb705498c84dd1e30d76bf5f Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Wed, 21 Aug 2024 12:44:19 -0700 Subject: [PATCH 002/138] feat(secrets): add function to retrieve secrets from VSCode --- src/common/vscodeapi.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/common/vscodeapi.ts b/src/common/vscodeapi.ts index ff2463bd..da826b46 100644 --- a/src/common/vscodeapi.ts +++ b/src/common/vscodeapi.ts @@ -18,6 +18,7 @@ import { ConfigurationScope, Disposable, DocumentSelector, + ExtensionContext, languages, LanguageStatusItem, LogOutputChannel, @@ -69,3 +70,14 @@ export function createLanguageStatusItem( ): LanguageStatusItem { return languages.createLanguageStatusItem(id, selector); } + +export async function getSecret(context: ExtensionContext, key: string) { + const secret = await context.secrets.get(key); + + if (secret === undefined) { + console.error(`The requested secret with key '${key}' does not exist.`); + return; + } + + return secret; +} From a0de734c62869122064fe7281c6a2763b899f67d Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Thu, 22 Aug 2024 10:58:04 -0700 Subject: [PATCH 003/138] feat: remove unnecessary deleteOpenAIKey function --- package.json | 6 ------ src/commands/secrets/cmds.ts | 12 ------------ src/commands/secrets/registry.ts | 4 ---- 3 files changed, 22 deletions(-) diff --git a/package.json b/package.json index fe1d0a1c..73b7ecfb 100644 --- a/package.json +++ b/package.json @@ -303,12 +303,6 @@ "title": "Register OpenAI API Key", "icon": "$(add)", "category": "ZenML Secrets" - }, - { - "command": "zenml.deleteOpenAIAPIKey", - "title": "Delete OpenAI API Key", - "icon": "$(trash)", - "category": "ZenML Secrets" } ], "viewsContainers": { diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index b68ad9b9..faf0ebf3 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -39,18 +39,6 @@ const registerOpenAIAPIKey = async (context: ExtensionContext) => { vscode.window.showInformationMessage('OpenAI API key stored successfully.'); }; -const deleteOpenAIAPIKey = async (context: ExtensionContext) => { - const apiKey = await context.secrets.get('OPENAI_API_KEY'); - - if (apiKey === undefined) { - vscode.window.showInformationMessage('No OpenAI API key exists.'); - return; - } - await context.secrets.delete('OPENAI_API_KEY'); - vscode.window.showInformationMessage('OpenAI API key successfully removed.'); -}; - export const secretsCommands = { registerOpenAIAPIKey, - deleteOpenAIAPIKey, }; diff --git a/src/commands/secrets/registry.ts b/src/commands/secrets/registry.ts index 951599ec..c63ed07f 100644 --- a/src/commands/secrets/registry.ts +++ b/src/commands/secrets/registry.ts @@ -31,10 +31,6 @@ export const registerSecretsCommands = (context: ExtensionContext) => { 'zenml.registerOpenAIAPIKey', async () => await secretsCommands.registerOpenAIAPIKey(context) ), - registerCommand( - 'zenml.deleteOpenAIAPIKey', - async () => await secretsCommands.deleteOpenAIAPIKey(context) - ), ]; registeredCommands.forEach(cmd => { From 682417499e9bde0ae222742b00bfc575f5109f1d Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Fri, 23 Aug 2024 12:56:17 -0700 Subject: [PATCH 004/138] feat: generalize LLM API Key registry command, support for Anthropic, Gemini, OpenAI --- package.json | 4 ++-- src/commands/secrets/cmds.ts | 37 ++++++++++++++++++++++++-------- src/commands/secrets/registry.ts | 4 ++-- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 73b7ecfb..6071c824 100644 --- a/package.json +++ b/package.json @@ -299,8 +299,8 @@ "category": "ZenML Environment" }, { - "command": "zenml.registerOpenAIAPIKey", - "title": "Register OpenAI API Key", + "command": "zenml.registerLLMAPIKey", + "title": "Register LLM API Key", "icon": "$(add)", "category": "ZenML Secrets" } diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index faf0ebf3..e35e06e1 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -14,31 +14,50 @@ import * as vscode from 'vscode'; import type { ExtensionContext } from 'vscode'; -// TODO I don't think retrieval of an api key will live in here +const registerLLMAPIKey = async (context: ExtensionContext) => { + const options: vscode.QuickPickItem[] = [ + { label: 'Anthropic' }, + { label: 'Gemini' }, + { label: 'OpenAI' }, + ]; -const registerOpenAIAPIKey = async (context: ExtensionContext) => { - let apiKey = await context.secrets.get('OPENAI_API_KEY'); + const selectedOption = await vscode.window.showQuickPick(options, { + placeHolder: 'Please select an LLM.', + canPickMany: false, + }); + + if (selectedOption === undefined) { + vscode.window.showWarningMessage('API key input was canceled.'); + return; + } + + const model = selectedOption.label; + const secretKey = `${model.toUpperCase()}_API_KEY`; + + let apiKey = await context.secrets.get(secretKey); + console.log(secretKey, apiKey); if (apiKey) { apiKey = await vscode.window.showInputBox({ - prompt: 'OpenAI API Key already exists, enter a new value to update.', + prompt: `${model} API Key already exists, enter a new value to update.`, password: true, }); } else { apiKey = await vscode.window.showInputBox({ - prompt: 'Please enter your OpenAI API key', + prompt: `Please enter your ${model} API key`, password: true, }); } if (apiKey === undefined) { - return undefined; + vscode.window.showWarningMessage('API key input was canceled.'); + return; } - await context.secrets.store('OPENAI_API_KEY', apiKey); - vscode.window.showInformationMessage('OpenAI API key stored successfully.'); + await context.secrets.store(secretKey, apiKey); + vscode.window.showInformationMessage(`${model} API key stored successfully.`); }; export const secretsCommands = { - registerOpenAIAPIKey, + registerLLMAPIKey, }; diff --git a/src/commands/secrets/registry.ts b/src/commands/secrets/registry.ts index c63ed07f..df0b5fce 100644 --- a/src/commands/secrets/registry.ts +++ b/src/commands/secrets/registry.ts @@ -28,8 +28,8 @@ export const registerSecretsCommands = (context: ExtensionContext) => { try { const registeredCommands = [ registerCommand( - 'zenml.registerOpenAIAPIKey', - async () => await secretsCommands.registerOpenAIAPIKey(context) + 'zenml.registerLLMAPIKey', + async () => await secretsCommands.registerLLMAPIKey(context) ), ]; From e1a363f593bdeaadcb850735f8042790032db7a7 Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Fri, 23 Aug 2024 12:59:30 -0700 Subject: [PATCH 005/138] chore: remove todo comments --- src/commands/secrets/registry.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/commands/secrets/registry.ts b/src/commands/secrets/registry.ts index df0b5fce..af861a6b 100644 --- a/src/commands/secrets/registry.ts +++ b/src/commands/secrets/registry.ts @@ -11,16 +11,13 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. -// TODO the registration of secrets commands should go in here, which is then imported into the ZenExtension file -// In registry, the "register command thing is passed context (i.e. context.secrets) as an arg" - import { secretsCommands } from './cmds'; import { registerCommand } from '../../common/vscodeapi'; import { ZenExtension } from '../../services/ZenExtension'; import { ExtensionContext, commands } from 'vscode'; /** - * Registers pipeline-related commands for the extension. + * Registers secrets related commands for the extension. * * @param {ExtensionContext} context - The context in which the extension operates, used for registering commands and managing their lifecycle. */ @@ -40,7 +37,7 @@ export const registerSecretsCommands = (context: ExtensionContext) => { commands.executeCommand('setContext', 'secretsCommandsRegistered', true); } catch (error) { - console.error('Error registering pipeline commands:', error); + console.error('Error registering secrets commands:', error); commands.executeCommand('setContext', 'secretsCommandsRegistered', false); } }; From c1933566c73fa2c23a280ebb7c0d0f1ef2eaa765 Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Fri, 23 Aug 2024 13:04:22 -0700 Subject: [PATCH 006/138] chore: address coderabbitai suggestions --- src/commands/secrets/cmds.ts | 3 +-- src/common/vscodeapi.ts | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index e35e06e1..99514123 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -28,14 +28,13 @@ const registerLLMAPIKey = async (context: ExtensionContext) => { if (selectedOption === undefined) { vscode.window.showWarningMessage('API key input was canceled.'); - return; + return undefined; } const model = selectedOption.label; const secretKey = `${model.toUpperCase()}_API_KEY`; let apiKey = await context.secrets.get(secretKey); - console.log(secretKey, apiKey); if (apiKey) { apiKey = await vscode.window.showInputBox({ diff --git a/src/common/vscodeapi.ts b/src/common/vscodeapi.ts index da826b46..5cdeaf58 100644 --- a/src/common/vscodeapi.ts +++ b/src/common/vscodeapi.ts @@ -76,7 +76,7 @@ export async function getSecret(context: ExtensionContext, key: string) { if (secret === undefined) { console.error(`The requested secret with key '${key}' does not exist.`); - return; + return undefined; } return secret; From e7f62ef5542e59d8d985d5f434b45626e23fefcb Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 20 Aug 2024 17:25:24 -0400 Subject: [PATCH 007/138] feat: added panel to extension --- .python-version | 1 + package.json | 4 ++ src/services/ZenExtension.ts | 2 + .../chatbotView/chatbotDataProvider.ts | 43 +++++++++++++++++++ .../activityBar/chatbotView/chatbotItem.ts | 17 ++++++++ 5 files changed, 67 insertions(+) create mode 100644 .python-version create mode 100644 src/views/activityBar/chatbotView/chatbotDataProvider.ts create mode 100644 src/views/activityBar/chatbotView/chatbotItem.ts diff --git a/.python-version b/.python-version new file mode 100644 index 00000000..c024ac33 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +zenml-env-3.10.0 diff --git a/package.json b/package.json index 2c9051e5..4bf5a8b1 100644 --- a/package.json +++ b/package.json @@ -341,6 +341,10 @@ "id": "zenmlEnvironmentView", "name": "Environment", "icon": "$(server-environment)" + }, + { + "id": "zenmlChatbotView", + "name": "Chatbot" } ], "zenmlPanel": [ diff --git a/src/services/ZenExtension.ts b/src/services/ZenExtension.ts index 43a0f8e7..6091e838 100644 --- a/src/services/ZenExtension.ts +++ b/src/services/ZenExtension.ts @@ -42,6 +42,7 @@ import { LSClient } from './LSClient'; import { toggleCommands } from '../utils/global'; import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; +import { ChatbotDataProvider } from '../views/activityBar/chatbotView/chatbotDataProvider' import { registerComponentCommands } from '../commands/components/registry'; export interface IServerInfo { @@ -66,6 +67,7 @@ export class ZenExtension { ['zenmlComponentView', ComponentDataProvider.getInstance()], ['zenmlPipelineView', PipelineDataProvider.getInstance()], ['zenmlPanelView', PanelDataProvider.getInstance()], + // ['zenmlChatbotView', ChatbotDataProvider.getInstance()], ]); private static registries = [ diff --git a/src/views/activityBar/chatbotView/chatbotDataProvider.ts b/src/views/activityBar/chatbotView/chatbotDataProvider.ts new file mode 100644 index 00000000..1157cee9 --- /dev/null +++ b/src/views/activityBar/chatbotView/chatbotDataProvider.ts @@ -0,0 +1,43 @@ +import { EventEmitter, TreeDataProvider, TreeItem, window } from 'vscode'; +import { chatbotItem } from './chatbotItem'; + +export class ChatbotDataProvider implements TreeDataProvider { + private _onDidChangeTreeData = new EventEmitter(); + readonly onDidChangeTreeData = this._onDidChangeTreeData.event; + + private inputText: string = ''; + + constructor() { + // Initialize the input box for user text input + this.showInputBox(); + } + + showInputBox() { + window.showInputBox({ prompt: 'Enter text here' }).then(value => { + if (value) { + this.inputText = value; + this.refresh(); + } + }); + } + + refresh(): void { + this._onDidChangeTreeData.fire(); + } + + getTreeItem(element: chatbotItem): TreeItem { + return element; + } + + getChildren(element?: chatbotItem): Thenable { + if (!element) { + // Root items with collapsible states + return Promise.resolve([ + new chatbotItem('Collapsible Section 1', 'Description 1', true), + new chatbotItem('Collapsible Section 2', 'Description 2', true), + new chatbotItem(`User Input: ${this.inputText}`, '', false) + ]); + } + return Promise.resolve([]); + } +} diff --git a/src/views/activityBar/chatbotView/chatbotItem.ts b/src/views/activityBar/chatbotView/chatbotItem.ts new file mode 100644 index 00000000..4537437c --- /dev/null +++ b/src/views/activityBar/chatbotView/chatbotItem.ts @@ -0,0 +1,17 @@ +import { TreeItem, TreeItemCollapsibleState } from 'vscode'; + +export class chatbotItem extends TreeItem { + constructor( + public readonly label: string, + public readonly description: string, + public readonly isCollapsible: boolean + ) { + super( + label, + isCollapsible + ? TreeItemCollapsibleState.Collapsed + : TreeItemCollapsibleState.None + ); + this.tooltip = `${this.label} - ${this.description}`; + } +} From 32bdbd3f3d397305156bbe9e4d8e1bd4acb0e5df Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Wed, 21 Aug 2024 14:55:24 -0400 Subject: [PATCH 008/138] feat: added chat to side-bar. Co-authored-by: Nathaniel Xu Co-authored-by: Alan Cho --- media/chat.css | 47 +++++++++++ media/chat.js | 24 ++++++ package.json | 14 +++- resources/chat.png | Bin 0 -> 2008 bytes src/extension.ts | 73 ++++++++++++++++++ src/services/ZenExtension.ts | 2 - .../chatbotView/chatbotDataProvider.ts | 43 ----------- .../activityBar/chatbotView/chatbotItem.ts | 17 ---- 8 files changed, 156 insertions(+), 64 deletions(-) create mode 100644 media/chat.css create mode 100644 media/chat.js create mode 100644 resources/chat.png delete mode 100644 src/views/activityBar/chatbotView/chatbotDataProvider.ts delete mode 100644 src/views/activityBar/chatbotView/chatbotItem.ts diff --git a/media/chat.css b/media/chat.css new file mode 100644 index 00000000..dbdf736f --- /dev/null +++ b/media/chat.css @@ -0,0 +1,47 @@ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #0000007a; +} + +#chat-container { + display: flex; + flex-direction: column; + height: 100vh; +} + +#messages { + flex: 1; + overflow-y: auto; + padding: 10px; + background-color: rgb(238, 226, 226); + border-bottom: 1px solid #030303; +} + +.message { + padding: 5px; + margin: 5px 0; + background-color: #f7f7f7b7; + border-radius: 5px; +} + +#input-container { + display: flex; + padding: 10px; + background-color: #2c2c2c70; + border-top: 1px solid #1a1919e7; +} + +#input { + flex: 1; + padding: 5px; + font-size: 14px; +} + +#send { + margin-left: 10px; + padding: 5px 10px; + font-size: 14px; + cursor: pointer; +} diff --git a/media/chat.js b/media/chat.js new file mode 100644 index 00000000..a4f090b8 --- /dev/null +++ b/media/chat.js @@ -0,0 +1,24 @@ +(function() { + const vscode = acquireVsCodeApi(); + + document.getElementById('send').addEventListener('click', () => { + const input = document.getElementById('input'); + if (input.value.trim()) { + const message = input.value; + input.value = ''; + + // Post the message back to the extension + vscode.postMessage({ + command: 'sendMessage', + text: message + }); + + // Append the message to the chat log + const messagesDiv = document.getElementById('messages'); + const messageDiv = document.createElement('div'); + messageDiv.className = 'message'; + messageDiv.textContent = message; + messagesDiv.appendChild(messageDiv); + } + }); +})(); diff --git a/package.json b/package.json index 4bf5a8b1..ae22adbf 100644 --- a/package.json +++ b/package.json @@ -141,6 +141,10 @@ } }, "commands": [ + { + "command": "zenml.openChat", + "title": "Open ZenML Chat" + }, { "command": "zenml.promptForInterpreter", "title": "Select Python Interpreter", @@ -343,8 +347,9 @@ "icon": "$(server-environment)" }, { - "id": "zenmlChatbotView", - "name": "Chatbot" + "id": "zenmlChatView", + "name": "Chat", + "type": "webview" } ], "zenmlPanel": [ @@ -356,6 +361,11 @@ }, "menus": { "view/title": [ + { + "command": "zenml.openChat", + "when": "view == zenmlChatView", + "group": "navigation" + }, { "when": "serverCommandsRegistered && view == zenmlServerView", "command": "zenml.connectServer", diff --git a/resources/chat.png b/resources/chat.png new file mode 100644 index 0000000000000000000000000000000000000000..9d8e7fc1371a0e8a8be2f42ba523be1d1d7b0cdb GIT binary patch literal 2008 zcmah}dpHvc8z&trD#luIfk#ges2F)6o-K73uMIbD1%nM*oFIyxwe zY9t(SD$8b=-LH1v>^>Xo94eiCUwwan@AG@!=Y8Ji_xzss_Pj-h5A9!U2ePxWvRWK= zU~jaQl{Lf?6BgK5e6lqp#gd3Qd=M7;jzoPwOdA-Xe;8$aATtKZO#CQ)V1zbI<51Y+ ztZ|80u2QIVO08b4o7CthRa%2mqn9bQ;}QjfCn7WXBr1E5%orG^k!Tz`S16rO>nEos zP1D9Hv%xg2n>49(2C+gtL}gE!%#%}QrPja`$wwJH{0LoW5i%GJ#wm_aCY{jejZ^wb zlT4|hvIXx+)DZ?(qEIvWVj_jb7b`eIDUB;+3nYXw7Lmdh$&?1;^cZV=jK#-~GB^V1 z`(f%Zojak{NhZ`5Eh;L;3UJ%DRPr99I->;y-L_AZFb{4qOVdHeVfXHn_&{8VS4E|sKk~Cp-hdZo zbYl`VAo_86UB5T^9qa3law@v#>GLW)Kn&hBbEPk_rC}@(xXQ~&a_YA}){+de+rU&i zy;)wPxw8%g0w&a*jK|*$?D@w4%RTA|)?B(lb)$$VTGyn00Cu~M@BCb;P!n_RE^4SU z?R1j2xF@4lteHWfkvctQ6Pwl@w!@{nD)98#_01dJGv>B#iPFy$haKQnE`y_8-9FhK z>S-zuMLEoq>O5ecmB}x{^Ly((?c*IW_dvoJOoRsts^fMkrjTnRVrjK*W4s4lnZ6jW?B^t(<3oT$C!j+{tcJ5S z)6-nCFEaB(cT?omx!FqsS16J-ad!GeQuA!{T+%D~bct`j%~71q$M%r;+kL60z^*-= z5zn{a24jhi2>#32)JwMcX)`qt%66WA3HEtYn&{d?CxRav0g%KdNPj!GQtc4uK-|ZE z`6WeCv9F3asuGOxO447M2_LmSj~M~}lur?eVw87xVfhH|1Y$kbt75#0MfKv`^hfN+ zFUGBEJblg~G1T)9O+-gm?hXJ7({T<9ZKaE03Db*S6C8n^=^3-t^gH*Dx}+?w2`_a< zTwc*M{$w9S&Tt?aeo1Lr>>C@&pnxvdY@(Xw4drlsRa8wM&8ek*5bt2XP`J z6z&;%>+ERY%cSKoGje)$r|D^5^aK)|a`vP4;A#`u`$XJuFUx#q#&t*J$HKb8o;jHB zoY~`92iDMEq~T{Hfp4mG8S~tSXsh<*t2KS7NN9!qfxN{0V&U3jljoUV+Paox--00S z>@B=P70pi%27@oew>vVH=Lo9t zU|?CDWvbBQr?7dCq~I>3Dm&?Z*@-0xUQ0n!LQ2MlmGG&S@^MWPiA6T7UM#+x#-1OmneJ-TgJuey&!u}7fRv&1{ye<1b9jQyRKneK z4Nuc!!?gVupwTF7XuTAjTo+ue0sL5ZI zhCGV%5zRm%eBD(LEbbQbzASQCEU6)DLg8NX_1VD__$>H!%M+i=a=`yFy%h~%gcws_bL!Pw(2&y2t9Xm@ zdBr>5!j7&%Lv+Dc`?BZI4)gzVsekR13?_g?Sm^DBu}o^7Xss2z{D*H?ZIAl~D^w8h z8?HeJ=>KBg|44AF@1)iCf!|3e>+d8q { } DagRenderer.getInstance()?.deactivate(); } + +class ChatViewProvider implements vscode.WebviewViewProvider { + private _view?: vscode.WebviewView; + private messages: string[] = []; // Array to store chat messages + + constructor(private readonly context: vscode.ExtensionContext) {} + + resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ) { + this._view = webviewView; + + webviewView.webview.options = { + enableScripts: true + }; + + webviewView.webview.html = this.getWebviewContent(webviewView.webview, this.context.extensionUri); + webviewView.webview.onDidReceiveMessage((message) => { + if (message.command === 'sendMessage') { + this.addMessage(message.text); + } + }); + } + + // Generate the Webview content + getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); + const chatLogHtml = this.messages.map(msg => `

${msg}

`).join(''); + + return ` + + + + + ZenML Chat + + + +
${chatLogHtml}
+
+ + +
+ + +`; + } + + // Add a new message to the chat log + addMessage(message: string) { + this.messages.push(message); // Add the message to the log + this._view && (this._view.webview.html = this.getWebviewContent(this._view.webview, this.context.extensionUri)); // Re-render the Webview + } +} diff --git a/src/services/ZenExtension.ts b/src/services/ZenExtension.ts index 6091e838..43a0f8e7 100644 --- a/src/services/ZenExtension.ts +++ b/src/services/ZenExtension.ts @@ -42,7 +42,6 @@ import { LSClient } from './LSClient'; import { toggleCommands } from '../utils/global'; import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; -import { ChatbotDataProvider } from '../views/activityBar/chatbotView/chatbotDataProvider' import { registerComponentCommands } from '../commands/components/registry'; export interface IServerInfo { @@ -67,7 +66,6 @@ export class ZenExtension { ['zenmlComponentView', ComponentDataProvider.getInstance()], ['zenmlPipelineView', PipelineDataProvider.getInstance()], ['zenmlPanelView', PanelDataProvider.getInstance()], - // ['zenmlChatbotView', ChatbotDataProvider.getInstance()], ]); private static registries = [ diff --git a/src/views/activityBar/chatbotView/chatbotDataProvider.ts b/src/views/activityBar/chatbotView/chatbotDataProvider.ts deleted file mode 100644 index 1157cee9..00000000 --- a/src/views/activityBar/chatbotView/chatbotDataProvider.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { EventEmitter, TreeDataProvider, TreeItem, window } from 'vscode'; -import { chatbotItem } from './chatbotItem'; - -export class ChatbotDataProvider implements TreeDataProvider { - private _onDidChangeTreeData = new EventEmitter(); - readonly onDidChangeTreeData = this._onDidChangeTreeData.event; - - private inputText: string = ''; - - constructor() { - // Initialize the input box for user text input - this.showInputBox(); - } - - showInputBox() { - window.showInputBox({ prompt: 'Enter text here' }).then(value => { - if (value) { - this.inputText = value; - this.refresh(); - } - }); - } - - refresh(): void { - this._onDidChangeTreeData.fire(); - } - - getTreeItem(element: chatbotItem): TreeItem { - return element; - } - - getChildren(element?: chatbotItem): Thenable { - if (!element) { - // Root items with collapsible states - return Promise.resolve([ - new chatbotItem('Collapsible Section 1', 'Description 1', true), - new chatbotItem('Collapsible Section 2', 'Description 2', true), - new chatbotItem(`User Input: ${this.inputText}`, '', false) - ]); - } - return Promise.resolve([]); - } -} diff --git a/src/views/activityBar/chatbotView/chatbotItem.ts b/src/views/activityBar/chatbotView/chatbotItem.ts deleted file mode 100644 index 4537437c..00000000 --- a/src/views/activityBar/chatbotView/chatbotItem.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TreeItem, TreeItemCollapsibleState } from 'vscode'; - -export class chatbotItem extends TreeItem { - constructor( - public readonly label: string, - public readonly description: string, - public readonly isCollapsible: boolean - ) { - super( - label, - isCollapsible - ? TreeItemCollapsibleState.Collapsed - : TreeItemCollapsibleState.None - ); - this.tooltip = `${this.label} - ${this.description}`; - } -} From 8504461f9e249f407dece0d6a5ef5936ffe07958 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Wed, 21 Aug 2024 22:31:38 -0400 Subject: [PATCH 009/138] feat(chat): integrated Gemini API using TokenJS for chat response Added functionality to the chat window in the sidebar (activity bar). User input is now sent to the Gemini API using TokenJS, and the response from the API is displayed in the chat window. Co-authored-by: Nathaniel Xu Co-authored-by: Alan Cho --- package-lock.json | 2725 +++++++++++++++++++++++++++++++---- package.json | 1 + src/extension.ts | 32 +- src/services/chatService.ts | 57 + 4 files changed, 2519 insertions(+), 296 deletions(-) create mode 100644 src/services/chatService.ts diff --git a/package-lock.json b/package-lock.json index 413db7d4..92ecfa23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "hbs": "^4.2.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", + "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" }, "devDependencies": { @@ -58,371 +59,2076 @@ "node": ">=0.10.0" } }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.24.3", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", + "integrity": "sha512-916wJXO6T6k8R6BAAcLhLPv/pnLGy7YSEBZXZ1XTFbLcTZE8oTy3oDW9WJf9KKZwMvVcePIfoTSvzXHRcGxkQQ==", + "license": "MIT", + "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", + "web-streams-polyfill": "^3.2.1" + } + }, + "node_modules/@aws-crypto/crc32": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-bedrock-runtime": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.635.0.tgz", + "integrity": "sha512-uvj4eR5NK90tz8+rxipnKKiWNQwBxafxVDlOGH7oXVvCIQn7Vn/CfTYw4mpGgKLez27BcLyXSoM4oeRN6X7Ogw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.635.0", + "@aws-sdk/client-sts": "3.635.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/eventstream-serde-browser": "^3.0.6", + "@smithy/eventstream-serde-config-resolver": "^3.0.3", + "@smithy/eventstream-serde-node": "^3.0.5", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-stream": "^3.1.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-cognito-identity": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.635.0.tgz", + "integrity": "sha512-fRMqHYuYOv53rjiyG7YSoi9QF0WatgwZ4MXt3wZi0pQYISZGzoJKWyUahB762M7N9c0hJFQ1cq1UP0Q46pkx3w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.635.0", + "@aws-sdk/client-sts": "3.635.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sagemaker": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sagemaker/-/client-sagemaker-3.635.0.tgz", + "integrity": "sha512-r48nZIDIhYG9lEUUzry+WxxjJ2b9bt35rOk5Q22zwGC/5j9xW2glgIUICY96507rn3pglUghMfIy86tK+75OFA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.635.0", + "@aws-sdk/client-sts": "3.635.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.635.0.tgz", + "integrity": "sha512-/Hl69+JpFUo9JNVmh2gSvMgYkE4xjd+1okiRoPBbQqjI7YBP2JWCUDP8IoEkNq3wj0vNTq0OWfn6RpZycIkAXQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.635.0.tgz", + "integrity": "sha512-RIwDlhzAFttB1vbpznewnPqz7h1H/2UhQLwB38yfZBwYQOxyxVfLV5j5VoUUX3jY4i4qH9wiHc7b02qeAOZY6g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.635.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.635.0.tgz", + "integrity": "sha512-Al2ytE69+cbA44qHlelqhzWwbURikfF13Zkal9utIG5Q6T2c7r8p6sePN92n8l/x1v0FhJ5VTxKak+cPTE0CZQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.635.0", + "@aws-sdk/core": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.632.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.4.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.15", + "@smithy/util-defaults-mode-node": "^3.0.15", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.635.0.tgz", + "integrity": "sha512-i1x/E/sgA+liUE1XJ7rj1dhyXpAKO1UKFUcTTHXok2ARjWTvszHnSXMOsB77aPbmn0fUp1JTx2kHUAZ1LVt5Bg==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/core": "^2.4.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-cognito-identity": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-cognito-identity/-/credential-provider-cognito-identity-3.635.0.tgz", + "integrity": "sha512-3R8yC9cDtLHX3T8PWu+JSY+OHFjvU4TLqkepfWrEBYKn5VXbRKKnuLJ49D3S9ZiWnisLBL9t5UUEfTiWaOBu3w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.635.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.635.0.tgz", + "integrity": "sha512-iJyRgEjOCQlBMXqtwPLIKYc7Bsc6nqjrZybdMDenPDa+kmLg7xh8LxHsu9088e+2/wtLicE34FsJJIfzu3L82g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.635.0.tgz", + "integrity": "sha512-+OqcNhhOFFY08YHLjO9/Y1n37RKAO7LADnsJ7VTXca7IfvYh27BVBn+FdlqnyEb1MQ5ArHTY4pq3pKRIg6RW4Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.635.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.635.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.635.0.tgz", + "integrity": "sha512-bmd23mnb94S6AxmWPgqJTnvT9ONKlTx7EPafE1RNO+vUl6mHih4iyqX6ZPaRcSfaPx4U1R7H1RM8cSnafXgaBg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.635.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.635.0.tgz", + "integrity": "sha512-hO/fKyvUaGpK9zyvCnmJz70EputvGWDr2UTOn/RzvcR6UB4yXoFf0QcCMubEsE3v67EsAv6PadgOeJ0vz6IazA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.635.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-providers": { + "version": "3.635.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.635.0.tgz", + "integrity": "sha512-E5z4EB3oXD+Q5hj4Jmn8lhySWYYZdX/kByIKHaccOfNseAot2/+OzYeEn4SeF+LhRe6rcz8t+n1G3YuZlT+huQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-cognito-identity": "3.635.0", + "@aws-sdk/client-sso": "3.635.0", + "@aws-sdk/client-sts": "3.635.0", + "@aws-sdk/credential-provider-cognito-identity": "3.635.0", + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.635.0", + "@aws-sdk/credential-provider-ini": "3.635.0", + "@aws-sdk/credential-provider-node": "3.635.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.635.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.632.0.tgz", + "integrity": "sha512-yY/sFsHKwG9yzSf/DTclqWJaGPI2gPBJDCGBujSqTG1zlS7Ot4fqi91DZ6088BFWzbOorDzJFcAhAEFzc6LuQg==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.632.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http": { + "version": "3.374.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/protocol-http/-/protocol-http-3.374.0.tgz", + "integrity": "sha512-9WpRUbINdGroV3HiZZIBoJvL2ndoWk39OfwxWs2otxByppJZNN14bg/lvCx5e8ggHUti7IBk5rb0nqQZ4m05pg==", + "deprecated": "This package has moved to @smithy/protocol-http", + "license": "Apache-2.0", + "dependencies": { + "@smithy/protocol-http": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/protocol-http": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-1.2.0.tgz", + "integrity": "sha512-GfGfruksi3nXdFok5RhgtOnWe5f6BndzYfmEXISD+5gAGdayFGpjWu5pIqIweTudMtse20bGbc+7MFZXT1Tb8Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^1.2.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/protocol-http/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4": { + "version": "3.374.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4/-/signature-v4-3.374.0.tgz", + "integrity": "sha512-2xLJvSdzcZZAg0lsDLUAuSQuihzK0dcxIK7WmfuJeF7DGKJFmp9czQmz5f3qiDz6IDQzvgK1M9vtJSVCslJbyQ==", + "deprecated": "This package has moved to @smithy/signature-v4", + "license": "Apache-2.0", + "dependencies": { + "@smithy/signature-v4": "^1.0.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-crypto/crc32": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-3.0.0.tgz", + "integrity": "sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/util": "^3.0.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-crypto/crc32/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-crypto/util": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-3.0.0.tgz", + "integrity": "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "license": "0BSD" + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/eventstream-codec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-1.1.0.tgz", + "integrity": "sha512-3tEbUb8t8an226jKB6V/Q2XU/J53lCwCzULuBPEaF4JjSh+FlCMp7TmogE/Aij5J9DwlsZ4VAD/IRDuQ/0ZtMw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "3.0.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "tslib": "^2.5.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/is-array-buffer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-1.1.0.tgz", + "integrity": "sha512-twpQ/n+3OWZJ7Z+xu43MJErmhB/WO/mMTnqR6PwWQShvSJ/emx5d1N59LQZk6ZpTAeuRWrc+eHhkzTp9NFjNRQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/signature-v4": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-1.1.0.tgz", + "integrity": "sha512-fDo3m7YqXBs7neciOePPd/X9LPm5QLlDMdIC4m1H6dgNLnXfLMFNIxEfPyohGA8VW9Wn4X8lygnPSGxDZSmp0Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-codec": "^1.1.0", + "@smithy/is-array-buffer": "^1.1.0", + "@smithy/types": "^1.2.0", + "@smithy/util-hex-encoding": "^1.1.0", + "@smithy/util-middleware": "^1.1.0", + "@smithy/util-uri-escape": "^1.1.0", + "@smithy/util-utf8": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/types": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-1.2.0.tgz", + "integrity": "sha512-z1r00TvBqF3dh4aHhya7nz1HhvCg4TRmw51fjMrh5do3h+ngSstt/yKlNbHeb9QxJmFbmN8KEVSWgb1bRvfEoA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/util-buffer-from": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-1.1.0.tgz", + "integrity": "sha512-9m6NXE0ww+ra5HKHCHig20T+FAwxBAm7DIdwc/767uGWbRcY720ybgPacQNB96JMOI7xVr/CDa3oMzKmW4a+kw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/util-hex-encoding": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-1.1.0.tgz", + "integrity": "sha512-7UtIE9eH0u41zpB60Jzr0oNCQ3hMJUabMcKRUVjmyHTXiWDE4vjSqN6qlih7rCNeKGbioS7f/y2Jgym4QZcKFg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/util-middleware": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-1.1.0.tgz", + "integrity": "sha512-6hhckcBqVgjWAqLy2vqlPZ3rfxLDhFWEmM7oLh2POGvsi7j0tHkbN7w4DFhuBExVJAbJ/qqxqZdRY6Fu7/OezQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/util-uri-escape": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-1.1.0.tgz", + "integrity": "sha512-/jL/V1xdVRt5XppwiaEU8Etp5WHZj609n0xMTuehmCqdoOFbId1M+aEeDWZsQ+8JbEB/BJ6ynY2SlYmOaKtt8w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4/node_modules/@smithy/util-utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-1.1.0.tgz", + "integrity": "sha512-p/MYV+JmqmPyjdgyN2UxAeYDj9cBqCjp0C/NsTWnnjoZUVqoeZ6IrW915L9CAKWVECgv9lVQGc4u/yz26/bI1A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/util-buffer-from": "^1.1.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.632.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.632.0.tgz", + "integrity": "sha512-LlYMU8pAbcEQphOpE6xaNLJ8kPGhklZZTVzZVpVW477NaaGgoGTMYNXTABYHcxeF5E2lLrxql9OmVpvr8GWN8Q==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.3.1" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@google/generative-ai": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.14.1.tgz", + "integrity": "sha512-pevEyZCb0Oc+dYNlSberW8oZBm4ofeTD5wN01TowQMhTwdAbGAnJMtQzoklh6Blq2AKsx8Ox6FWa44KioZLZiA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@mistralai/mistralai": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-0.5.0.tgz", + "integrity": "sha512-56xfoC/0CiT0RFHrRNoJYSKCNc922EyHzEPJYY6ttalQ5KZdrNVgXeOetIGX0lDx7IjbxAJrrae2MQgUIlL9+g==", + "license": "ISC", + "dependencies": { + "node-fetch": "^2.6.7" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", + "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@sinonjs/samsam": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", + "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^2.0.0", + "lodash.get": "^4.4.2", + "type-detect": "^4.0.8" + } + }, + "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", + "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/text-encoding": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", + "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", + "dev": true + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, + "node_modules/@smithy/config-resolver": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=10.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, + "node_modules/@smithy/core": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.4.0.tgz", + "integrity": "sha512-cHXq+FneIF/KJbt4q4pjN186+Jf4ZB0ZOqEaZMBhT79srEyGDDBV31NqBRBjazz8ppQ1bJbDJMY9ba5wKFV36w==", + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.15", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "dev": true, + "node_modules/@smithy/eventstream-codec": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-3.1.2.tgz", + "integrity": "sha512-0mBcu49JWt4MXhrhRAlxASNy0IjDRFU+aWNDRal9OtUJvJNiwDuyKMUONSOjLjSCeGwZaE0wOErdqULer8r7yw==", + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/crc32": "5.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/eventstream-serde-browser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.6.tgz", + "integrity": "sha512-2hM54UWQUOrki4BtsUI1WzmD13/SeaqT/AB3EUJKbcver/WgKNaiJ5y5F5XXuVe6UekffVzuUDrBZVAA3AWRpQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, + "node_modules/@smithy/eventstream-serde-config-resolver": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.3.tgz", + "integrity": "sha512-NVTYjOuYpGfrN/VbRQgn31x73KDLfCXCsFdad8DiIc3IcdxL+dYA9zEQPyOP7Fy2QL8CPy2WE4WCUD+ZsLNfaQ==", + "license": "Apache-2.0", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/eventstream-serde-node": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.5.tgz", + "integrity": "sha512-+upXvnHNyZP095s11jF5dhGw/Ihzqwl5G+/KtMnoQOpdfC3B5HYCcDVG9EmgkhJMXJlM64PyN5gjJl0uXFQehQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/eventstream-serde-universal": "^3.0.5", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "node_modules/@smithy/eventstream-serde-universal": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.5.tgz", + "integrity": "sha512-5u/nXbyoh1s4QxrvNre9V6vfyoLWuiVvvd5TlZjGThIikc3G+uNiG9uOTCWweSRjv1asdDIWK7nOmN7le4RYHQ==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@smithy/eventstream-codec": "^3.1.2", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", - "dev": true, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", - "dev": true, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "license": "Apache-2.0", "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=10.10.0" + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "license": "Apache-2.0", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.15.tgz", + "integrity": "sha512-iTMedvNt1ApdvkaoE8aSDuwaoc+BhvHqttbA/FO4Ty+y/S5hW6Ci/CTScG7vam4RYJWZxdTElc3MEfHRVH6cgQ==", + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" }, "engines": { - "node": "*" + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12.22" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "dev": true + "node_modules/@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, + "node_modules/@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "license": "Apache-2.0", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, + "node_modules/@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=12" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "license": "Apache-2.0", "dependencies": { - "ansi-regex": "^6.0.1" + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12" + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.2.0.tgz", + "integrity": "sha512-pDbtxs8WOhJLJSeaF/eAbPgXg4VVYFlRcL/zoNYA5WbG3wBL06CHtBSg53ppkttDpAJ/hdiede+xApip1CwSLw==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/gen-mapping/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "tslib": "^2.6.2" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=6.0.0" + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "dev": true, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.15.tgz", + "integrity": "sha512-FZ4Psa3vjp8kOXcd3HJOiDPBCWtiilLl57r0cnNtq/Ga9RSDrM5ERL6xt+tO43+2af6Pn5Yp92x2n5vPuduNfg==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.15.tgz", + "integrity": "sha512-KSyAAx2q6d0t6f/S4XB2+3+6aQacm3aLMhs9aLMqn18uYGUepbdssfogW5JQZpc6lXNBnp0tEnR5e9CEKmEd7A==", + "license": "Apache-2.0", "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.2.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, + "node_modules/@smithy/util-endpoints": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", + "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.6.2" + }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, + "node_modules/@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "license": "Apache-2.0", "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">= 8" + "node": ">=16.0.0" } }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, + "node_modules/@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "license": "Apache-2.0", + "dependencies": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, "engines": { - "node": ">=14" + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, + "node_modules/@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "11.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-11.2.2.tgz", - "integrity": "sha512-G2piCSxQ7oWOxwGSAyFHfPIsyeJGXYtc6mFbnFA+kRXkiEnTl8c/8jul2S329iFBnDI9HGoeWWAZvuvOkZccgw==", - "dev": true, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^3.0.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/samsam": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.0.tgz", - "integrity": "sha512-Bp8KUVlLp8ibJZrnvq2foVhP0IVX2CIprMJPK0vqGqgrDa0OHVKeZyBykqskkrdxV6yKBPmGasO8LVjAKR3Gew==", - "dev": true, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "license": "Apache-2.0", "dependencies": { - "@sinonjs/commons": "^2.0.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/samsam/node_modules/@sinonjs/commons": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-2.0.0.tgz", - "integrity": "sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==", - "dev": true, + "node_modules/@smithy/util-waiter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", + "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", + "license": "Apache-2.0", "dependencies": { - "type-detect": "4.0.8" + "@smithy/abort-controller": "^3.1.1", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.2.tgz", - "integrity": "sha512-sXXKG+uL9IrKqViTtao2Ws6dy0znu9sOaP1di/jKGW1M6VssO8vlpXCQcpZ+jisQ1tTFAC5Jo/EOzFbggBagFQ==", - "dev": true - }, "node_modules/@svgdotjs/svg.js": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@svgdotjs/svg.js/-/svg.js-3.2.4.tgz", @@ -551,11 +2257,20 @@ "version": "18.19.29", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.29.tgz", "integrity": "sha512-5pAX7ggTmWZdhUrhRWLPf+5oM7F80bcKVCBbr0zwEkTNzTJL2CWQjznpFgHYy6GrzkYi2Yjy7DHKoynFxqPV8g==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.11.tgz", + "integrity": "sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==", + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -1119,6 +2834,18 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1170,6 +2897,18 @@ "node": ">= 6.0.0" } }, + "node_modules/agentkeepalive": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1353,6 +3092,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -1463,7 +3208,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -1696,37 +3440,158 @@ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/cohere-ai": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/cohere-ai/-/cohere-ai-7.12.1.tgz", + "integrity": "sha512-EgyhPSPR+d8ZP9JDe4obc/MHWbDVPlyqjnbGlY+rpL752JJ+9Fb2LCphRQt9AZhiwQhZUnUj2/xM+2cFK8fyAQ==", + "dependencies": { + "@aws-sdk/client-sagemaker": "^3.583.0", + "@aws-sdk/credential-providers": "^3.583.0", + "@aws-sdk/protocol-http": "^3.374.0", + "@aws-sdk/signature-v4": "^3.374.0", + "form-data": "^4.0.0", + "form-data-encoder": "^4.0.2", + "formdata-node": "^6.0.3", + "js-base64": "3.7.2", + "node-fetch": "2.7.0", + "qs": "6.11.2", + "readable-stream": "^4.5.2", + "url-join": "4.0.1" + } + }, + "node_modules/cohere-ai/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/cohere-ai/node_modules/form-data-encoder": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.0.2.tgz", + "integrity": "sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cohere-ai/node_modules/formdata-node": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-6.0.3.tgz", + "integrity": "sha512-8e1++BCiTzUno9v5IZ2J6bv4RU+3UKDmqWUQD0MIMVCd9AdhWkO1gw57oo1mNEX1dMq2EGI+FbWz4B92pscSQg==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "node_modules/cohere-ai/node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" }, "engines": { - "node": ">=10" + "node": ">=0.6" }, "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "node_modules/cohere-ai/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, "engines": { - "node": ">=0.8" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, + "node_modules/cohere-ai/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/cohere-ai/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" + "safe-buffer": "~5.2.0" } }, "node_modules/color-convert": { @@ -1904,7 +3769,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -2097,7 +3961,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, "dependencies": { "get-intrinsic": "^1.2.4" }, @@ -2109,7 +3972,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -2408,11 +4270,19 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, "engines": { "node": ">=0.8.x" } @@ -2460,6 +4330,28 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -2626,6 +4518,34 @@ "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", + "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", + "license": "MIT" + }, + "node_modules/formdata-node": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", + "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", + "license": "MIT", + "dependencies": { + "node-domexception": "1.0.0", + "web-streams-polyfill": "4.0.0-beta.3" + }, + "engines": { + "node": ">= 12.20" + } + }, + "node_modules/formdata-node/node_modules/web-streams-polyfill": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", + "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -2670,7 +4590,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -2688,7 +4607,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2", @@ -2789,7 +4707,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, "dependencies": { "get-intrinsic": "^1.1.3" }, @@ -2859,7 +4776,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -2871,7 +4787,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -2883,7 +4798,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -2895,7 +4809,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -2984,11 +4897,19 @@ "node": ">= 6" } }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, "funding": [ { "type": "github", @@ -3002,8 +4923,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/ignore": { "version": "5.3.1", @@ -3291,6 +5211,12 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-base64": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", + "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", + "license": "BSD-3-Clause" + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3964,8 +5890,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -3973,6 +5898,24 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -4024,6 +5967,45 @@ "dev": true, "optional": true }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -4055,7 +6037,6 @@ "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -4069,6 +6050,32 @@ "wrappy": "1" } }, + "node_modules/openai": { + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-4.56.0.tgz", + "integrity": "sha512-zcag97+3bG890MNNa0DQD9dGmmTWL8unJdNkulZzWRXrl+QeD+YkBI4H58rJcwErxqGK6a0jVPZ4ReJjhDGcmw==", + "license": "Apache-2.0", + "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" + }, + "bin": { + "openai": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -4398,6 +6405,15 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -4765,7 +6781,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -4821,7 +6836,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "es-errors": "^1.3.0", @@ -5090,6 +7104,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", @@ -5296,6 +7316,99 @@ "node": ">=8.0" } }, + "node_modules/token.js": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/token.js/-/token.js-0.4.3.tgz", + "integrity": "sha512-3TXC0op+OqVrwqLWCPoFrL99Q+qLa9iwLRTXA7c5WuwRZgrff4pIxltVvpSwzhukVrAzGopRXBfGdZxS930+5A==", + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "^0.24.3", + "@aws-sdk/client-bedrock-runtime": "^3.609.0", + "@google/generative-ai": "^0.14.1", + "@mistralai/mistralai": "^0.5.0", + "chalk": "^4.1.2", + "cohere-ai": "^7.10.6", + "mime-types": "^2.1.35", + "nanoid": "^5.0.7", + "openai": "^4.52.2" + } + }, + "node_modules/token.js/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/token.js/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/token.js/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/token.js/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/token.js/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/token.js/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, "node_modules/ts-api-utils": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", @@ -5562,8 +7675,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicode-properties": { "version": "1.4.1", @@ -5638,8 +7750,7 @@ "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", - "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", - "dev": true + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, "node_modules/util-deprecate": { "version": "1.0.2", @@ -5647,6 +7758,19 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -5721,6 +7845,21 @@ "node": ">=10.13.0" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, "node_modules/webpack": { "version": "5.91.0", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", @@ -5867,6 +8006,16 @@ "node": ">=4.0" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index ae22adbf..fa9d5850 100644 --- a/package.json +++ b/package.json @@ -520,6 +520,7 @@ "hbs": "^4.2.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", + "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" } } diff --git a/src/extension.ts b/src/extension.ts index 2fef01ad..4bec125b 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,6 +21,7 @@ import { LSP_ZENML_CLIENT_INITIALIZED } from './utils/constants'; import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; +import { ChatService } from './services/chatService'; export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); @@ -73,9 +74,14 @@ export async function deactivate(): Promise { DagRenderer.getInstance()?.deactivate(); } + + +// TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder +// class ChatViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; private messages: string[] = []; // Array to store chat messages + private chatService: ChatService = ChatService.getInstance(); // ChatService instance constructor(private readonly context: vscode.ExtensionContext) {} @@ -91,11 +97,11 @@ class ChatViewProvider implements vscode.WebviewViewProvider { }; webviewView.webview.html = this.getWebviewContent(webviewView.webview, this.context.extensionUri); - webviewView.webview.onDidReceiveMessage((message) => { - if (message.command === 'sendMessage') { - this.addMessage(message.text); - } - }); + webviewView.webview.onDidReceiveMessage(async (message) => { + if (message.command === 'sendMessage') { + await this.addMessage(message.text); + } + }); } // Generate the Webview content @@ -133,9 +139,19 @@ class ChatViewProvider implements vscode.WebviewViewProvider { `; } - // Add a new message to the chat log - addMessage(message: string) { - this.messages.push(message); // Add the message to the log + // Add a new message to the chat log and send to Gemini + async addMessage(message: string) { + this.messages.push(`User: ${message}`); // Add the message to the log + + // Get the bot's response and add it to the log + const botResponse = await this.chatService.getChatResponse(message); + this.messages.push(`Gemini: ${botResponse}`); + + + // Re-render the Webview content this._view && (this._view.webview.html = this.getWebviewContent(this._view.webview, this.context.extensionUri)); // Re-render the Webview + + // Post the bot's response back to the webview + this._view && this._view.webview.postMessage({ command: 'receiveMessage', text: `Gemini: ${botResponse}` }); } } diff --git a/src/services/chatService.ts b/src/services/chatService.ts new file mode 100644 index 00000000..c394d658 --- /dev/null +++ b/src/services/chatService.ts @@ -0,0 +1,57 @@ +export class ChatService { + private static instance: ChatService; + private tokenjs: any; // Update the type accordingly + private initialized: Promise; + + private constructor() { + // handling initialization as promise + this.initialized = this.initialize(); + } + + + public static getInstance(): ChatService { + if (!ChatService.instance) { + ChatService.instance = new ChatService(); + } + return ChatService.instance; + } + + private async initialize() { + console.log('starting to initialize chatservice'); + try { + // Use dynamic import to load the ESM module + const { TokenJS } = await import('token.js'); + + // TODO find another way to access the apiKey, instead of having it hardcoded + const apiKey = 'GEMINI_API_KEY_GOES_HERE'; + if (!apiKey) { + throw new Error('GEMINI_API_KEY is not set'); + } + this.tokenjs = new TokenJS({ apiKey }); + } catch (error) { + console.error('Error initializing TokenJS:', error); + this.tokenjs = null; // ensure tokenjs is null if initialization fails + } + } + + public async getChatResponse(message: string): Promise { + try { + // wait for initialization to complete + await this.initialize(); + + if (!this.tokenjs) { + throw new Error('ChatService not initialized properly'); + } + + const completion = await this.tokenjs.chat.completions.create({ + provider: 'gemini', + model: 'gemini-1.5-flash', + messages: [{ role: 'user', content: message }], + }); + return completion.choices[0]?.message?.content || 'No content'; + } catch (error) { + console.error('Error with Gemini API:', error); + return 'Error: Unable to get a response from Gemini.'; + } + } +} From 124ed243f2e880f2a0edb006212c9d94a4886904 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Thu, 22 Aug 2024 13:26:56 -0700 Subject: [PATCH 010/138] feat: added context for serverDataProvider messages Co-authored-by: Nathaniel-Xu Co-authored-by: William Yennie --- src/services/chatService.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index c394d658..cc6ae3ca 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,3 +1,5 @@ +import { ServerDataProvider } from '../views/activityBar'; + export class ChatService { private static instance: ChatService; private tokenjs: any; // Update the type accordingly @@ -23,7 +25,7 @@ export class ChatService { const { TokenJS } = await import('token.js'); // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = 'GEMINI_API_KEY_GOES_HERE'; + const apiKey = ''; if (!apiKey) { throw new Error('GEMINI_API_KEY is not set'); } @@ -42,11 +44,28 @@ export class ChatService { if (!this.tokenjs) { throw new Error('ChatService not initialized properly'); } + + let contextMessages = [ + { role: 'system', content: 'You are an AI assistant helping with ZenML tasks.' } + ]; + + if (message.includes('server')) { + let server = ServerDataProvider.getInstance(); + let status = server.getCurrentStatus(); + console.log("Server Information: ", server); + console.log("Server Status: ", JSON.stringify(status)); + contextMessages.push({ role: 'system', content: "This the user's ZenML server information: " + JSON.stringify(status) }); + } + + const allMessages = [ + ...contextMessages, + { role: 'user', content: message } + ]; const completion = await this.tokenjs.chat.completions.create({ provider: 'gemini', model: 'gemini-1.5-flash', - messages: [{ role: 'user', content: message }], + messages: allMessages, }); return completion.choices[0]?.message?.content || 'No content'; } catch (error) { From c0c8d93d8348d1b1cdcecaad227f954b3e603864 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 23 Aug 2024 10:05:43 -0400 Subject: [PATCH 011/138] feat: added chat history and stack component data/environment data/pipeline data --- package-lock.json | 628 +++++++++++++++++- package.json | 2 + requirements.txt | 201 +++--- src/extension.ts | 1 - src/services/chatService.ts | 171 +++-- src/test/python_tests/requirements.txt | 16 +- .../EnvironmentDataProvider.ts | 6 + .../pipelineView/PipelineDataProvider.ts | 11 +- 8 files changed, 832 insertions(+), 204 deletions(-) diff --git a/package-lock.json b/package-lock.json index 92ecfa23..9f8bc7f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,12 +10,14 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { + "@langchain/google-genai": "^0.0.26", "@svgdotjs/svg.js": "^3.2.4", "@vscode/python-extension": "^1.0.5", "axios": "^1.6.7", "dagre": "^0.8.5", "fs-extra": "^11.2.0", "hbs": "^4.2.0", + "langchain": "^0.2.16", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", "token.js": "^0.4.3", @@ -1397,6 +1399,106 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@langchain/core": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.28.tgz", + "integrity": "sha512-xN3+UdfxFaBcm29auMHFHGEYRh+3HwBc/dICHtwfk2wTSmw4HzWmBtZMx3BG+TOgh5Et7+mT6eF6E3omDLfk+A==", + "dependencies": { + "ansi-styles": "^5.0.0", + "camelcase": "6", + "decamelize": "1.2.0", + "js-tiktoken": "^1.0.12", + "langsmith": "^0.1.43", + "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" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@langchain/core/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@langchain/core/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/@langchain/google-genai": { + "version": "0.0.26", + "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-0.0.26.tgz", + "integrity": "sha512-XGrnnWGifoEi/WOU8rl2a1e22go58ILEeLvkrt09/tSaUN7nJoVSBF7Hug9+8SuKup1DELI3+sLcHDzfMxCLuw==", + "dependencies": { + "@google/generative-ai": "^0.7.0", + "@langchain/core": ">=0.2.21 <0.3.0", + "zod-to-json-schema": "^3.22.4" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/google-genai/node_modules/@google/generative-ai": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.7.1.tgz", + "integrity": "sha512-WTjMLLYL/xfA5BW6xAycRPiAX7FNHKAxrid/ayqC1QMam0KAK0NbMeS9Lubw80gVg5xFMLE+H7pw4wdNzTOlxw==", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@langchain/openai": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.2.7.tgz", + "integrity": "sha512-f2XDXbExJf4SYsy17QSiq0YY/UWJXhJwoiS8uRi/gBa20zBQ8+bBFRnb9vPdLkOkGiaTy+yXZVFro3a9iW2r3w==", + "dependencies": { + "@langchain/core": ">=0.2.26 <0.3.0", + "js-tiktoken": "^1.0.12", + "openai": "^4.55.0", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@langchain/textsplitters": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.0.3.tgz", + "integrity": "sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA==", + "dependencies": { + "@langchain/core": ">0.2.0 <0.3.0", + "js-tiktoken": "^1.0.12" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@mistralai/mistralai": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-0.5.0.tgz", @@ -2271,6 +2373,11 @@ "form-data": "^4.0.0" } }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" + }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -2298,6 +2405,11 @@ "integrity": "sha512-ZFwX8cDhbz6jiv3JZdMVYq8SSWHOUchChPmRoMwdIu3lz89aCu/gVK9TdR1eeb0ARQ8+5rtjUKrk1UR8hh0dhQ==", "dev": true }, + "node_modules/@types/uuid": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", + "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" + }, "node_modules/@types/vscode": { "version": "1.87.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.87.0.tgz", @@ -2986,8 +3098,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/array-union": { "version": "2.1.0", @@ -3051,7 +3162,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -3090,7 +3200,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true + "devOptional": true }, "node_modules/bowser": { "version": "2.11.0", @@ -3235,7 +3345,6 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, "engines": { "node": ">=10" }, @@ -3293,7 +3402,7 @@ "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "dev": true, + "devOptional": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -3314,7 +3423,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "dev": true, + "devOptional": true, "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -3671,7 +3780,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, + "devOptional": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -3687,7 +3796,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 6" }, @@ -3841,7 +3950,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, + "devOptional": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -3855,7 +3964,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, + "devOptional": true, "funding": [ { "type": "github", @@ -3867,7 +3976,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, + "devOptional": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -3882,7 +3991,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, + "devOptional": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -3937,7 +4046,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.12" }, @@ -4279,6 +4388,11 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4734,13 +4848,13 @@ } }, "node_modules/handlebars": { - "version": "4.7.7", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", - "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", - "license": "MIT", + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "devOptional": true, "dependencies": { "minimist": "^1.2.5", - "neo-async": "^2.6.0", + "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, @@ -4758,6 +4872,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "devOptional": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -4830,6 +4945,34 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/hbs/node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/hbs/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -4855,7 +4998,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "dev": true, + "devOptional": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -4929,7 +5072,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "dev": true, + "devOptional": true, "engines": { "node": ">= 4" } @@ -5217,11 +5360,18 @@ "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", "license": "BSD-3-Clause" }, + "node_modules/js-tiktoken": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.14.tgz", + "integrity": "sha512-Pk3l3WOgM9joguZY2k52+jH82RtABRgB5RdGFZNUGbOKGMVlNmafcPA3b0ITcCZPu1L9UclP1tne6aw7ZI4Myg==", + "dependencies": { + "base64-js": "^1.5.1" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -5270,6 +5420,14 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -5318,6 +5476,318 @@ "node": ">=0.10.0" } }, + "node_modules/langchain": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.2.16.tgz", + "integrity": "sha512-NaCl1jdxladaLd63VxGtVcbuypzTq14XSmQI6vBajAISJgz02Q1+wiAIPIC2wMfsKjBRoCSgNCJw3/7nxqUuoQ==", + "dependencies": { + "@langchain/core": ">=0.2.21 <0.3.0", + "@langchain/openai": ">=0.1.0 <0.3.0", + "@langchain/textsplitters": "~0.0.0", + "binary-extensions": "^2.2.0", + "js-tiktoken": "^1.0.12", + "js-yaml": "^4.1.0", + "jsonpointer": "^5.0.1", + "langsmith": "~0.1.40", + "openapi-types": "^12.1.3", + "p-retry": "4", + "uuid": "^10.0.0", + "yaml": "^2.2.1", + "zod": "^3.22.4", + "zod-to-json-schema": "^3.22.3" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@aws-sdk/client-s3": "*", + "@aws-sdk/client-sagemaker-runtime": "*", + "@aws-sdk/client-sfn": "*", + "@aws-sdk/credential-provider-node": "*", + "@azure/storage-blob": "*", + "@browserbasehq/sdk": "*", + "@gomomento/sdk": "*", + "@gomomento/sdk-core": "*", + "@gomomento/sdk-web": "^1.51.1", + "@langchain/anthropic": "*", + "@langchain/aws": "*", + "@langchain/cohere": "*", + "@langchain/community": "*", + "@langchain/google-genai": "*", + "@langchain/google-vertexai": "*", + "@langchain/groq": "*", + "@langchain/mistralai": "*", + "@langchain/ollama": "*", + "@mendable/firecrawl-js": "*", + "@notionhq/client": "*", + "@pinecone-database/pinecone": "*", + "@supabase/supabase-js": "*", + "@vercel/kv": "*", + "@xata.io/client": "*", + "apify-client": "*", + "assemblyai": "*", + "axios": "*", + "cheerio": "*", + "chromadb": "*", + "convex": "*", + "couchbase": "*", + "d3-dsv": "*", + "epub2": "*", + "fast-xml-parser": "*", + "handlebars": "^4.7.8", + "html-to-text": "*", + "ignore": "*", + "ioredis": "*", + "jsdom": "*", + "mammoth": "*", + "mongodb": "*", + "node-llama-cpp": "*", + "notion-to-md": "*", + "officeparser": "*", + "pdf-parse": "*", + "peggy": "^3.0.2", + "playwright": "*", + "puppeteer": "*", + "pyodide": "^0.24.1", + "redis": "*", + "sonix-speech-recognition": "*", + "srt-parser-2": "*", + "typeorm": "*", + "weaviate-ts-client": "*", + "web-auth-library": "*", + "ws": "*", + "youtube-transcript": "*", + "youtubei.js": "*" + }, + "peerDependenciesMeta": { + "@aws-sdk/client-s3": { + "optional": true + }, + "@aws-sdk/client-sagemaker-runtime": { + "optional": true + }, + "@aws-sdk/client-sfn": { + "optional": true + }, + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@browserbasehq/sdk": { + "optional": true + }, + "@gomomento/sdk": { + "optional": true + }, + "@gomomento/sdk-core": { + "optional": true + }, + "@gomomento/sdk-web": { + "optional": true + }, + "@langchain/anthropic": { + "optional": true + }, + "@langchain/aws": { + "optional": true + }, + "@langchain/cohere": { + "optional": true + }, + "@langchain/community": { + "optional": true + }, + "@langchain/google-genai": { + "optional": true + }, + "@langchain/google-vertexai": { + "optional": true + }, + "@langchain/groq": { + "optional": true + }, + "@langchain/mistralai": { + "optional": true + }, + "@langchain/ollama": { + "optional": true + }, + "@mendable/firecrawl-js": { + "optional": true + }, + "@notionhq/client": { + "optional": true + }, + "@pinecone-database/pinecone": { + "optional": true + }, + "@supabase/supabase-js": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "@xata.io/client": { + "optional": true + }, + "apify-client": { + "optional": true + }, + "assemblyai": { + "optional": true + }, + "axios": { + "optional": true + }, + "cheerio": { + "optional": true + }, + "chromadb": { + "optional": true + }, + "convex": { + "optional": true + }, + "couchbase": { + "optional": true + }, + "d3-dsv": { + "optional": true + }, + "epub2": { + "optional": true + }, + "faiss-node": { + "optional": true + }, + "fast-xml-parser": { + "optional": true + }, + "handlebars": { + "optional": true + }, + "html-to-text": { + "optional": true + }, + "ignore": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "mammoth": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "node-llama-cpp": { + "optional": true + }, + "notion-to-md": { + "optional": true + }, + "officeparser": { + "optional": true + }, + "pdf-parse": { + "optional": true + }, + "peggy": { + "optional": true + }, + "playwright": { + "optional": true + }, + "puppeteer": { + "optional": true + }, + "pyodide": { + "optional": true + }, + "redis": { + "optional": true + }, + "sonix-speech-recognition": { + "optional": true + }, + "srt-parser-2": { + "optional": true + }, + "typeorm": { + "optional": true + }, + "weaviate-ts-client": { + "optional": true + }, + "web-auth-library": { + "optional": true + }, + "ws": { + "optional": true + }, + "youtube-transcript": { + "optional": true + }, + "youtubei.js": { + "optional": true + } + } + }, + "node_modules/langchain/node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/langsmith": { + "version": "0.1.45", + "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.45.tgz", + "integrity": "sha512-ufgPYOTsZDYIYCStHx8gGJX5jLbk1GhFDxEDyycCicPFxlG40et53J9tYdaZ+MTBUCMexs9eKXCmvxBaUt9wrg==", + "dependencies": { + "@types/uuid": "^9.0.1", + "commander": "^10.0.1", + "p-queue": "^6.6.2", + "p-retry": "4", + "semver": "^7.6.3", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "@langchain/core": "*", + "langchain": "*", + "openai": "*" + }, + "peerDependenciesMeta": { + "@langchain/core": { + "optional": true + }, + "langchain": { + "optional": true + }, + "openai": { + "optional": true + } + } + }, + "node_modules/langsmith/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "engines": { + "node": ">=14" + } + }, "node_modules/legacy-swc-helpers": { "name": "@swc/helpers", "version": "0.4.14", @@ -5498,6 +5968,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -5892,6 +6363,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -6025,7 +6504,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, + "devOptional": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -6076,6 +6555,11 @@ } } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6093,6 +6577,14 @@ "node": ">= 0.8.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6123,6 +6615,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -6172,7 +6702,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "dev": true, + "devOptional": true, "dependencies": { "entities": "^4.4.0" }, @@ -6184,7 +6714,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "dev": true, + "devOptional": true, "dependencies": { "domhandler": "^5.0.2", "parse5": "^7.0.0" @@ -6635,6 +7165,14 @@ "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -6755,12 +7293,9 @@ } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "bin": { "semver": "bin/semver.js" }, @@ -7320,7 +7855,6 @@ "version": "0.4.3", "resolved": "https://registry.npmjs.org/token.js/-/token.js-0.4.3.tgz", "integrity": "sha512-3TXC0op+OqVrwqLWCPoFrL99Q+qLa9iwLRTXA7c5WuwRZgrff4pIxltVvpSwzhukVrAzGopRXBfGdZxS930+5A==", - "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.24.3", "@aws-sdk/client-bedrock-runtime": "^3.609.0", @@ -8216,7 +8750,19 @@ "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", + "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } }, "node_modules/yargs": { "version": "17.7.2", @@ -8328,6 +8874,22 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.23.8", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", + "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz", + "integrity": "sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw==", + "peerDependencies": { + "zod": "^3.23.3" + } } } } diff --git a/package.json b/package.json index fa9d5850..86d2e8aa 100644 --- a/package.json +++ b/package.json @@ -512,12 +512,14 @@ "webpack-cli": "^5.1.4" }, "dependencies": { + "@langchain/google-genai": "^0.0.26", "@svgdotjs/svg.js": "^3.2.4", "@vscode/python-extension": "^1.0.5", "axios": "^1.6.7", "dagre": "^0.8.5", "fs-extra": "^11.2.0", "hbs": "^4.2.0", + "langchain": "^0.2.16", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", "token.js": "^0.4.3", diff --git a/requirements.txt b/requirements.txt index 8bd80588..a402d009 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,12 +1,12 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --generate-hashes ./requirements.in # -attrs==23.2.0 \ - --hash=sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30 \ - --hash=sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1 +attrs==24.2.0 \ + --hash=sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346 \ + --hash=sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2 # via # cattrs # lsprotocol @@ -16,12 +16,6 @@ cattrs==23.2.3 \ # via # lsprotocol # pygls -exceptiongroup==1.2.0 ; python_version < "3.11" \ - --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \ - --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68 - # via - # -r ./requirements.in - # cattrs lsprotocol==2023.0.1 \ --hash=sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2 \ --hash=sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d @@ -34,100 +28,99 @@ pygls==1.3.1 \ --hash=sha256:140edceefa0da0e9b3c533547c892a42a7d2fd9217ae848c330c53d266a55018 \ --hash=sha256:6e00f11efc56321bdeb6eac04f6d86131f654c7d49124344a9ebb968da3dd91e # via -r ./requirements.in -pyyaml==6.0.1 \ - --hash=sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5 \ - --hash=sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc \ - --hash=sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df \ - --hash=sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741 \ - --hash=sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206 \ - --hash=sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27 \ - --hash=sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595 \ - --hash=sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62 \ - --hash=sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98 \ - --hash=sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696 \ - --hash=sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290 \ - --hash=sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9 \ - --hash=sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d \ - --hash=sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6 \ - --hash=sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867 \ - --hash=sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47 \ - --hash=sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486 \ - --hash=sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6 \ - --hash=sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3 \ - --hash=sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007 \ - --hash=sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938 \ - --hash=sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0 \ - --hash=sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c \ - --hash=sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735 \ - --hash=sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d \ - --hash=sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28 \ - --hash=sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4 \ - --hash=sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba \ - --hash=sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8 \ - --hash=sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef \ - --hash=sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5 \ - --hash=sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd \ - --hash=sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3 \ - --hash=sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0 \ - --hash=sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515 \ - --hash=sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c \ - --hash=sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c \ - --hash=sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924 \ - --hash=sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34 \ - --hash=sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43 \ - --hash=sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859 \ - --hash=sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673 \ - --hash=sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54 \ - --hash=sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a \ - --hash=sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b \ - --hash=sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab \ - --hash=sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa \ - --hash=sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c \ - --hash=sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585 \ - --hash=sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d \ - --hash=sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f +pyyaml==6.0.2 \ + --hash=sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff \ + --hash=sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48 \ + --hash=sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086 \ + --hash=sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e \ + --hash=sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133 \ + --hash=sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5 \ + --hash=sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484 \ + --hash=sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee \ + --hash=sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5 \ + --hash=sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68 \ + --hash=sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a \ + --hash=sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf \ + --hash=sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99 \ + --hash=sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8 \ + --hash=sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85 \ + --hash=sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19 \ + --hash=sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc \ + --hash=sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a \ + --hash=sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1 \ + --hash=sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317 \ + --hash=sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c \ + --hash=sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631 \ + --hash=sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d \ + --hash=sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652 \ + --hash=sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5 \ + --hash=sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e \ + --hash=sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b \ + --hash=sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8 \ + --hash=sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476 \ + --hash=sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706 \ + --hash=sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563 \ + --hash=sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237 \ + --hash=sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b \ + --hash=sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083 \ + --hash=sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180 \ + --hash=sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425 \ + --hash=sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e \ + --hash=sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f \ + --hash=sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725 \ + --hash=sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183 \ + --hash=sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab \ + --hash=sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774 \ + --hash=sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725 \ + --hash=sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e \ + --hash=sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5 \ + --hash=sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d \ + --hash=sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290 \ + --hash=sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44 \ + --hash=sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed \ + --hash=sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4 \ + --hash=sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba \ + --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ + --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 # via -r ./requirements.in -types-pyyaml==6.0.12.20240311 \ - --hash=sha256:a9e0f0f88dc835739b0c1ca51ee90d04ca2a897a71af79de9aec5f38cb0a5342 \ - --hash=sha256:b845b06a1c7e54b8e5b4c683043de0d9caf205e7434b3edc678ff2411979b8f6 +types-pyyaml==6.0.12.20240808 \ + --hash=sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af \ + --hash=sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35 # via -r ./requirements.in -typing-extensions==4.12.2 ; python_version < "3.11" \ - --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ - --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 - # via - # -r ./requirements.in - # cattrs -watchdog==4.0.1 \ - --hash=sha256:0144c0ea9997b92615af1d94afc0c217e07ce2c14912c7b1a5731776329fcfc7 \ - --hash=sha256:03e70d2df2258fb6cb0e95bbdbe06c16e608af94a3ffbd2b90c3f1e83eb10767 \ - --hash=sha256:093b23e6906a8b97051191a4a0c73a77ecc958121d42346274c6af6520dec175 \ - --hash=sha256:123587af84260c991dc5f62a6e7ef3d1c57dfddc99faacee508c71d287248459 \ - --hash=sha256:17e32f147d8bf9657e0922c0940bcde863b894cd871dbb694beb6704cfbd2fb5 \ - --hash=sha256:206afc3d964f9a233e6ad34618ec60b9837d0582b500b63687e34011e15bb429 \ - --hash=sha256:4107ac5ab936a63952dea2a46a734a23230aa2f6f9db1291bf171dac3ebd53c6 \ - --hash=sha256:4513ec234c68b14d4161440e07f995f231be21a09329051e67a2118a7a612d2d \ - --hash=sha256:611be3904f9843f0529c35a3ff3fd617449463cb4b73b1633950b3d97fa4bfb7 \ - --hash=sha256:62c613ad689ddcb11707f030e722fa929f322ef7e4f18f5335d2b73c61a85c28 \ - --hash=sha256:667f3c579e813fcbad1b784db7a1aaa96524bed53437e119f6a2f5de4db04235 \ - --hash=sha256:6e8c70d2cd745daec2a08734d9f63092b793ad97612470a0ee4cbb8f5f705c57 \ - --hash=sha256:7577b3c43e5909623149f76b099ac49a1a01ca4e167d1785c76eb52fa585745a \ - --hash=sha256:998d2be6976a0ee3a81fb8e2777900c28641fb5bfbd0c84717d89bca0addcdc5 \ - --hash=sha256:a3c2c317a8fb53e5b3d25790553796105501a235343f5d2bf23bb8649c2c8709 \ - --hash=sha256:ab998f567ebdf6b1da7dc1e5accfaa7c6992244629c0fdaef062f43249bd8dee \ - --hash=sha256:ac7041b385f04c047fcc2951dc001671dee1b7e0615cde772e84b01fbf68ee84 \ - --hash=sha256:bca36be5707e81b9e6ce3208d92d95540d4ca244c006b61511753583c81c70dd \ - --hash=sha256:c9904904b6564d4ee8a1ed820db76185a3c96e05560c776c79a6ce5ab71888ba \ - --hash=sha256:cad0bbd66cd59fc474b4a4376bc5ac3fc698723510cbb64091c2a793b18654db \ - --hash=sha256:d10a681c9a1d5a77e75c48a3b8e1a9f2ae2928eda463e8d33660437705659682 \ - --hash=sha256:d4925e4bf7b9bddd1c3de13c9b8a2cdb89a468f640e66fbfabaf735bd85b3e35 \ - --hash=sha256:d7b9f5f3299e8dd230880b6c55504a1f69cf1e4316275d1b215ebdd8187ec88d \ - --hash=sha256:da2dfdaa8006eb6a71051795856bedd97e5b03e57da96f98e375682c48850645 \ - --hash=sha256:dddba7ca1c807045323b6af4ff80f5ddc4d654c8bce8317dde1bd96b128ed253 \ - --hash=sha256:e7921319fe4430b11278d924ef66d4daa469fafb1da679a2e48c935fa27af193 \ - --hash=sha256:e93f451f2dfa433d97765ca2634628b789b49ba8b504fdde5837cdcf25fdb53b \ - --hash=sha256:eebaacf674fa25511e8867028d281e602ee6500045b57f43b08778082f7f8b44 \ - --hash=sha256:ef0107bbb6a55f5be727cfc2ef945d5676b97bffb8425650dadbb184be9f9a2b \ - --hash=sha256:f0de0f284248ab40188f23380b03b59126d1479cd59940f2a34f8852db710625 \ - --hash=sha256:f27279d060e2ab24c0aa98363ff906d2386aa6c4dc2f1a374655d4e02a6c5e5e \ - --hash=sha256:f8affdf3c0f0466e69f5b3917cdd042f89c8c63aebdb9f7c078996f607cdb0f5 +watchdog==4.0.2 \ + --hash=sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4 \ + --hash=sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19 \ + --hash=sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a \ + --hash=sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa \ + --hash=sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a \ + --hash=sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a \ + --hash=sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1 \ + --hash=sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc \ + --hash=sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9 \ + --hash=sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930 \ + --hash=sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73 \ + --hash=sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b \ + --hash=sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83 \ + --hash=sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7 \ + --hash=sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef \ + --hash=sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1 \ + --hash=sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040 \ + --hash=sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b \ + --hash=sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270 \ + --hash=sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c \ + --hash=sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d \ + --hash=sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8 \ + --hash=sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508 \ + --hash=sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b \ + --hash=sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503 \ + --hash=sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757 \ + --hash=sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b \ + --hash=sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29 \ + --hash=sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c \ + --hash=sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22 \ + --hash=sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578 \ + --hash=sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e \ + --hash=sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee \ + --hash=sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7 \ + --hash=sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3 # via -r ./requirements.in diff --git a/src/extension.ts b/src/extension.ts index 4bec125b..ee4d0efd 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -147,7 +147,6 @@ class ChatViewProvider implements vscode.WebviewViewProvider { const botResponse = await this.chatService.getChatResponse(message); this.messages.push(`Gemini: ${botResponse}`); - // Re-render the Webview content this._view && (this._view.webview.html = this.getWebviewContent(this._view.webview, this.context.extensionUri)); // Re-render the Webview diff --git a/src/services/chatService.ts b/src/services/chatService.ts index cc6ae3ca..0e6b2c33 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,15 +1,24 @@ -import { ServerDataProvider } from '../views/activityBar'; +import { PipelineDataProvider, PipelineRunTreeItem, PipelineTreeItem, ServerDataProvider } from '../views/activityBar'; +import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; +import { InMemoryChatMessageHistory } from "@langchain/core/chat_history"; +import { ChatPromptTemplate } from "@langchain/core/prompts"; +import { RunnableWithMessageHistory } from "@langchain/core/runnables"; +import { MessageContent } from '@langchain/core/messages'; +import { StackComponentTreeItem } from '../views/activityBar'; +import * as vscode from 'vscode'; +import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; +import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; export class ChatService { private static instance: ChatService; - private tokenjs: any; // Update the type accordingly + private messageHistories: Record = {}; + private sessionId: string = "abc42"; private initialized: Promise; + private model!: ChatGoogleGenerativeAI; private constructor() { - // handling initialization as promise - this.initialized = this.initialize(); - } - + this.initialized = this.initialize(); + } public static getInstance(): ChatService { if (!ChatService.instance) { @@ -19,58 +28,116 @@ export class ChatService { } private async initialize() { - console.log('starting to initialize chatservice'); - try { - // Use dynamic import to load the ESM module - const { TokenJS } = await import('token.js'); - - // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = ''; - if (!apiKey) { - throw new Error('GEMINI_API_KEY is not set'); - } - this.tokenjs = new TokenJS({ apiKey }); - } catch (error) { - console.error('Error initializing TokenJS:', error); - this.tokenjs = null; // ensure tokenjs is null if initialization fails - } + this.model = new ChatGoogleGenerativeAI({ + model: "gemini-1.5-flash", + apiKey: "" + }); } - public async getChatResponse(message: string): Promise { + public async getChatResponse(message: string): Promise { try { - // wait for initialization to complete - await this.initialize(); - - if (!this.tokenjs) { - throw new Error('ChatService not initialized properly'); - } - - let contextMessages = [ - { role: 'system', content: 'You are an AI assistant helping with ZenML tasks.' } - ]; - - if (message.includes('server')) { - let server = ServerDataProvider.getInstance(); - let status = server.getCurrentStatus(); - console.log("Server Information: ", server); - console.log("Server Status: ", JSON.stringify(status)); - contextMessages.push({ role: 'system', content: "This the user's ZenML server information: " + JSON.stringify(status) }); - } - - const allMessages = [ - ...contextMessages, - { role: 'user', content: message } - ]; - - const completion = await this.tokenjs.chat.completions.create({ - provider: 'gemini', - model: 'gemini-1.5-flash', - messages: allMessages, - }); - return completion.choices[0]?.message?.content || 'No content'; + let context = '' + if (message.includes('environment')) { + context += this.getEnvironmentData() + } + if (message.includes('pipeline')) { + context += this.getPipelineData() + } + if (message.includes('stack')) { + context += this.getStackComponentData() + } + if (message.includes('server')) { + context += this.getServerStatus() + } + + const prompt = ChatPromptTemplate.fromMessages([ + [ + "system", + `You are a helpful assistant who remembers all details the user shares with you.`, + ], + ["placeholder", "{chat_history}"], + ["human", "{input}"], + ]); + + const chain = prompt.pipe(this.model); + + const withMessageHistory = new RunnableWithMessageHistory({ + runnable: chain, + getMessageHistory: async (sessionId) => { + if (this.messageHistories[sessionId] === undefined) { + this.messageHistories[sessionId] = new InMemoryChatMessageHistory(); + } + return this.messageHistories[sessionId]; + }, + inputMessagesKey: "input", + historyMessagesKey: "chat_history", + }); + + const config = { + configurable: { + sessionId: this.sessionId, + }, + }; + + const response = await withMessageHistory.invoke( + { + input: (message + `Here is contexual information for reference: ${context}`), + }, + config + ); + + return response.content; } catch (error) { console.error('Error with Gemini API:', error); return 'Error: Unable to get a response from Gemini.'; } } + + // private getContext() { + + // } + + private getServerStatus(): string { + return (`Server Status Data:\n` + JSON.stringify(ServerDataProvider.getInstance().getCurrentStatus()) + '\n'); + } + + private getStackComponentData(): string { + let components = ComponentDataProvider.getInstance().items; + let componentData = components.map((item: vscode.TreeItem) => { + if (item instanceof StackComponentTreeItem) { + let { name, type, flavor, id } = item.component; + let stackId = item.stackId; + let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; + let componentId = ` - Component ID: ${id}`; + return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; + } else { + return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; + } + }).join('\n'); + return `Stack Component Data:\n${componentData}\n`; + } + + private getEnvironmentData(): string { + let data = EnvironmentDataProvider.getInstance().getEnvironmentData() + let contextString = data.map((item) => `${item.label}: ${item.description || ''}`).join('\n'); + return `Environment Data:\n${contextString}\n`; + } + + private getPipelineData(): string { + let pipelineData = PipelineDataProvider.getInstance().getPipelineData() + let contextString = pipelineData.map((pipelineRun: PipelineTreeItem) => { + return (`Pipeline Run:\n` + pipelineRun.children?.map((item: PipelineRunTreeItem) => { + return `${item.tooltip}` + }).join('\n') + `\n${pipelineRun.description}`) + }).join('\n') + return `Pipeline Data:\n${contextString}\n`; + } + + // private getStackData(): string { + + // } + + // private getPanelData(): { + + // } } diff --git a/src/test/python_tests/requirements.txt b/src/test/python_tests/requirements.txt index d9003d0f..5741e600 100644 --- a/src/test/python_tests/requirements.txt +++ b/src/test/python_tests/requirements.txt @@ -1,13 +1,9 @@ # -# This file is autogenerated by pip-compile with Python 3.9 +# This file is autogenerated by pip-compile with Python 3.11 # by the following command: # # pip-compile --generate-hashes ./src/test/python_tests/requirements.in # -exceptiongroup==1.2.1 \ - --hash=sha256:5258b9ed329c5bbdd31a309f53cbfb0b155341807f6ff7606a1e801a891b29ad \ - --hash=sha256:a4785e48b045528f5bfe627b6ad554ff32def154f42372786903b7abcfe1aa16 - # via pytest iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -24,18 +20,14 @@ pyhamcrest==2.1.0 \ --hash=sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c \ --hash=sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587 # via -r ./src/test/python_tests/requirements.in -pytest==8.2.2 \ - --hash=sha256:c434598117762e2bd304e526244f67bf66bbd7b5d6cf22138be51ff661980343 \ - --hash=sha256:de4bb8104e201939ccdc688b27a89a7be2079b22e2bd2b07f806b6ba71117977 +pytest==8.3.2 \ + --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ + --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce # via -r ./src/test/python_tests/requirements.in python-jsonrpc-server==0.4.0 \ --hash=sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595 \ --hash=sha256:e5a908ff182e620aac07db5f57887eeb0afe33993008f57dc1b85b594cea250c # via -r ./src/test/python_tests/requirements.in -tomli==2.0.1 \ - --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ - --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f - # via pytest ujson==5.10.0 \ --hash=sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e \ --hash=sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b \ diff --git a/src/views/activityBar/environmentView/EnvironmentDataProvider.ts b/src/views/activityBar/environmentView/EnvironmentDataProvider.ts index 215773a5..c4ca5ce9 100644 --- a/src/views/activityBar/environmentView/EnvironmentDataProvider.ts +++ b/src/views/activityBar/environmentView/EnvironmentDataProvider.ts @@ -37,6 +37,7 @@ export class EnvironmentDataProvider implements TreeDataProvider { private lsClientStatus: State = State.Stopped; private zenmlClientReady: boolean = false; private zenmlInstallationStatus: LSNotificationIsZenMLInstalled | null = null; + private items: EnvironmentItem[] = []; private eventBus = EventBus.getInstance(); @@ -133,9 +134,14 @@ export class EnvironmentDataProvider implements TreeDataProvider { ...(await createInterpreterDetails()), ...(await createWorkspaceSettingsItems()), ]; + this.items = items; return items; } + public getEnvironmentData(): EnvironmentItem[] { + return this.items + } + /** * Simplifies getChildren by always returning root items, as there are no collapsible items now. */ diff --git a/src/views/activityBar/pipelineView/PipelineDataProvider.ts b/src/views/activityBar/pipelineView/PipelineDataProvider.ts index fc83d062..80a99346 100644 --- a/src/views/activityBar/pipelineView/PipelineDataProvider.ts +++ b/src/views/activityBar/pipelineView/PipelineDataProvider.ts @@ -34,6 +34,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { private static instance: PipelineDataProvider | null = null; private eventBus = EventBus.getInstance(); private zenmlClientReady = false; + private pipelineData: PipelineTreeItem[] = []; constructor() { super(); @@ -145,7 +146,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { totalPages: total_pages, }; - return runs.map((run: PipelineRun) => { + this.pipelineData = runs.map((run: PipelineRun) => { const formattedStartTime = new Date(run.startTime).toLocaleString(); const formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; @@ -160,6 +161,8 @@ export class PipelineDataProvider extends PaginatedDataProvider { return new PipelineTreeItem(run, run.id, children); }); + + return this.pipelineData } else { console.error(`Unexpected response format:`, result); return []; @@ -174,4 +177,8 @@ export class PipelineDataProvider extends PaginatedDataProvider { ]; } } -} + + public getPipelineData(): PipelineTreeItem[]{ + return this.pipelineData + } +} From 42c9feb99c0668639d5a9c394c8b17e0ec448a93 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 23 Aug 2024 13:54:35 -0400 Subject: [PATCH 012/138] fix: Removed langchain and used tokenjs, removed message history Co-authored-by: Will Yennie Co-authored-by: Alan Cho --- src/services/chatService.ts | 78 +++++++++++++------------------------ 1 file changed, 28 insertions(+), 50 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 0e6b2c33..48272dce 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,24 +1,18 @@ import { PipelineDataProvider, PipelineRunTreeItem, PipelineTreeItem, ServerDataProvider } from '../views/activityBar'; -import { ChatGoogleGenerativeAI } from "@langchain/google-genai"; -import { InMemoryChatMessageHistory } from "@langchain/core/chat_history"; -import { ChatPromptTemplate } from "@langchain/core/prompts"; -import { RunnableWithMessageHistory } from "@langchain/core/runnables"; -import { MessageContent } from '@langchain/core/messages'; import { StackComponentTreeItem } from '../views/activityBar'; import * as vscode from 'vscode'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; +import { TokenJS } from 'token.js' export class ChatService { private static instance: ChatService; - private messageHistories: Record = {}; - private sessionId: string = "abc42"; private initialized: Promise; - private model!: ChatGoogleGenerativeAI; + private tokenjs: any; private constructor() { this.initialized = this.initialize(); - } + } public static getInstance(): ChatService { if (!ChatService.instance) { @@ -28,13 +22,27 @@ export class ChatService { } private async initialize() { - this.model = new ChatGoogleGenerativeAI({ - model: "gemini-1.5-flash", - apiKey: "" - }); + // Use dynamic import to load the ESM module + // const { TokenJS } = await import('token.js'); + + // TODO find another way to access the apiKey, instead of having it hardcoded + const apiKey = ''; + if (!apiKey) { + throw new Error('GEMINI_API_KEY is not set'); + } + this.tokenjs = new TokenJS({ apiKey }); } - public async getChatResponse(message: string): Promise { + public async getChatResponse(message: string): Promise { + //Recreate or copy from langchain + //minimize everything when you enter a question + //markdown editor + //tooltips for what the context does + //sample questions + //prompt engineering (system prompt) + //syntax like @stacks for context + //tests + //Stack Data Provider and Panel Data Provider need to be implemented try { let context = '' if (message.includes('environment')) { @@ -50,43 +58,13 @@ export class ChatService { context += this.getServerStatus() } - const prompt = ChatPromptTemplate.fromMessages([ - [ - "system", - `You are a helpful assistant who remembers all details the user shares with you.`, - ], - ["placeholder", "{chat_history}"], - ["human", "{input}"], - ]); - - const chain = prompt.pipe(this.model); - - const withMessageHistory = new RunnableWithMessageHistory({ - runnable: chain, - getMessageHistory: async (sessionId) => { - if (this.messageHistories[sessionId] === undefined) { - this.messageHistories[sessionId] = new InMemoryChatMessageHistory(); - } - return this.messageHistories[sessionId]; - }, - inputMessagesKey: "input", - historyMessagesKey: "chat_history", + const completion = await this.tokenjs.chat.completions.create({ + provider: 'gemini', + model: 'gemini-1.5-flash', + messages: [{ role: 'user', content: (message + `Use this context to answer the question: ${context}`) }], }); - - const config = { - configurable: { - sessionId: this.sessionId, - }, - }; - - const response = await withMessageHistory.invoke( - { - input: (message + `Here is contexual information for reference: ${context}`), - }, - config - ); - - return response.content; + return completion.choices[0]?.message?.content || 'No content'; + } catch (error) { console.error('Error with Gemini API:', error); return 'Error: Unable to get a response from Gemini.'; From 8ede3cb8c4618f1706d094d471839ab648c89d8f Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 23 Aug 2024 12:41:14 -0700 Subject: [PATCH 013/138] feat: added additional functions for gathering context --- src/extension.ts | 68 +++--- src/services/chatService.ts | 225 +++++++++++------- .../EnvironmentDataProvider.ts | 2 +- .../pipelineView/PipelineDataProvider.ts | 8 +- 4 files changed, 180 insertions(+), 123 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index ee4d0efd..5832f208 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -42,12 +42,10 @@ export async function activate(context: vscode.ExtensionContext) { }); registerEnvironmentCommands(context); - context.subscriptions.push( vscode.window.registerWebviewViewProvider('zenmlChatView', new ChatViewProvider(context)) ); - await ZenExtension.activate(context, lsClient); context.subscriptions.push( @@ -74,34 +72,35 @@ export async function deactivate(): Promise { DagRenderer.getInstance()?.deactivate(); } - - // TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder -// +// class ChatViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; - private messages: string[] = []; // Array to store chat messages + private messages: string[] = []; // Array to store chat messages private chatService: ChatService = ChatService.getInstance(); // ChatService instance constructor(private readonly context: vscode.ExtensionContext) {} resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken ) { - this._view = webviewView; - - webviewView.webview.options = { - enableScripts: true - }; - - webviewView.webview.html = this.getWebviewContent(webviewView.webview, this.context.extensionUri); - webviewView.webview.onDidReceiveMessage(async (message) => { - if (message.command === 'sendMessage') { - await this.addMessage(message.text); - } - }); + this._view = webviewView; + + webviewView.webview.options = { + enableScripts: true, + }; + + webviewView.webview.html = this.getWebviewContent( + webviewView.webview, + this.context.extensionUri + ); + webviewView.webview.onDidReceiveMessage(async message => { + if (message.command === 'sendMessage') { + await this.addMessage(message.text); + } + }); } // Generate the Webview content @@ -141,16 +140,21 @@ class ChatViewProvider implements vscode.WebviewViewProvider { // Add a new message to the chat log and send to Gemini async addMessage(message: string) { - this.messages.push(`User: ${message}`); // Add the message to the log - - // Get the bot's response and add it to the log - const botResponse = await this.chatService.getChatResponse(message); - this.messages.push(`Gemini: ${botResponse}`); - - // Re-render the Webview content - this._view && (this._view.webview.html = this.getWebviewContent(this._view.webview, this.context.extensionUri)); // Re-render the Webview - - // Post the bot's response back to the webview - this._view && this._view.webview.postMessage({ command: 'receiveMessage', text: `Gemini: ${botResponse}` }); + this.messages.push(`User: ${message}`); // Add the message to the log + + // Get the bot's response and add it to the log + const botResponse = await this.chatService.getChatResponse(message); + this.messages.push(`Gemini: ${botResponse}`); + + // Re-render the Webview content + this._view && + (this._view.webview.html = this.getWebviewContent( + this._view.webview, + this.context.extensionUri + )); // Re-render the Webview + + // Post the bot's response back to the webview + this._view && + this._view.webview.postMessage({ command: 'receiveMessage', text: `Gemini: ${botResponse}` }); } } diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 48272dce..120ab251 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,121 +1,174 @@ -import { PipelineDataProvider, PipelineRunTreeItem, PipelineTreeItem, ServerDataProvider } from '../views/activityBar'; -import { StackComponentTreeItem } from '../views/activityBar'; import * as vscode from 'vscode'; +import { TokenJS } from 'token.js'; +import { + PipelineDataProvider, + PipelineRunTreeItem, + PipelineTreeItem, + ServerDataProvider, + StackDataProvider, + StackComponentTreeItem, +} from '../views/activityBar'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; -import { TokenJS } from 'token.js' +// import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; export class ChatService { - private static instance: ChatService; - private initialized: Promise; - private tokenjs: any; + private static instance: ChatService; + private initialized: Promise; + private tokenjs: any; + private allMessages: string[]; - private constructor() { - this.initialized = this.initialize(); - } + private constructor() { + this.initialized = this.initialize(); + } - public static getInstance(): ChatService { - if (!ChatService.instance) { - ChatService.instance = new ChatService(); - } - return ChatService.instance; + public static getInstance(): ChatService { + if (!ChatService.instance) { + ChatService.instance = new ChatService(); } + return ChatService.instance; + } - private async initialize() { - // Use dynamic import to load the ESM module - // const { TokenJS } = await import('token.js'); - - // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = ''; - if (!apiKey) { - throw new Error('GEMINI_API_KEY is not set'); - } - this.tokenjs = new TokenJS({ apiKey }); + private async initialize() { + // TODO find another way to access the apiKey, instead of having it hardcoded + const apiKey = ''; + if (!apiKey) { + throw new Error('GEMINI_API_KEY is not set'); } + this.tokenjs = new TokenJS({ apiKey }); + } - public async getChatResponse(message: string): Promise { - //Recreate or copy from langchain - //minimize everything when you enter a question - //markdown editor - //tooltips for what the context does - //sample questions - //prompt engineering (system prompt) - //syntax like @stacks for context - //tests - //Stack Data Provider and Panel Data Provider need to be implemented - try { - let context = '' - if (message.includes('environment')) { - context += this.getEnvironmentData() - } - if (message.includes('pipeline')) { - context += this.getPipelineData() - } - if (message.includes('stack')) { - context += this.getStackComponentData() - } - if (message.includes('server')) { - context += this.getServerStatus() - } - - const completion = await this.tokenjs.chat.completions.create({ - provider: 'gemini', - model: 'gemini-1.5-flash', - messages: [{ role: 'user', content: (message + `Use this context to answer the question: ${context}`) }], - }); - return completion.choices[0]?.message?.content || 'No content'; - - } catch (error) { - console.error('Error with Gemini API:', error); - return 'Error: Unable to get a response from Gemini.'; - } + public async getChatResponse(message: string): Promise { + //Recreate or copy from langchain + //minimize everything when you enter a question + //markdown editor + //tooltips for what the context does + //sample questions + //prompt engineering (system prompt) + //syntax like @stacks for context + //tests + //Stack Data Provider and Panel Data Provider need to be implemented + try { + let context = ''; + if (message.includes('environment')) { + context += this.getEnvironmentData(); + } + if (message.includes('pipeline')) { + context += this.getPipelineData(); + } + if (message.includes('stack')) { + context += this.getStackComponentData(); + context += this.getStackData(); + } + if (message.includes('server')) { + context += this.getServerStatus(); + } + // if (message.includes('panel')) { + // this.getPanelData(); + // } + + const completion = await this.tokenjs.chat.completions.create({ + provider: 'gemini', + model: 'gemini-1.5-flash', + messages: [ + { + role: 'user', + content: message + `Use this context to answer the question: ${context}`, + }, + ], + }); + return completion.choices[0]?.message?.content || 'No content'; + } catch (error) { + console.error('Error with Gemini API:', error); + return 'Error: Unable to get a response from Gemini.'; } + } // private getContext() { // } + private addMessage(message: string): void { + this.allMessages.push(message); + } + + private getMessages(): string[] {} + + /** + * + * @returns A parsed string containing the information of the server. + */ private getServerStatus(): string { - return (`Server Status Data:\n` + JSON.stringify(ServerDataProvider.getInstance().getCurrentStatus()) + '\n'); + let serverData = ServerDataProvider.getInstance().getCurrentStatus(); + let contextString = + `URL: ${serverData.url}\n` + + `Dashboard URL: ${serverData.dashboard_url}\n` + + `Version: ${serverData.version}\n` + + `Store Type: ${serverData.store_type}\n` + + `Deployment Type: ${serverData.deployment_type}\n` + + `Database Type: ${serverData.database_type}\n` + + `Secrets Store Type: ${serverData.secrets_store_type}\n` + + `ID: ${serverData.id}\n` + + `Debug: ${serverData.debug}\n` + + `Auth Scheme ${serverData.auth_scheme}`; + return `Server Status Data:\n${contextString}\n`; } private getStackComponentData(): string { let components = ComponentDataProvider.getInstance().items; - let componentData = components.map((item: vscode.TreeItem) => { - if (item instanceof StackComponentTreeItem) { - let { name, type, flavor, id } = item.component; - let stackId = item.stackId; - let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; - let componentId = ` - Component ID: ${id}`; - return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; - } else { - return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; - } - }).join('\n'); + let componentData = components + .map((item: vscode.TreeItem) => { + if (item instanceof StackComponentTreeItem) { + let { name, type, flavor, id } = item.component; + let stackId = item.stackId; + let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; + let componentId = ` - Component ID: ${id}`; + return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; + } else { + return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; + } + }) + .join('\n'); return `Stack Component Data:\n${componentData}\n`; } private getEnvironmentData(): string { - let data = EnvironmentDataProvider.getInstance().getEnvironmentData() - let contextString = data.map((item) => `${item.label}: ${item.description || ''}`).join('\n'); + let environmentData = EnvironmentDataProvider.getInstance().getEnvironmentData(); + let contextString = environmentData + .map(item => `${item.label}: ${item.description || ''}`) + .join('\n'); return `Environment Data:\n${contextString}\n`; } private getPipelineData(): string { - let pipelineData = PipelineDataProvider.getInstance().getPipelineData() - let contextString = pipelineData.map((pipelineRun: PipelineTreeItem) => { - return (`Pipeline Run:\n` + pipelineRun.children?.map((item: PipelineRunTreeItem) => { - return `${item.tooltip}` - }).join('\n') + `\n${pipelineRun.description}`) - }).join('\n') + let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); + let contextString = pipelineData + .map((pipelineRun: PipelineTreeItem) => { + return ( + `Pipeline Run:\n` + + pipelineRun.children + ?.map((item: PipelineRunTreeItem) => { + return `${item.tooltip}`; + }) + .join('\n') + + `\n${pipelineRun.description}` + ); + }) + .join('\n'); return `Pipeline Data:\n${contextString}\n`; } - // private getStackData(): string { - - // } - - // private getPanelData(): { + private getStackData(): string { + let stackData = StackDataProvider.getInstance().items; + let contextString = stackData + .map(item => `Name: ${item.label}\n` + `ID: ${item.id}\n` + `Active: ${item.isActive}`) + .join('\n'); + return `Stack Data:\n${contextString}\n`; + } - // } + // private getPanelData(): string { + // let panelData = PanelDataProvider.getInstance(); + // console.log(panelData); + // return ``; + // } } diff --git a/src/views/activityBar/environmentView/EnvironmentDataProvider.ts b/src/views/activityBar/environmentView/EnvironmentDataProvider.ts index c4ca5ce9..c97a9297 100644 --- a/src/views/activityBar/environmentView/EnvironmentDataProvider.ts +++ b/src/views/activityBar/environmentView/EnvironmentDataProvider.ts @@ -139,7 +139,7 @@ export class EnvironmentDataProvider implements TreeDataProvider { } public getEnvironmentData(): EnvironmentItem[] { - return this.items + return this.items; } /** diff --git a/src/views/activityBar/pipelineView/PipelineDataProvider.ts b/src/views/activityBar/pipelineView/PipelineDataProvider.ts index 80a99346..eb30010e 100644 --- a/src/views/activityBar/pipelineView/PipelineDataProvider.ts +++ b/src/views/activityBar/pipelineView/PipelineDataProvider.ts @@ -162,7 +162,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { return new PipelineTreeItem(run, run.id, children); }); - return this.pipelineData + return this.pipelineData; } else { console.error(`Unexpected response format:`, result); return []; @@ -178,7 +178,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { } } - public getPipelineData(): PipelineTreeItem[]{ - return this.pipelineData + public getPipelineData(): PipelineTreeItem[] { + return this.pipelineData; } -} +} From 34bbddb4fc5930827fc1df8c3c24898550dff479 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 23 Aug 2024 15:47:33 -0700 Subject: [PATCH 014/138] feat: added message history to chatbot Co-authored-by: William Yennie Co-authored-by: Nathaniel Xu --- src/services/chatService.ts | 67 +++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 120ab251..8498604c 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -10,16 +10,18 @@ import { } from '../views/activityBar'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; +import { request } from 'axios'; // import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; export class ChatService { private static instance: ChatService; private initialized: Promise; private tokenjs: any; - private allMessages: string[]; + private allMessages: object[]; private constructor() { this.initialized = this.initialize(); + this.allMessages = []; } public static getInstance(): ChatService { @@ -31,7 +33,7 @@ export class ChatService { private async initialize() { // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = ''; + const apiKey = 'AIzaSyAaYzVDRtuq9aFT91usYFQT8tO2HItM224'; if (!apiKey) { throw new Error('GEMINI_API_KEY is not set'); } @@ -49,34 +51,17 @@ export class ChatService { //tests //Stack Data Provider and Panel Data Provider need to be implemented try { - let context = ''; + this.addUserMessage(message); if (message.includes('environment')) { - context += this.getEnvironmentData(); + this.addContext('environment'); } - if (message.includes('pipeline')) { - context += this.getPipelineData(); - } - if (message.includes('stack')) { - context += this.getStackComponentData(); - context += this.getStackData(); - } - if (message.includes('server')) { - context += this.getServerStatus(); - } - // if (message.includes('panel')) { - // this.getPanelData(); - // } const completion = await this.tokenjs.chat.completions.create({ provider: 'gemini', model: 'gemini-1.5-flash', - messages: [ - { - role: 'user', - content: message + `Use this context to answer the question: ${context}`, - }, - ], + messages: this.allMessages, }); + return completion.choices[0]?.message?.content || 'No content'; } catch (error) { console.error('Error with Gemini API:', error); @@ -84,21 +69,45 @@ export class ChatService { } } - // private getContext() { - // } + private addContext(requestedContext: string): void { + let systemMessage = { role: 'system', content: "Use this context to answer the question. " }; + if (requestedContext === 'server') { + systemMessage.content += this.getServerData(); + } + if (requestedContext === 'environment') { + systemMessage.content += this.getEnvironmentData(); + } + if (requestedContext === 'pipeline') { + systemMessage.content += this.getPipelineData(); + } + if (requestedContext === 'stack_components') { + systemMessage.content += this.getStackComponentData(); + } + if (requestedContext === 'stack') { + systemMessage.content += this.getStackData(); + } + this.allMessages.push(systemMessage); + } + +// private addSystemMessage(message: string): void { +// let systemMessage = { role: 'system', content: message }; +// this.allMessages.push(systemMessage); +// } - private addMessage(message: string): void { - this.allMessages.push(message); + private addUserMessage(message: string): void { + let userMessage = { role: 'user', content: message }; + this.allMessages.push(userMessage); } - private getMessages(): string[] {} +// private getRecentMessages(): string[] { +// } /** * * @returns A parsed string containing the information of the server. */ - private getServerStatus(): string { + private getServerData(): string { let serverData = ServerDataProvider.getInstance().getCurrentStatus(); let contextString = `URL: ${serverData.url}\n` + From 9ffc4fab9d8667dfee65424d58b8c35f08ecf47e Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Mon, 26 Aug 2024 10:56:12 -0700 Subject: [PATCH 015/138] fix: limits the amount remembered Co-authored-by: William Yennie Co-authored-by: Nathaniel Xu --- src/services/chatService.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 8498604c..32730bdd 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -33,7 +33,7 @@ export class ChatService { private async initialize() { // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = 'AIzaSyAaYzVDRtuq9aFT91usYFQT8tO2HItM224'; + const apiKey = ''; if (!apiKey) { throw new Error('GEMINI_API_KEY is not set'); } @@ -59,7 +59,7 @@ export class ChatService { const completion = await this.tokenjs.chat.completions.create({ provider: 'gemini', model: 'gemini-1.5-flash', - messages: this.allMessages, + messages: this.getRecentMessages(), }); return completion.choices[0]?.message?.content || 'No content'; @@ -100,8 +100,17 @@ export class ChatService { this.allMessages.push(userMessage); } -// private getRecentMessages(): string[] { -// } + private getRecentMessages(): object[] { + let recentMessages: object[]; + + if (this.allMessages.length > 10) { + recentMessages = this.allMessages.slice(-10); + } else { + recentMessages = this.allMessages; + } + + return recentMessages; + } /** * From c25fa8ef132f25452b4ba79cdb337652acc96f05 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 23 Aug 2024 21:49:29 -0400 Subject: [PATCH 016/138] feat: added markdown renderer using markedjs --- package-lock.json | 12 ++++++++ package.json | 1 + src/extension.ts | 61 +++++++++++++++++++------------------ src/services/chatService.ts | 11 +------ 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9f8bc7f8..bcbe1773 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "fs-extra": "^11.2.0", "hbs": "^4.2.0", "langchain": "^0.2.16", + "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", "token.js": "^0.4.3", @@ -6007,6 +6008,17 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/marked": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", + "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", diff --git a/package.json b/package.json index 86d2e8aa..c33fd03b 100644 --- a/package.json +++ b/package.json @@ -520,6 +520,7 @@ "fs-extra": "^11.2.0", "hbs": "^4.2.0", "langchain": "^0.2.16", + "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", "token.js": "^0.4.3", diff --git a/src/extension.ts b/src/extension.ts index 5832f208..fc36ad01 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,7 @@ import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; import { ChatService } from './services/chatService'; +import { marked } from 'marked'; export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); @@ -106,45 +107,45 @@ class ChatViewProvider implements vscode.WebviewViewProvider { // Generate the Webview content getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); - const chatLogHtml = this.messages.map(msg => `

${msg}

`).join(''); + const chatLogHtml = this.messages.join(''); return ` - - - - - ZenML Chat - - - -
${chatLogHtml}
-
- - -
- - -`; + + + + + ZenML Chat + + + +
${chatLogHtml}
+
+ + +
+ + + `; } // Add a new message to the chat log and send to Gemini async addMessage(message: string) { - this.messages.push(`User: ${message}`); // Add the message to the log + this.messages.push(`User: ${marked.parse(message)}`); // Add the message to the log // Get the bot's response and add it to the log const botResponse = await this.chatService.getChatResponse(message); - this.messages.push(`Gemini: ${botResponse}`); + this.messages.push(`Gemini: ${marked.parse(botResponse)}`); // Re-render the Webview content this._view && diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 32730bdd..e4815146 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -40,16 +40,7 @@ export class ChatService { this.tokenjs = new TokenJS({ apiKey }); } - public async getChatResponse(message: string): Promise { - //Recreate or copy from langchain - //minimize everything when you enter a question - //markdown editor - //tooltips for what the context does - //sample questions - //prompt engineering (system prompt) - //syntax like @stacks for context - //tests - //Stack Data Provider and Panel Data Provider need to be implemented + public async getChatResponse(message: string): Promise { try { this.addUserMessage(message); if (message.includes('environment')) { From b2e490e21b29ab18a4f8354e23a4cf5d5ecdb727 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 25 Aug 2024 16:02:43 -0400 Subject: [PATCH 017/138] refactor(ChatViewProvider): seperate out HTML into a new file and refactor logic --- media/chat.html | 29 ++++++++++ src/extension.ts | 138 +++++++++++++++++++++++++++-------------------- 2 files changed, 110 insertions(+), 57 deletions(-) create mode 100644 media/chat.html diff --git a/media/chat.html b/media/chat.html new file mode 100644 index 00000000..69e07e69 --- /dev/null +++ b/media/chat.html @@ -0,0 +1,29 @@ + + + + + + + ZenML Chat + + + +
${chatLogHtml}
+
+ + +
+ + + diff --git a/src/extension.ts b/src/extension.ts index fc36ad01..39fa21b4 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,6 +11,7 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. import * as vscode from 'vscode'; +import * as fs from 'fs'; import { EventBus } from './services/EventBus'; import { LSClient } from './services/LSClient'; import { ZenExtension } from './services/ZenExtension'; @@ -22,7 +23,6 @@ import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; import { ChatService } from './services/chatService'; -import { marked } from 'marked'; export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); @@ -73,8 +73,9 @@ export async function deactivate(): Promise { DagRenderer.getInstance()?.deactivate(); } -// TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder -// +/** + * TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder + */ class ChatViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; private messages: string[] = []; // Array to store chat messages @@ -82,80 +83,103 @@ class ChatViewProvider implements vscode.WebviewViewProvider { constructor(private readonly context: vscode.ExtensionContext) {} + /** + * Called when the webview is resolved. Initializes the webview content and sets up the message handling. + */ resolveWebviewView( webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, _token: vscode.CancellationToken ) { this._view = webviewView; + this.configureWebViewOptions(webviewView.webview); + this.updateWebviewContent(); + + // Handle messages recieved from the webview + webviewView.webview.onDidReceiveMessage(async message => { + this.addMessage(message.text); + }); + } - webviewView.webview.options = { + /** + * Configure the webview to allow scripts to run + */ + private configureWebViewOptions(webview: vscode.Webview) { + webview.options = { enableScripts: true, }; + } - webviewView.webview.html = this.getWebviewContent( - webviewView.webview, - this.context.extensionUri - ); - webviewView.webview.onDidReceiveMessage(async message => { - if (message.command === 'sendMessage') { - await this.addMessage(message.text); - } - }); + /** + * Handle incomming messages from the webview + */ + private async handleWebViewMessages(message: any) { + if (message.command === 'sendMessage' && message.text?.trim()) { + await this.addMessage(message.text); + } } - // Generate the Webview content - getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + /** + * Generate the webview HTML content, including the chat log and the input elements + */ + private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + const htmlPath = vscode.Uri.joinPath(extensionUri, 'media', 'chat.html'); + let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); const chatLogHtml = this.messages.join(''); - return ` - - - - - ZenML Chat - - - -
${chatLogHtml}
-
- - -
- - - `; - } + // Replace placeholders in HTML with actual values + html = html.replace('${cssUri}', cssUri.toString()); + html = html.replace('${chatLogHtml}', chatLogHtml); - // Add a new message to the chat log and send to Gemini - async addMessage(message: string) { - this.messages.push(`User: ${marked.parse(message)}`); // Add the message to the log + return html; + } - // Get the bot's response and add it to the log - const botResponse = await this.chatService.getChatResponse(message); - this.messages.push(`Gemini: ${marked.parse(botResponse)}`); + /** + * Render the chat log as HTML. + */ + private renderChatLog(): string { + return this.messages.map(msg => `

${msg}

`).join(''); + } - // Re-render the Webview content - this._view && - (this._view.webview.html = this.getWebviewContent( + /** + * Update the webview with the latest content, including the chat message. + */ + private updateWebviewContent() { + if (this._view) { + this._view.webview.html = this.getWebviewContent( this._view.webview, this.context.extensionUri - )); // Re-render the Webview + ); + } + } - // Post the bot's response back to the webview - this._view && - this._view.webview.postMessage({ command: 'receiveMessage', text: `Gemini: ${botResponse}` }); + /** + * Add a new message to the chat log, get a response, and update the webview + */ + async addMessage(message: string) { + // Add the message to the log + this.messages.push(`User: ${message}`); + + // Get the bot's response and add it to the log + try { + const botResponse = await this.chatService.getChatResponse(message); + this.messages.push(`Gemini: ${(botResponse)}`); + this.updateWebviewContent(); + this.sendMessageToWebview(`Gemini: ${botResponse}`); + } catch (error) { + console.error("Error getting Gemini's response:", error); + this.messages.push("Error: Unable to get response from Gemini"); + } + } + + /** + * Send a message from Gemini back to the webview + */ + private sendMessageToWebview(message: string) { + if (this._view) { + this._view.webview.postMessage({ command: 'recieveMessage', text: message}); + } } } From 76dbef8c8326e874dca22182358a6a85c7209b20 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 25 Aug 2024 17:26:06 -0400 Subject: [PATCH 018/138] feat: updated chat styling for better readability and user experience --- media/chat.css | 50 +++++++++++++++++++++++++++++++++++++-------- media/chat.html | 30 ++++++++++----------------- media/chat.js | 53 +++++++++++++++++++++++++++++------------------- src/extension.ts | 50 +++++++++++++++++++++++++++++---------------- 4 files changed, 117 insertions(+), 66 deletions(-) diff --git a/media/chat.css b/media/chat.css index dbdf736f..85b0c644 100644 --- a/media/chat.css +++ b/media/chat.css @@ -2,7 +2,7 @@ body { font-family: Arial, sans-serif; margin: 0; padding: 0; - background-color: #0000007a; + background-color: #2e2e2e; } #chat-container { @@ -15,22 +15,35 @@ body { flex: 1; overflow-y: auto; padding: 10px; - background-color: rgb(238, 226, 226); - border-bottom: 1px solid #030303; + background-color: #3c3c3c; + border-bottom: 1px solid #1a1a1a; } -.message { - padding: 5px; - margin: 5px 0; - background-color: #f7f7f7b7; +#messageInput { + flex: 1; + padding: 10px; + font-size: 14px; + border: 1px solid #555; border-radius: 5px; + background-color: #2c2c2c; + color: white; +} + +.user-message { + background-color: #d1ffd1; + align-self: flex-end; +} + +.gemini-message { + background-color: #e0e0ff; + align-self: flex-start; } #input-container { display: flex; padding: 10px; - background-color: #2c2c2c70; - border-top: 1px solid #1a1919e7; + background-color: #333; + border-top: 1px solid #1a1a1a; } #input { @@ -45,3 +58,22 @@ body { font-size: 14px; cursor: pointer; } + + +.message { + padding: 10px; + margin: 5px 0; + background-color: #4a4a4a; + border-radius: 10px; + color: white; +} + +#sendMessage { + margin-left: 10px; + padding: 10px; + background-color: #007acc; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; +} diff --git a/media/chat.html b/media/chat.html index 69e07e69..f4ee3aad 100644 --- a/media/chat.html +++ b/media/chat.html @@ -1,29 +1,21 @@ - - ZenML Chat - + Chat + -
${chatLogHtml}
-
- - +
+
+ ${chatLogHtml} +
+
+ + +
- + diff --git a/media/chat.js b/media/chat.js index a4f090b8..0efbd92a 100644 --- a/media/chat.js +++ b/media/chat.js @@ -1,24 +1,35 @@ (function() { const vscode = acquireVsCodeApi(); - - document.getElementById('send').addEventListener('click', () => { - const input = document.getElementById('input'); - if (input.value.trim()) { - const message = input.value; - input.value = ''; - - // Post the message back to the extension - vscode.postMessage({ - command: 'sendMessage', - text: message - }); - - // Append the message to the chat log - const messagesDiv = document.getElementById('messages'); - const messageDiv = document.createElement('div'); - messageDiv.className = 'message'; - messageDiv.textContent = message; - messagesDiv.appendChild(messageDiv); - } + + // Event listener for the send button + document.getElementById('sendMessage').addEventListener('click', () => { + const input = document.getElementById('messageInput'); + const message = input.value.trim(); + + // Ensure there's a message before sending + if (message) { + // Post the message to the VSCode extension + vscode.postMessage({ + command: 'sendMessage', + text: message + }); + + // Clear the input field + input.value = ''; + } }); -})(); + + // Handle receiving messages from the VSCode extension + window.addEventListener('message', event => { + const message = event.data; + + if (message.command === 'receiveMessage') { + const messagesDiv = document.getElementById('messages'); + const messageDiv = document.createElement('div'); + messageDiv.className = 'message gemini-message'; + messageDiv.textContent = message.text; + messagesDiv.appendChild(messageDiv); + } + }); + })(); + \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index 39fa21b4..d0b975cf 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -12,6 +12,7 @@ // permissions and limitations under the License. import * as vscode from 'vscode'; import * as fs from 'fs'; +import * as path from 'path'; import { EventBus } from './services/EventBus'; import { LSClient } from './services/LSClient'; import { ZenExtension } from './services/ZenExtension'; @@ -73,9 +74,9 @@ export async function deactivate(): Promise { DagRenderer.getInstance()?.deactivate(); } -/** - * TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder - */ + +// TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder +// class ChatViewProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; private messages: string[] = []; // Array to store chat messages @@ -84,7 +85,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { constructor(private readonly context: vscode.ExtensionContext) {} /** - * Called when the webview is resolved. Initializes the webview content and sets up the message handling. + * Called when the webview is resolved. Initializes the webview content and sets up the message handling */ resolveWebviewView( webviewView: vscode.WebviewView, @@ -95,9 +96,9 @@ class ChatViewProvider implements vscode.WebviewViewProvider { this.configureWebViewOptions(webviewView.webview); this.updateWebviewContent(); - // Handle messages recieved from the webview + // Handle messages received from the webview webviewView.webview.onDidReceiveMessage(async message => { - this.addMessage(message.text); + await this.handleWebviewMessage(message); }); } @@ -109,30 +110,39 @@ class ChatViewProvider implements vscode.WebviewViewProvider { enableScripts: true, }; } - + /** - * Handle incomming messages from the webview + * Handle incoming messages from the webview. */ - private async handleWebViewMessages(message: any) { + private async handleWebviewMessage(message: any) { + console.log("Received message from webview:", message); + if (message.command === 'sendMessage' && message.text?.trim()) { + console.log("Handling 'sendMessage' command with text:", message.text); await this.addMessage(message.text); } } /** - * Generate the webview HTML content, including the chat log and the input elements + * Generate the webview HTML content, including the chat log and the input elements. */ private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + // Path to HTML file const htmlPath = vscode.Uri.joinPath(extensionUri, 'media', 'chat.html'); let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + // Webview URIs for CSS and JS const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); - const chatLogHtml = this.messages.join(''); + const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.js')); + + // Chat log HTML + const chatLogHtml = this.renderChatLog(); - // Replace placeholders in HTML with actual values + // Replace placeholders in the HTML with actual values html = html.replace('${cssUri}', cssUri.toString()); + html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${chatLogHtml}', chatLogHtml); - + return html; } @@ -140,7 +150,11 @@ class ChatViewProvider implements vscode.WebviewViewProvider { * Render the chat log as HTML. */ private renderChatLog(): string { - return this.messages.map(msg => `

${msg}

`).join(''); + return this.messages.map(msg => { + const isUserMessage = msg.startsWith('User:'); + const className = isUserMessage ? 'user-message' : 'gemini-message'; + return `
${msg}
`; + }).join(''); } /** @@ -156,21 +170,22 @@ class ChatViewProvider implements vscode.WebviewViewProvider { } /** - * Add a new message to the chat log, get a response, and update the webview + * Add a message to the chat log, get a response from Gemini, and update the webview. */ async addMessage(message: string) { // Add the message to the log this.messages.push(`User: ${message}`); - // Get the bot's response and add it to the log + // Get Gemini's response try { const botResponse = await this.chatService.getChatResponse(message); - this.messages.push(`Gemini: ${(botResponse)}`); + this.messages.push(`Gemini: ${botResponse}`); this.updateWebviewContent(); this.sendMessageToWebview(`Gemini: ${botResponse}`); } catch (error) { console.error("Error getting Gemini's response:", error); this.messages.push("Error: Unable to get response from Gemini"); + this.updateWebviewContent(); } } @@ -179,6 +194,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { */ private sendMessageToWebview(message: string) { if (this._view) { + console.log("Sending message to webview:", message); this._view.webview.postMessage({ command: 'recieveMessage', text: message}); } } From 7f640ebb198ad8507acdc8e9a2bdc7cc66d76c59 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 25 Aug 2024 17:32:33 -0400 Subject: [PATCH 019/138] feat: added Enter key support for sending messasges in chat input --- media/chat.js | 57 ++++++++++++++++++++++++++------------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/media/chat.js b/media/chat.js index 0efbd92a..1d3715a3 100644 --- a/media/chat.js +++ b/media/chat.js @@ -1,35 +1,36 @@ (function() { const vscode = acquireVsCodeApi(); - // Event listener for the send button - document.getElementById('sendMessage').addEventListener('click', () => { - const input = document.getElementById('messageInput'); - const message = input.value.trim(); + // Function to send the message + function sendMessage() { + const input = document.getElementById('messageInput'); + if (input.value.trim()) { + const message = input.value; + input.value = ''; // Clear input - // Ensure there's a message before sending - if (message) { - // Post the message to the VSCode extension - vscode.postMessage({ - command: 'sendMessage', - text: message - }); + // Post the message back to the extension + vscode.postMessage({ + command: 'sendMessage', + text: message + }); - // Clear the input field - input.value = ''; - } - }); + // Append the message to the chat log + const messagesDiv = document.getElementById('chatLog'); + const messageDiv = document.createElement('div'); + messageDiv.className = 'message'; + messageDiv.textContent = message; + messagesDiv.appendChild(messageDiv); + } + } + + // Click event for the send button + document.getElementById('sendMessage').addEventListener('click', sendMessage); - // Handle receiving messages from the VSCode extension - window.addEventListener('message', event => { - const message = event.data; - - if (message.command === 'receiveMessage') { - const messagesDiv = document.getElementById('messages'); - const messageDiv = document.createElement('div'); - messageDiv.className = 'message gemini-message'; - messageDiv.textContent = message.text; - messagesDiv.appendChild(messageDiv); - } + // Keydown event for the Enter key + document.getElementById('messageInput').addEventListener('keydown', (event) => { + if (event.key === 'Enter') { + event.preventDefault(); // Prevent default Enter key behavior (e.g., newline) + sendMessage(); + } }); - })(); - \ No newline at end of file + })(); \ No newline at end of file From 5013298a6ba5595d69569e690cee261202054b1b Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 25 Aug 2024 18:03:34 -0400 Subject: [PATCH 020/138] feat: add markdown support to chat messages Integrated `marked` library to convert Markdown text to HTML. Updated `ChatViewProvider` to process and render Markdown in chat message. Modified `renderChatLog` to parse Markdown before displaying message. --- media/chat.html | 1 + media/chat.js | 4 ++-- package-lock.json | 1 + src/extension.ts | 10 +++++----- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/media/chat.html b/media/chat.html index f4ee3aad..c5e0b0d6 100644 --- a/media/chat.html +++ b/media/chat.html @@ -17,5 +17,6 @@
+ diff --git a/media/chat.js b/media/chat.js index 1d3715a3..851e3a09 100644 --- a/media/chat.js +++ b/media/chat.js @@ -15,10 +15,10 @@ }); // Append the message to the chat log - const messagesDiv = document.getElementById('chatLog'); + const messagesDiv = document.getElementById('messages'); const messageDiv = document.createElement('div'); messageDiv.className = 'message'; - messageDiv.textContent = message; + messageDiv.innerHTML = marked(message); messagesDiv.appendChild(messageDiv); } } diff --git a/package-lock.json b/package-lock.json index bcbe1773..3f9c21c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6012,6 +6012,7 @@ "version": "14.0.0", "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "license": "MIT", "bin": { "marked": "bin/marked.js" }, diff --git a/src/extension.ts b/src/extension.ts index d0b975cf..da58511d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -13,6 +13,7 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; +import * as marked from 'marked'; import { EventBus } from './services/EventBus'; import { LSClient } from './services/LSClient'; import { ZenExtension } from './services/ZenExtension'; @@ -115,8 +116,6 @@ class ChatViewProvider implements vscode.WebviewViewProvider { * Handle incoming messages from the webview. */ private async handleWebviewMessage(message: any) { - console.log("Received message from webview:", message); - if (message.command === 'sendMessage' && message.text?.trim()) { console.log("Handling 'sendMessage' command with text:", message.text); await this.addMessage(message.text); @@ -134,6 +133,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { // Webview URIs for CSS and JS const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.js')); + const markedUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'marked.min.js')); // Chat log HTML const chatLogHtml = this.renderChatLog(); @@ -141,6 +141,7 @@ class ChatViewProvider implements vscode.WebviewViewProvider { // Replace placeholders in the HTML with actual values html = html.replace('${cssUri}', cssUri.toString()); html = html.replace('${jsUri}', jsUri.toString()); + html = html.replace('${markedUri}', markedUri.toString()); html = html.replace('${chatLogHtml}', chatLogHtml); return html; @@ -153,7 +154,8 @@ class ChatViewProvider implements vscode.WebviewViewProvider { return this.messages.map(msg => { const isUserMessage = msg.startsWith('User:'); const className = isUserMessage ? 'user-message' : 'gemini-message'; - return `
${msg}
`; + const htmlMessage = marked.parse(msg.replace(/^(User:|Gemini:)\s*/, '')); + return `
${htmlMessage}
`; }).join(''); } @@ -183,7 +185,6 @@ class ChatViewProvider implements vscode.WebviewViewProvider { this.updateWebviewContent(); this.sendMessageToWebview(`Gemini: ${botResponse}`); } catch (error) { - console.error("Error getting Gemini's response:", error); this.messages.push("Error: Unable to get response from Gemini"); this.updateWebviewContent(); } @@ -194,7 +195,6 @@ class ChatViewProvider implements vscode.WebviewViewProvider { */ private sendMessageToWebview(message: string) { if (this._view) { - console.log("Sending message to webview:", message); this._view.webview.postMessage({ command: 'recieveMessage', text: message}); } } From 9e7e3b38757cf42a4dd16d5fd103a6ff0ff8fbee Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Mon, 26 Aug 2024 13:12:33 -0400 Subject: [PATCH 021/138] faat(chat): rename ChatViewProvider to ChatDataProvider and restructure files Renamed ChatViewProvider to ChatDataProvider for consistency with other files Moved ChatDataProvider class from extension.ts to src/activityBar/chatView Moved chat.js, chat.html, and chat.css to resources/chat-view --- {media => resources/chat-view}/chat.css | 0 {media => resources/chat-view}/chat.html | 0 {media => resources/chat-view}/chat.js | 0 src/extension.ts | 133 +----------------- .../activityBar/chatView/ChatDataProvider.ts | 127 +++++++++++++++++ 5 files changed, 130 insertions(+), 130 deletions(-) rename {media => resources/chat-view}/chat.css (100%) rename {media => resources/chat-view}/chat.html (100%) rename {media => resources/chat-view}/chat.js (100%) create mode 100644 src/views/activityBar/chatView/ChatDataProvider.ts diff --git a/media/chat.css b/resources/chat-view/chat.css similarity index 100% rename from media/chat.css rename to resources/chat-view/chat.css diff --git a/media/chat.html b/resources/chat-view/chat.html similarity index 100% rename from media/chat.html rename to resources/chat-view/chat.html diff --git a/media/chat.js b/resources/chat-view/chat.js similarity index 100% rename from media/chat.js rename to resources/chat-view/chat.js diff --git a/src/extension.ts b/src/extension.ts index da58511d..236d858c 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -11,20 +11,18 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. import * as vscode from 'vscode'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as marked from 'marked'; import { EventBus } from './services/EventBus'; import { LSClient } from './services/LSClient'; import { ZenExtension } from './services/ZenExtension'; import { refreshUIComponents } from './utils/refresh'; import { EnvironmentDataProvider } from './views/activityBar/environmentView/EnvironmentDataProvider'; +import { ChatDataProvider } from './views/activityBar/chatView/ChatDataProvider'; import { registerEnvironmentCommands } from './commands/environment/registry'; import { LSP_ZENML_CLIENT_INITIALIZED } from './utils/constants'; import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; -import { ChatService } from './services/chatService'; + export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); @@ -46,7 +44,7 @@ export async function activate(context: vscode.ExtensionContext) { registerEnvironmentCommands(context); context.subscriptions.push( - vscode.window.registerWebviewViewProvider('zenmlChatView', new ChatViewProvider(context)) + vscode.window.registerWebviewViewProvider('zenmlChatView', new ChatDataProvider(context)) ); await ZenExtension.activate(context, lsClient); @@ -74,128 +72,3 @@ export async function deactivate(): Promise { } DagRenderer.getInstance()?.deactivate(); } - - -// TODO: ChatViewProvider should be moved into it's own folder/file in the src/views/activityBar folder -// -class ChatViewProvider implements vscode.WebviewViewProvider { - private _view?: vscode.WebviewView; - private messages: string[] = []; // Array to store chat messages - private chatService: ChatService = ChatService.getInstance(); // ChatService instance - - constructor(private readonly context: vscode.ExtensionContext) {} - - /** - * Called when the webview is resolved. Initializes the webview content and sets up the message handling - */ - resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken - ) { - this._view = webviewView; - this.configureWebViewOptions(webviewView.webview); - this.updateWebviewContent(); - - // Handle messages received from the webview - webviewView.webview.onDidReceiveMessage(async message => { - await this.handleWebviewMessage(message); - }); - } - - /** - * Configure the webview to allow scripts to run - */ - private configureWebViewOptions(webview: vscode.Webview) { - webview.options = { - enableScripts: true, - }; - } - - /** - * Handle incoming messages from the webview. - */ - private async handleWebviewMessage(message: any) { - if (message.command === 'sendMessage' && message.text?.trim()) { - console.log("Handling 'sendMessage' command with text:", message.text); - await this.addMessage(message.text); - } - } - - /** - * Generate the webview HTML content, including the chat log and the input elements. - */ - private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { - // Path to HTML file - const htmlPath = vscode.Uri.joinPath(extensionUri, 'media', 'chat.html'); - let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); - - // Webview URIs for CSS and JS - const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.css')); - const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'chat.js')); - const markedUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'media', 'marked.min.js')); - - // Chat log HTML - const chatLogHtml = this.renderChatLog(); - - // Replace placeholders in the HTML with actual values - html = html.replace('${cssUri}', cssUri.toString()); - html = html.replace('${jsUri}', jsUri.toString()); - html = html.replace('${markedUri}', markedUri.toString()); - html = html.replace('${chatLogHtml}', chatLogHtml); - - return html; - } - - /** - * Render the chat log as HTML. - */ - private renderChatLog(): string { - return this.messages.map(msg => { - const isUserMessage = msg.startsWith('User:'); - const className = isUserMessage ? 'user-message' : 'gemini-message'; - const htmlMessage = marked.parse(msg.replace(/^(User:|Gemini:)\s*/, '')); - return `
${htmlMessage}
`; - }).join(''); - } - - /** - * Update the webview with the latest content, including the chat message. - */ - private updateWebviewContent() { - if (this._view) { - this._view.webview.html = this.getWebviewContent( - this._view.webview, - this.context.extensionUri - ); - } - } - - /** - * Add a message to the chat log, get a response from Gemini, and update the webview. - */ - async addMessage(message: string) { - // Add the message to the log - this.messages.push(`User: ${message}`); - - // Get Gemini's response - try { - const botResponse = await this.chatService.getChatResponse(message); - this.messages.push(`Gemini: ${botResponse}`); - this.updateWebviewContent(); - this.sendMessageToWebview(`Gemini: ${botResponse}`); - } catch (error) { - this.messages.push("Error: Unable to get response from Gemini"); - this.updateWebviewContent(); - } - } - - /** - * Send a message from Gemini back to the webview - */ - private sendMessageToWebview(message: string) { - if (this._view) { - this._view.webview.postMessage({ command: 'recieveMessage', text: message}); - } - } -} diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts new file mode 100644 index 00000000..b925676e --- /dev/null +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -0,0 +1,127 @@ +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import * as marked from 'marked'; +import { ChatService } from "../../../services/chatService"; + +export class ChatDataProvider implements vscode.WebviewViewProvider { + private _view?: vscode.WebviewView; + private messages: string[] = []; // Array to store chat messages + private chatService: ChatService = ChatService.getInstance(); // ChatService instance + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Called when the webview is resolved. Initializes the webview content and sets up the message handling + */ + resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ) { + this._view = webviewView; + this.configureWebViewOptions(webviewView.webview); + this.updateWebviewContent(); + + // Handle messages received from the webview + webviewView.webview.onDidReceiveMessage(async message => { + await this.handleWebviewMessage(message); + }); + } + + /** + * Configure the webview to allow scripts to run + */ + private configureWebViewOptions(webview: vscode.Webview) { + webview.options = { + enableScripts: true, + }; + } + + /** + * Handle incoming messages from the webview. + */ + private async handleWebviewMessage(message: any) { + if (message.command === 'sendMessage' && message.text?.trim()) { + console.log("Handling 'sendMessage' command with text:", message.text); + await this.addMessage(message.text); + } + } + + /** + * Generate the webview HTML content, including the chat log and the input elements. + */ + private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + // Path to HTML file + const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); + let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + + // Webview URIs for CSS and JS + const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css')); + const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js')); + const markedUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'marked.min.js')); + + // Chat log HTML + const chatLogHtml = this.renderChatLog(); + + // Replace placeholders in the HTML with actual values + html = html.replace('${cssUri}', cssUri.toString()); + html = html.replace('${jsUri}', jsUri.toString()); + html = html.replace('${markedUri}', markedUri.toString()); + html = html.replace('${chatLogHtml}', chatLogHtml); + + return html; + } + + /** + * Render the chat log as HTML. + */ + private renderChatLog(): string { + return this.messages.map(msg => { + const isUserMessage = msg.startsWith('User:'); + const className = isUserMessage ? 'user-message' : 'gemini-message'; + const htmlMessage = marked.parse(msg.replace(/^(User:|Gemini:)\s*/, '')); + return `
${htmlMessage}
`; + }).join(''); + } + + /** + * Update the webview with the latest content, including the chat message. + */ + private updateWebviewContent() { + if (this._view) { + this._view.webview.html = this.getWebviewContent( + this._view.webview, + this.context.extensionUri + ); + } + } + + /** + * Add a message to the chat log, get a response from Gemini, and update the webview. + */ + async addMessage(message: string) { + // Add the message to the log + this.messages.push(`User: ${message}`); + + // Get Gemini's response + try { + const botResponse = await this.chatService.getChatResponse(message); + this.messages.push(`Gemini: ${botResponse}`); + this.updateWebviewContent(); + this.sendMessageToWebview(`Gemini: ${botResponse}`); + } catch (error) { + this.messages.push("Error: Unable to get response from Gemini"); + this.updateWebviewContent(); + } + } + + /** + * Send a message from Gemini back to the webview + */ + private sendMessageToWebview(message: string) { + if (this._view) { + this._view.webview.postMessage({ command: 'recieveMessage', text: message}); + } + } + } + \ No newline at end of file From 950d7153a004b0fde4daddaf39496335fc304082 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Mon, 26 Aug 2024 14:32:39 -0400 Subject: [PATCH 022/138] feat: added panel data connection --- src/services/chatService.ts | 35 ++++++++++++++----- .../panel/panelView/PanelDataProvider.ts | 4 +++ 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index e4815146..ba8d0fc4 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -10,8 +10,9 @@ import { } from '../views/activityBar'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; -import { request } from 'axios'; -// import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; +import { LSClient } from './LSClient'; +import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; +import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; export class ChatService { private static instance: ChatService; @@ -150,6 +151,7 @@ export class ChatService { } private getPipelineData(): string { + //Check if this.items works instead let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); let contextString = pipelineData .map((pipelineRun: PipelineTreeItem) => { @@ -175,9 +177,26 @@ export class ChatService { return `Stack Data:\n${contextString}\n`; } - // private getPanelData(): string { - // let panelData = PanelDataProvider.getInstance(); - // console.log(panelData); - // return ``; - // } -} + private async getPanelData(): Promise { + //Retrieve the run data through ls client requests + //TODO: + //Separate artifact/step data + //Separate source code data + let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); + let lsClient = LSClient.getInstance(); + let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { + return await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); + })); + console.log("DAG Data:", dagData); + let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { + return Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { + if (node.type == "step") { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } else { + return null; + } + }).filter(Boolean)); + })); + return JSON.stringify(stepData); + } +} \ No newline at end of file diff --git a/src/views/panel/panelView/PanelDataProvider.ts b/src/views/panel/panelView/PanelDataProvider.ts index ec7c4451..6f241ed7 100644 --- a/src/views/panel/panelView/PanelDataProvider.ts +++ b/src/views/panel/panelView/PanelDataProvider.ts @@ -100,4 +100,8 @@ export class PanelDataProvider implements TreeDataProvider { return [new PanelTreeItem(this.dataType, this.data)]; } + + public getData(): TreeItem | JsonObject { + return this.data + } } From 72ca4ceceee73db4e27901ebe91a62611fd8d9f8 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Mon, 26 Aug 2024 14:39:24 -0400 Subject: [PATCH 023/138] chore: removed unnecessary function --- src/views/panel/panelView/PanelDataProvider.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/views/panel/panelView/PanelDataProvider.ts b/src/views/panel/panelView/PanelDataProvider.ts index 6f241ed7..ec7c4451 100644 --- a/src/views/panel/panelView/PanelDataProvider.ts +++ b/src/views/panel/panelView/PanelDataProvider.ts @@ -100,8 +100,4 @@ export class PanelDataProvider implements TreeDataProvider { return [new PanelTreeItem(this.dataType, this.data)]; } - - public getData(): TreeItem | JsonObject { - return this.data - } } From 9d3c322064a90921ea5bfb6f48bdfbf73ab4fb9b Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Mon, 26 Aug 2024 17:27:07 -0700 Subject: [PATCH 024/138] feat: Buttons are added to the UI to select context --- resources/chat-view/chat.html | 28 +++++++- resources/chat-view/chat.js | 69 +++++++++---------- src/services/chatService.ts | 41 ++++++----- .../activityBar/chatView/ChatDataProvider.ts | 6 +- 4 files changed, 83 insertions(+), 61 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index c5e0b0d6..f63e94c9 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -12,8 +12,32 @@ ${chatLogHtml}
- - +
+
+ + + + + +
+ + +
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 851e3a09..30bc2294 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -1,36 +1,35 @@ -(function() { - const vscode = acquireVsCodeApi(); - - // Function to send the message - function sendMessage() { - const input = document.getElementById('messageInput'); - if (input.value.trim()) { - const message = input.value; - input.value = ''; // Clear input - - // Post the message back to the extension - vscode.postMessage({ - command: 'sendMessage', - text: message - }); - - // Append the message to the chat log - const messagesDiv = document.getElementById('messages'); - const messageDiv = document.createElement('div'); - messageDiv.className = 'message'; - messageDiv.innerHTML = marked(message); - messagesDiv.appendChild(messageDiv); - } +(function () { + const vscode = acquireVsCodeApi(); + + // Function to send the message + function sendMessage(event) { + event.preventDefault(); + const formData = new FormData(event.target); + const text = formData.get('messageInput').trim(); + const context = []; + + context.push(formData.get('serverContext')); + context.push(formData.get('environmentContext')); + context.push(formData.get('pipelineContext')); + context.push(formData.get('stackContext')); + context.push(formData.get('stackComponentsContext')); + + if (text) { + vscode.postMessage({ + command: 'sendMessage', + text: text, + context: context, + }); + + event.target.reset(); + + const messagesDiv = document.getElementById('messages'); + const messageDiv = document.createElement('div'); + messageDiv.className = 'message'; + messageDiv.innerHTML = marked(message); + messagesDiv.appendChild(messageDiv); } - - // Click event for the send button - document.getElementById('sendMessage').addEventListener('click', sendMessage); - - // Keydown event for the Enter key - document.getElementById('messageInput').addEventListener('keydown', (event) => { - if (event.key === 'Enter') { - event.preventDefault(); // Prevent default Enter key behavior (e.g., newline) - sendMessage(); - } - }); - })(); \ No newline at end of file + } + + document.getElementById('chatForm').addEventListener('submit', sendMessage); +})(); diff --git a/src/services/chatService.ts b/src/services/chatService.ts index ba8d0fc4..6a940a94 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -41,11 +41,11 @@ export class ChatService { this.tokenjs = new TokenJS({ apiKey }); } - public async getChatResponse(message: string): Promise { + public async getChatResponse(message: string, context?: string[]): Promise { try { this.addUserMessage(message); - if (message.includes('environment')) { - this.addContext('environment'); + if (context) { + this.addContext(context); } const completion = await this.tokenjs.chat.completions.create({ @@ -62,23 +62,23 @@ export class ChatService { } - private addContext(requestedContext: string): void { + private addContext(requestedContext: string[]): void { let systemMessage = { role: 'system', content: "Use this context to answer the question. " }; - if (requestedContext === 'server') { - systemMessage.content += this.getServerData(); - } - if (requestedContext === 'environment') { - systemMessage.content += this.getEnvironmentData(); - } - if (requestedContext === 'pipeline') { - systemMessage.content += this.getPipelineData(); - } - if (requestedContext === 'stack_components') { - systemMessage.content += this.getStackComponentData(); - } - if (requestedContext === 'stack') { - systemMessage.content += this.getStackData(); - } + if (requestedContext.includes('serverContext')) { + systemMessage.content += this.getServerData(); + } + if (requestedContext.includes('environmentContext')) { + systemMessage.content += this.getEnvironmentData(); + } + if (requestedContext.includes('pipelineContext')) { + systemMessage.content += this.getPipelineData(); + } + if (requestedContext.includes('stackContext')) { + systemMessage.content += this.getStackData(); +} + if (requestedContext.includes('stackComponentsContext')) { + systemMessage.content += this.getStackComponentData(); + } this.allMessages.push(systemMessage); } @@ -187,10 +187,9 @@ export class ChatService { let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { return await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); })); - console.log("DAG Data:", dagData); let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { return Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { - if (node.type == "step") { + if (node.type === "step") { return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); } else { return null; diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index b925676e..e565ab48 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -43,7 +43,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private async handleWebviewMessage(message: any) { if (message.command === 'sendMessage' && message.text?.trim()) { console.log("Handling 'sendMessage' command with text:", message.text); - await this.addMessage(message.text); + await this.addMessage(message.text, message.context); } } @@ -99,13 +99,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { /** * Add a message to the chat log, get a response from Gemini, and update the webview. */ - async addMessage(message: string) { + async addMessage(message: string, context?: string[]) { // Add the message to the log this.messages.push(`User: ${message}`); // Get Gemini's response try { - const botResponse = await this.chatService.getChatResponse(message); + const botResponse = await this.chatService.getChatResponse(message, context); this.messages.push(`Gemini: ${botResponse}`); this.updateWebviewContent(); this.sendMessageToWebview(`Gemini: ${botResponse}`); From 98b88554b185a66ece5a8e37dbe38db318e6cf36 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Tue, 27 Aug 2024 13:05:04 -0400 Subject: [PATCH 025/138] feat: get global config for api token removed typescript error Co-authored-by: William Yennie --- src/services/chatService.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 6a940a94..7b3723e2 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -110,18 +110,7 @@ export class ChatService { */ private getServerData(): string { let serverData = ServerDataProvider.getInstance().getCurrentStatus(); - let contextString = - `URL: ${serverData.url}\n` + - `Dashboard URL: ${serverData.dashboard_url}\n` + - `Version: ${serverData.version}\n` + - `Store Type: ${serverData.store_type}\n` + - `Deployment Type: ${serverData.deployment_type}\n` + - `Database Type: ${serverData.database_type}\n` + - `Secrets Store Type: ${serverData.secrets_store_type}\n` + - `ID: ${serverData.id}\n` + - `Debug: ${serverData.debug}\n` + - `Auth Scheme ${serverData.auth_scheme}`; - return `Server Status Data:\n${contextString}\n`; + return `Server Status Data:\n${JSON.stringify(serverData)}\n`; } private getStackComponentData(): string { @@ -198,4 +187,9 @@ export class ChatService { })); return JSON.stringify(stepData); } + + private async getLogData() { + let lsClient = LSClient.getInstance(); + return JSON.stringify(await lsClient.sendLsClientRequest('getGlobalConfig')) + } } \ No newline at end of file From a65bc0dcb585206fe5cca126ea8b6e992a4270f0 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Tue, 27 Aug 2024 16:52:59 -0400 Subject: [PATCH 026/138] feat: got log data for some pipeline runs from the dashboard api --- package-lock.json | 12 ++++++++++ package.json | 1 + src/services/chatService.ts | 38 +++++++++++++++++++++++++++++- src/types/LSClientResponseTypes.ts | 18 ++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 3f9c21c4..85f44b69 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "dagre": "^0.8.5", "fs-extra": "^11.2.0", "hbs": "^4.2.0", + "ky": "^1.7.1", "langchain": "^0.2.16", "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", @@ -5477,6 +5478,17 @@ "node": ">=0.10.0" } }, + "node_modules/ky": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.7.1.tgz", + "integrity": "sha512-KJ/IXXkFhTDqxcN8wKqMXk1/UoOpc0UnOB6H7QcqlPInh/M2B5Mlj+i9exez1w4RSwJhNFmHiUDPriAYFwb5VA==", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/ky?sponsor=1" + } + }, "node_modules/langchain": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.2.16.tgz", diff --git a/package.json b/package.json index c33fd03b..e90a59cf 100644 --- a/package.json +++ b/package.json @@ -519,6 +519,7 @@ "dagre": "^0.8.5", "fs-extra": "^11.2.0", "hbs": "^4.2.0", + "ky": "^1.7.1", "langchain": "^0.2.16", "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 7b3723e2..6cf51057 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,5 +1,6 @@ import * as vscode from 'vscode'; import { TokenJS } from 'token.js'; +import axios from 'axios'; import { PipelineDataProvider, PipelineRunTreeItem, @@ -13,6 +14,10 @@ import { EnvironmentDataProvider } from '../views/activityBar/environmentView/En import { LSClient } from './LSClient'; import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; +import { ZenmlGlobalConfigResp } from 'type_hints' +import { ZenmlStoreConfig } from '../types/LSClientResponseTypes'; +import { Z_SYNC_FLUSH } from 'zlib'; +import { setPriority } from 'os'; export class ChatService { private static instance: ChatService; @@ -166,6 +171,25 @@ export class ChatService { return `Stack Data:\n${contextString}\n`; } + private async getPipelineRunNodes(type: string) { + let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); + let lsClient = LSClient.getInstance(); + let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { + let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); + return dag + })); + let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { + let filteredNodes = await Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { + if (type === "all" || node.type === type) { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + })); + return filteredNodes.filter((value) => value !== null); + })); + return stepData + } + private async getPanelData(): Promise { //Retrieve the run data through ls client requests //TODO: @@ -190,6 +214,18 @@ export class ChatService { private async getLogData() { let lsClient = LSClient.getInstance(); - return JSON.stringify(await lsClient.sendLsClientRequest('getGlobalConfig')) + let dashboardUrl: string = ServerDataProvider.getInstance().getCurrentStatus().dashboard_url; + let apiToken: string = (await lsClient.sendLsClientRequest('getGlobalConfig')).store.api_token + let pipelineRunSteps = await this.getPipelineRunNodes('step') + let logs = await Promise.all(pipelineRunSteps[0].map(async (step) => { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + 'accept': 'application/json' + } + }) + return response.data + })) + return logs } } \ No newline at end of file diff --git a/src/types/LSClientResponseTypes.ts b/src/types/LSClientResponseTypes.ts index f86f1f80..30bd79ff 100644 --- a/src/types/LSClientResponseTypes.ts +++ b/src/types/LSClientResponseTypes.ts @@ -52,3 +52,21 @@ export interface ActiveStackResponse { export type SetActiveStackResponse = ActiveStackResponse | ErrorMessageResponse; export type GetActiveStackResponse = ActiveStackResponse | ErrorMessageResponse; + +/***** Global Config Type *****/ + +export interface ZenmlGlobalConfigResp { + user_id: string, + user_email: string, + analytics_opt_in: boolean, + version: string, + active_stack_id: string, + active_workspace_name: string, + store: ZenmlStoreConfig +} + +export interface ZenmlStoreConfig { + type: string, + url: string, + api_token?: string +} \ No newline at end of file From 386d3fc25ff8667c7821159450b422a00b097191 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 27 Aug 2024 15:04:00 -0700 Subject: [PATCH 027/138] feat: Added specific pipeline runs to chatbot UI --- resources/chat-view/chat.html | 4 + resources/chat-view/chat.js | 1 + src/extension.ts | 1 - src/services/chatService.ts | 80 +++--- .../activityBar/chatView/ChatDataProvider.ts | 232 +++++++++--------- 5 files changed, 173 insertions(+), 145 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index f63e94c9..59ee8039 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -34,6 +34,10 @@ Stack Components + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 30bc2294..a6fb9e56 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -13,6 +13,7 @@ context.push(formData.get('pipelineContext')); context.push(formData.get('stackContext')); context.push(formData.get('stackComponentsContext')); + context.push(formData.get('recentPipelineContext')); if (text) { vscode.postMessage({ diff --git a/src/extension.ts b/src/extension.ts index 236d858c..cd137b72 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,7 +23,6 @@ import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; - export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); const lsClient = LSClient.getInstance(); diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 7b3723e2..747c6b1b 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -61,31 +61,33 @@ export class ChatService { } } - private addContext(requestedContext: string[]): void { - let systemMessage = { role: 'system', content: "Use this context to answer the question. " }; + let systemMessage = { role: 'system', content: 'Use this context to answer the question. ' }; if (requestedContext.includes('serverContext')) { systemMessage.content += this.getServerData(); - } - if (requestedContext.includes('environmentContext')) { + } + if (requestedContext.includes('environmentContext')) { systemMessage.content += this.getEnvironmentData(); - } - if (requestedContext.includes('pipelineContext')) { + } + if (requestedContext.includes('pipelineContext')) { systemMessage.content += this.getPipelineData(); - } - if (requestedContext.includes('stackContext')) { - systemMessage.content += this.getStackData(); -} - if (requestedContext.includes('stackComponentsContext')) { + } + if (requestedContext.includes('stackContext')) { + systemMessage.content += this.getStackData(); + } + if (requestedContext.includes('stackComponentsContext')) { systemMessage.content += this.getStackComponentData(); - } + } + if (requestedContext.includes('recentPipelineContext')) { + systemMessage.content += this.getRecentPipelineRun(); + } this.allMessages.push(systemMessage); } -// private addSystemMessage(message: string): void { -// let systemMessage = { role: 'system', content: message }; -// this.allMessages.push(systemMessage); -// } + // private addSystemMessage(message: string): void { + // let systemMessage = { role: 'system', content: message }; + // this.allMessages.push(systemMessage); + // } private addUserMessage(message: string): void { let userMessage = { role: 'user', content: message }; @@ -168,28 +170,44 @@ export class ChatService { private async getPanelData(): Promise { //Retrieve the run data through ls client requests - //TODO: + //TODO: //Separate artifact/step data //Separate source code data let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); let lsClient = LSClient.getInstance(); - let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { - return await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - })); - let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { - return Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { - if (node.type === "step") { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } else { - return null; - } - }).filter(Boolean)); - })); + let dagData = await Promise.all( + pipelineData.map(async (node: PipelineTreeItem) => { + return await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); + }) + ); + let stepData = await Promise.all( + dagData.map(async (dag: PipelineRunDag) => { + return Promise.all( + dag.nodes + .map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [ + node.id, + ]); + } else { + return null; + } + }) + .filter(Boolean) + ); + }) + ); return JSON.stringify(stepData); } private async getLogData() { let lsClient = LSClient.getInstance(); - return JSON.stringify(await lsClient.sendLsClientRequest('getGlobalConfig')) + return JSON.stringify(await lsClient.sendLsClientRequest('getGlobalConfig')); } -} \ No newline at end of file + + private getRecentPipelineRun() { + let pipelineData = PipelineDataProvider.getInstance().getPipelineData()[0]; + let contextString = JSON.stringify(pipelineData); + return `Pipeline Data:\n${contextString}\n`; + } +} diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index e565ab48..ddb357e5 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -1,127 +1,133 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as marked from 'marked'; -import { ChatService } from "../../../services/chatService"; +import { ChatService } from '../../../services/chatService'; export class ChatDataProvider implements vscode.WebviewViewProvider { - private _view?: vscode.WebviewView; - private messages: string[] = []; // Array to store chat messages - private chatService: ChatService = ChatService.getInstance(); // ChatService instance - - constructor(private readonly context: vscode.ExtensionContext) {} - - /** - * Called when the webview is resolved. Initializes the webview content and sets up the message handling - */ - resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - _token: vscode.CancellationToken - ) { - this._view = webviewView; - this.configureWebViewOptions(webviewView.webview); - this.updateWebviewContent(); - - // Handle messages received from the webview - webviewView.webview.onDidReceiveMessage(async message => { - await this.handleWebviewMessage(message); - }); - } - - /** - * Configure the webview to allow scripts to run - */ - private configureWebViewOptions(webview: vscode.Webview) { - webview.options = { - enableScripts: true, - }; - } - - /** - * Handle incoming messages from the webview. - */ - private async handleWebviewMessage(message: any) { - if (message.command === 'sendMessage' && message.text?.trim()) { - console.log("Handling 'sendMessage' command with text:", message.text); - await this.addMessage(message.text, message.context); - } - } - - /** - * Generate the webview HTML content, including the chat log and the input elements. - */ - private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { - // Path to HTML file - const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); - let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); - - // Webview URIs for CSS and JS - const cssUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css')); - const jsUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js')); - const markedUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'marked.min.js')); - - // Chat log HTML - const chatLogHtml = this.renderChatLog(); - - // Replace placeholders in the HTML with actual values - html = html.replace('${cssUri}', cssUri.toString()); - html = html.replace('${jsUri}', jsUri.toString()); - html = html.replace('${markedUri}', markedUri.toString()); - html = html.replace('${chatLogHtml}', chatLogHtml); - - return html; + private _view?: vscode.WebviewView; + private messages: string[] = []; // Array to store chat messages + private chatService: ChatService = ChatService.getInstance(); // ChatService instance + + constructor(private readonly context: vscode.ExtensionContext) {} + + /** + * Called when the webview is resolved. Initializes the webview content and sets up the message handling + */ + resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken + ) { + this._view = webviewView; + this.configureWebViewOptions(webviewView.webview); + this.updateWebviewContent(); + + // Handle messages received from the webview + webviewView.webview.onDidReceiveMessage(async message => { + await this.handleWebviewMessage(message); + }); + } + + /** + * Configure the webview to allow scripts to run + */ + private configureWebViewOptions(webview: vscode.Webview) { + webview.options = { + enableScripts: true, + }; + } + + /** + * Handle incoming messages from the webview. + */ + private async handleWebviewMessage(message: any) { + if (message.command === 'sendMessage' && message.text?.trim()) { + await this.addMessage(message.text, message.context); } - - /** - * Render the chat log as HTML. - */ - private renderChatLog(): string { - return this.messages.map(msg => { + } + + /** + * Generate the webview HTML content, including the chat log and the input elements. + */ + private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { + // Path to HTML file + const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); + let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + + // Webview URIs for CSS and JS + const cssUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css') + ); + const jsUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') + ); + const markedUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'marked.min.js') + ); + + // Chat log HTML + const chatLogHtml = this.renderChatLog(); + + // Replace placeholders in the HTML with actual values + html = html.replace('${cssUri}', cssUri.toString()); + html = html.replace('${jsUri}', jsUri.toString()); + html = html.replace('${markedUri}', markedUri.toString()); + html = html.replace('${chatLogHtml}', chatLogHtml); + + return html; + } + + /** + * Render the chat log as HTML. + */ + private renderChatLog(): string { + return this.messages + .map(msg => { const isUserMessage = msg.startsWith('User:'); const className = isUserMessage ? 'user-message' : 'gemini-message'; const htmlMessage = marked.parse(msg.replace(/^(User:|Gemini:)\s*/, '')); return `
${htmlMessage}
`; - }).join(''); - } - - /** - * Update the webview with the latest content, including the chat message. - */ - private updateWebviewContent() { - if (this._view) { - this._view.webview.html = this.getWebviewContent( - this._view.webview, - this.context.extensionUri - ); - } + }) + .join(''); + } + + /** + * Update the webview with the latest content, including the chat message. + */ + private updateWebviewContent() { + if (this._view) { + this._view.webview.html = this.getWebviewContent( + this._view.webview, + this.context.extensionUri + ); } - - /** - * Add a message to the chat log, get a response from Gemini, and update the webview. - */ - async addMessage(message: string, context?: string[]) { - // Add the message to the log - this.messages.push(`User: ${message}`); - - // Get Gemini's response - try { - const botResponse = await this.chatService.getChatResponse(message, context); - this.messages.push(`Gemini: ${botResponse}`); - this.updateWebviewContent(); - this.sendMessageToWebview(`Gemini: ${botResponse}`); - } catch (error) { - this.messages.push("Error: Unable to get response from Gemini"); - this.updateWebviewContent(); - } + } + + /** + * Add a message to the chat log, get a response from Gemini, and update the webview. + */ + async addMessage(message: string, context?: string[]) { + // Add the message to the log + this.messages.push(`User: ${message}`); + + // Get Gemini's response + try { + const botResponse = await this.chatService.getChatResponse(message, context); + this.messages.push(`Gemini: ${botResponse}`); + this.updateWebviewContent(); + this.sendMessageToWebview(`Gemini: ${botResponse}`); + } catch (error) { + this.messages.push('Error: Unable to get response from Gemini'); + this.updateWebviewContent(); } - - /** - * Send a message from Gemini back to the webview - */ - private sendMessageToWebview(message: string) { - if (this._view) { - this._view.webview.postMessage({ command: 'recieveMessage', text: message}); - } + } + + /** + * Send a message from Gemini back to the webview + */ + private sendMessageToWebview(message: string) { + if (this._view) { + this._view.webview.postMessage({ command: 'recieveMessage', text: message }); } } - \ No newline at end of file +} From bffa8eeb64af5ff79423fa974f511e7e33037ce6 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 27 Aug 2024 18:19:27 -0400 Subject: [PATCH 028/138] style(chatService): format code and fix TypeScript linting issues Added missing semicolons and formatted code Fixed TypeScript linting issues with type checks and type guards Improved error handling and logging --- src/services/chatService.ts | 57 ++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 6cf51057..9698c348 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,5 +1,4 @@ import * as vscode from 'vscode'; -import { TokenJS } from 'token.js'; import axios from 'axios'; import { PipelineDataProvider, @@ -14,10 +13,8 @@ import { EnvironmentDataProvider } from '../views/activityBar/environmentView/En import { LSClient } from './LSClient'; import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; -import { ZenmlGlobalConfigResp } from 'type_hints' -import { ZenmlStoreConfig } from '../types/LSClientResponseTypes'; -import { Z_SYNC_FLUSH } from 'zlib'; -import { setPriority } from 'os'; +import { ZenmlGlobalConfigResp } from '../types/LSClientResponseTypes'; +import { TreeItem } from 'vscode'; export class ChatService { private static instance: ChatService; @@ -38,12 +35,17 @@ export class ChatService { } private async initialize() { - // TODO find another way to access the apiKey, instead of having it hardcoded - const apiKey = ''; - if (!apiKey) { - throw new Error('GEMINI_API_KEY is not set'); + try { + const module = await import('token.js'); + const { TokenJS } = module; + const apiKey = ''; // TODO find another way to access the apiKey, instead of having it hardcoded + if (!apiKey) { + throw new Error('GEMINI_API_KEY is not set'); + } + this.tokenjs = new TokenJS({ apiKey }); + } catch (error) { + console.error('Error loading TokenJS:', error); } - this.tokenjs = new TokenJS({ apiKey }); } public async getChatResponse(message: string, context?: string[]): Promise { @@ -166,7 +168,10 @@ export class ChatService { private getStackData(): string { let stackData = StackDataProvider.getInstance().items; let contextString = stackData - .map(item => `Name: ${item.label}\n` + `ID: ${item.id}\n` + `Active: ${item.isActive}`) + .map(item => { + let stackItem = item as TreeItem & { isActive: boolean; id: string }; + return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; + }) .join('\n'); return `Stack Data:\n${contextString}\n`; } @@ -176,7 +181,7 @@ export class ChatService { let lsClient = LSClient.getInstance(); let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - return dag + return dag; })); let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { let filteredNodes = await Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { @@ -187,7 +192,7 @@ export class ChatService { })); return filteredNodes.filter((value) => value !== null); })); - return stepData + return stepData; } private async getPanelData(): Promise { @@ -214,18 +219,30 @@ export class ChatService { private async getLogData() { let lsClient = LSClient.getInstance(); - let dashboardUrl: string = ServerDataProvider.getInstance().getCurrentStatus().dashboard_url; - let apiToken: string = (await lsClient.sendLsClientRequest('getGlobalConfig')).store.api_token - let pipelineRunSteps = await this.getPipelineRunNodes('step') + let currentStatus = ServerDataProvider.getInstance().getCurrentStatus(); + + // Type guard to ensure we are working with ServerStatus + if (!('dashboard_url' in currentStatus)) { + throw new Error('Dashboard URL not available in current status.'); + } + let dashboardUrl: string = currentStatus.dashboard_url; + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + + if (!apiToken) { + throw new Error('API Token is not available in gloval configuration'); + } + + let pipelineRunSteps = await this.getPipelineRunNodes('step'); let logs = await Promise.all(pipelineRunSteps[0].map(async (step) => { let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { headers: { Authorization: `Bearer ${apiToken}`, 'accept': 'application/json' } - }) - return response.data - })) - return logs + }); + return response.data; + })); + return logs; } } \ No newline at end of file From 61e9ba6f5810792b4cf187fb6d7e5904a1a2f320 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 27 Aug 2024 16:44:44 -0700 Subject: [PATCH 029/138] fix: Restore missing function --- src/services/chatService.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index f97ba9f8..ce1ebd3c 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -86,7 +86,7 @@ export class ChatService { systemMessage.content += this.getStackComponentData(); } if (requestedContext.includes('recentPipelineContext')) { - systemMessage.content += this.getRecentPipelineRun(); + systemMessage.content += this.getRecentPipelineRunData(); } this.allMessages.push(systemMessage); } @@ -167,6 +167,12 @@ export class ChatService { return `Pipeline Data:\n${contextString}\n`; } + private getRecentPipelineRunData() { + let pipelineData = PipelineDataProvider.getInstance().getPipelineData()[0]; + let contextString = JSON.stringify(pipelineData); + return `Pipeline Data:\n${contextString}\n`; + } + private getStackData(): string { let stackData = StackDataProvider.getInstance().items; let contextString = stackData From ef89d6b772ac41f2f25756c6377e175fae97f436 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Tue, 27 Aug 2024 21:29:18 -0400 Subject: [PATCH 030/138] feat: added get pipline run logs you can get the logs of a pipeline run given the id --- src/services/chatService.ts | 78 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/services/chatService.ts b/src/services/chatService.ts index ce1ebd3c..4f5d3deb 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -185,12 +185,14 @@ export class ChatService { } private async getPipelineRunNodes(type: string) { + //change back to just step or add artifact command let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); let lsClient = LSClient.getInstance(); let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); return dag; })); + let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { let filteredNodes = await Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { if (type === "all" || node.type === type) { @@ -203,49 +205,12 @@ export class ChatService { return stepData; } - private async getPanelData(): Promise { - //Retrieve the run data through ls client requests - //TODO: - //Separate artifact/step data - //Separate source code data - let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); - let lsClient = LSClient.getInstance(); - let dagData = await Promise.all( - pipelineData.map(async (node: PipelineTreeItem) => { - return await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - }) - ); - let stepData = await Promise.all( - dagData.map(async (dag: PipelineRunDag) => { - return Promise.all( - dag.nodes - .map(async (node: DagArtifact | DagStep) => { - if (node.type === 'step') { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [ - node.id, - ]); - } else { - return null; - } - }) - .filter(Boolean) - ); - }) - ); - return JSON.stringify(stepData); - } - private async getLogData() { let lsClient = LSClient.getInstance(); - let currentStatus = ServerDataProvider.getInstance().getCurrentStatus(); - - // Type guard to ensure we are working with ServerStatus - if (!('dashboard_url' in currentStatus)) { - throw new Error('Dashboard URL not available in current status.'); - } - let dashboardUrl: string = currentStatus.dashboard_url; + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url if (!apiToken) { throw new Error('API Token is not available in gloval configuration'); @@ -263,4 +228,39 @@ export class ChatService { })); return logs; } + + private async getPipelineRunLogs(id:string) { + let lsClient = LSClient.getInstance(); + + let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); + + let stepData = await Promise.all(dagData.nodes.map(async (node: DagArtifact|DagStep) => { + if (node.type === "step") { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + ); + + stepData = stepData.filter((value) => value !== null) + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url + + if (!apiToken) { + throw new Error('API Token is not available in gloval configuration'); + } + + let logs = await Promise.all(stepData.map(async (step) => { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + 'accept': 'application/json' + } + }); + return response.data; + })); + return logs; + } } From b696f9c78df3980811444b083c56c36573e9594f Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 27 Aug 2024 23:35:37 -0400 Subject: [PATCH 031/138] feat(secrets): implement API key management commands and integrate with vscode secret storage Added commands for registering and deleting the Gemini API key. Integrated vscode secret storage for managing API keys in `chatService.ts` --- package.json | 12 ++++++ src/commands/secrets/cmds.ts | 37 +++++++++++++++++++ src/commands/secrets/registry.ts | 8 ++++ src/services/chatService.ts | 24 +++++++----- .../activityBar/chatView/ChatDataProvider.ts | 6 ++- 5 files changed, 76 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index fb792ae5..459bfd90 100644 --- a/package.json +++ b/package.json @@ -313,6 +313,18 @@ "title": "Delete OpenAI API Key", "icon": "$(trash)", "category": "ZenML Secrets" + }, + { + "command": "zenml.registerGeminiAPIKey", + "title": "Register Gemini API Key", + "icon": "$(add)", + "category": "ZenML Secrets" + }, + { + "command": "zenml.deleteGeminiAPIKey", + "title": "Delete Gemini API Key", + "icon": "$(trash)", + "category": "ZenML Secrets" } ], "viewsContainers": { diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index b68ad9b9..7fa9536d 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -50,7 +50,44 @@ const deleteOpenAIAPIKey = async (context: ExtensionContext) => { vscode.window.showInformationMessage('OpenAI API key successfully removed.'); }; +const registerGeminiAPIKey = async (context: ExtensionContext) => { + let apiKey = await context.secrets.get('API_KEY'); + + if (apiKey) { + apiKey = await vscode.window.showInputBox({ + prompt: 'Gemini API Key already exists, enter a new value to update.', + password: true, + }); + } else { + apiKey = await vscode.window.showInputBox({ + prompt: 'Please enter your Gemini API key', + password: true, + }); + } + + if (apiKey === undefined) { + return undefined; + } + + await context.secrets.store('API_KEY', apiKey); + vscode.window.showInformationMessage('Gemini API key stored successfully.'); +}; + +const deleteGeminiAPIKey = async (context: ExtensionContext) => { + const apiKey = await context.secrets.get('API_KEY'); + + if (apiKey === undefined) { + vscode.window.showInformationMessage('No Gemini API key exists.'); + return; + } + await context.secrets.delete('API_KEY'); + vscode.window.showInformationMessage('Gemini API key successfully removed.'); +}; + + export const secretsCommands = { registerOpenAIAPIKey, deleteOpenAIAPIKey, + registerGeminiAPIKey, + deleteGeminiAPIKey, }; diff --git a/src/commands/secrets/registry.ts b/src/commands/secrets/registry.ts index 951599ec..608c5b2d 100644 --- a/src/commands/secrets/registry.ts +++ b/src/commands/secrets/registry.ts @@ -35,6 +35,14 @@ export const registerSecretsCommands = (context: ExtensionContext) => { 'zenml.deleteOpenAIAPIKey', async () => await secretsCommands.deleteOpenAIAPIKey(context) ), + registerCommand( + 'zenml.registerGeminiAPIKey', + async () => await secretsCommands.registerGeminiAPIKey(context) + ), + registerCommand( + 'zenml.deleteGeminiAPIKey', + async () => await secretsCommands.deleteGeminiAPIKey(context) + ) ]; registeredCommands.forEach(cmd => { diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 4f5d3deb..44c5e3cb 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -21,15 +21,17 @@ export class ChatService { private initialized: Promise; private tokenjs: any; private allMessages: object[]; + private context: vscode.ExtensionContext; - private constructor() { + private constructor(context: vscode.ExtensionContext) { + this.context = context; this.initialized = this.initialize(); this.allMessages = []; } - public static getInstance(): ChatService { + public static getInstance(context: vscode.ExtensionContext): ChatService { if (!ChatService.instance) { - ChatService.instance = new ChatService(); + ChatService.instance = new ChatService(context); } return ChatService.instance; } @@ -38,9 +40,12 @@ export class ChatService { try { const module = await import('token.js'); const { TokenJS } = module; - const apiKey = ''; // TODO find another way to access the apiKey, instead of having it hardcoded + + // Use context to access secrets + const apiKey = await this.context.secrets.get('API_KEY'); if (!apiKey) { - throw new Error('GEMINI_API_KEY is not set'); + vscode.window.showErrorMessage('No Gemini API key found. Please register one'); + } this.tokenjs = new TokenJS({ apiKey }); } catch (error) { @@ -192,7 +197,7 @@ export class ChatService { let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); return dag; })); - + let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { let filteredNodes = await Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { if (type === "all" || node.type === type) { @@ -207,7 +212,7 @@ export class ChatService { private async getLogData() { let lsClient = LSClient.getInstance(); - + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; let dashboardUrl = globalConfig.store.url @@ -242,7 +247,7 @@ export class ChatService { }) ); - stepData = stepData.filter((value) => value !== null) + stepData = stepData.filter((value) => value !== null); let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; @@ -253,7 +258,8 @@ export class ChatService { } let logs = await Promise.all(stepData.map(async (step) => { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + let validStep = step as JsonObject; // Type assertion. If we're not sure if step is not null, should change this to a guard check. + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { headers: { Authorization: `Bearer ${apiToken}`, 'accept': 'application/json' diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index ddb357e5..4e2e8c0e 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -6,9 +6,11 @@ import { ChatService } from '../../../services/chatService'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; private messages: string[] = []; // Array to store chat messages - private chatService: ChatService = ChatService.getInstance(); // ChatService instance + private chatService: ChatService; - constructor(private readonly context: vscode.ExtensionContext) {} + constructor(private readonly context: vscode.ExtensionContext) { + this.chatService = ChatService.getInstance(this.context); + } /** * Called when the webview is resolved. Initializes the webview content and sets up the message handling From b6dc649a0473d0ba15edcd0e1ace46b545f56032 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Wed, 28 Aug 2024 10:55:17 -0400 Subject: [PATCH 032/138] feat: created dropdown menu --- resources/chat-view/chat.css | 47 ++++++++++++++++++++ resources/chat-view/chat.html | 80 +++++++++++++++++++++++------------ 2 files changed, 99 insertions(+), 28 deletions(-) diff --git a/resources/chat-view/chat.css b/resources/chat-view/chat.css index 85b0c644..6942471e 100644 --- a/resources/chat-view/chat.css +++ b/resources/chat-view/chat.css @@ -77,3 +77,50 @@ body { border-radius: 4px; cursor: pointer; } + +#chatForm { + display: flex; + gap: 10px; +} +.dropup { + position: relative; + display: inline-block; +} +.dropup-content { + display: none; + position: absolute; + background-color: #ffffff; + min-width: 160px; + box-shadow: 0px -8px 16px 0px rgba(0,0,0,0.2); + bottom: 100%; + left: 0; + z-index: 1; + border-radius: 4px; + margin-bottom: 5px; +} +.dropup-content label { + display: block; + padding: 12px 16px; + color: black; + font-family: Arial, sans-serif; +} +.dropup-content label:hover { + background-color: #f1f1f1; +} +.dropup-content input[type="checkbox"] { + margin-right: 8px; + vertical-align: middle; +} +.show { + display: block; +} +#contextButton { + background-color: #f0f0f0; + border: 1px solid #ccc; + padding: 5px 10px; + cursor: pointer; + border-radius: 4px; +} +#contextButton:hover { + background-color: #e0e0e0; +} \ No newline at end of file diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 59ee8039..098d5165 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -13,38 +13,62 @@
-
- - - - - - -
- - +
+ +
+ + + + + +
+
+ +
+ From eb9fb4e322a4f16f7668e2d7baa4ee3cd0edfbee Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Wed, 28 Aug 2024 16:43:26 -0400 Subject: [PATCH 033/138] refactor: eliminated redundant storage and created chatMessage type chat messages are now passed into chatservice rather than being stored there --- resources/chat-view/chat.css | 4 +- src/services/chatService.ts | 44 +++++-------------- .../activityBar/chatView/ChatDataProvider.ts | 25 +++++------ src/views/activityBar/chatView/chatMessage.ts | 17 +++++++ 4 files changed, 40 insertions(+), 50 deletions(-) create mode 100644 src/views/activityBar/chatView/chatMessage.ts diff --git a/resources/chat-view/chat.css b/resources/chat-view/chat.css index 6942471e..5d1a429d 100644 --- a/resources/chat-view/chat.css +++ b/resources/chat-view/chat.css @@ -29,12 +29,12 @@ body { color: white; } -.user-message { +.user { background-color: #d1ffd1; align-self: flex-end; } -.gemini-message { +.assistant { background-color: #e0e0ff; align-self: flex-start; } diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 44c5e3cb..67ee3caa 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -15,18 +15,16 @@ import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; import { ZenmlGlobalConfigResp } from '../types/LSClientResponseTypes'; import { TreeItem } from 'vscode'; +import { chatMessage } from '../views/activityBar/chatView/chatMessage'; export class ChatService { private static instance: ChatService; - private initialized: Promise; private tokenjs: any; - private allMessages: object[]; private context: vscode.ExtensionContext; private constructor(context: vscode.ExtensionContext) { this.context = context; - this.initialized = this.initialize(); - this.allMessages = []; + this.initialize(); } public static getInstance(context: vscode.ExtensionContext): ChatService { @@ -53,17 +51,16 @@ export class ChatService { } } - public async getChatResponse(message: string, context?: string[]): Promise { + public async getChatResponse(messages: chatMessage[], context?: string[] | undefined): Promise { try { - this.addUserMessage(message); if (context) { - this.addContext(context); + messages = this.addContext(messages, context); } - + const completion = await this.tokenjs.chat.completions.create({ provider: 'gemini', model: 'gemini-1.5-flash', - messages: this.getRecentMessages(), + messages: messages, }); return completion.choices[0]?.message?.content || 'No content'; @@ -73,8 +70,8 @@ export class ChatService { } } - private addContext(requestedContext: string[]): void { - let systemMessage = { role: 'system', content: 'Use this context to answer the question. ' }; + private addContext(messages: chatMessage[], requestedContext: string[]): chatMessage[] { + let systemMessage: chatMessage = { role: 'system', content: 'Context:' }; if (requestedContext.includes('serverContext')) { systemMessage.content += this.getServerData(); } @@ -93,29 +90,8 @@ export class ChatService { if (requestedContext.includes('recentPipelineContext')) { systemMessage.content += this.getRecentPipelineRunData(); } - this.allMessages.push(systemMessage); - } - - // private addSystemMessage(message: string): void { - // let systemMessage = { role: 'system', content: message }; - // this.allMessages.push(systemMessage); - // } - - private addUserMessage(message: string): void { - let userMessage = { role: 'user', content: message }; - this.allMessages.push(userMessage); - } - - private getRecentMessages(): object[] { - let recentMessages: object[]; - - if (this.allMessages.length > 10) { - recentMessages = this.allMessages.slice(-10); - } else { - recentMessages = this.allMessages; - } - - return recentMessages; + messages.push(systemMessage); + return messages } /** diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index 4e2e8c0e..70c7cbf1 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -2,10 +2,11 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as marked from 'marked'; import { ChatService } from '../../../services/chatService'; +import { chatMessage } from './chatMessage'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; - private messages: string[] = []; // Array to store chat messages + private messages: chatMessage[] = []; // Array to store chat messages private chatService: ChatService; constructor(private readonly context: vscode.ExtensionContext) { @@ -83,12 +84,10 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { * Render the chat log as HTML. */ private renderChatLog(): string { - return this.messages - .map(msg => { - const isUserMessage = msg.startsWith('User:'); - const className = isUserMessage ? 'user-message' : 'gemini-message'; - const htmlMessage = marked.parse(msg.replace(/^(User:|Gemini:)\s*/, '')); - return `
${htmlMessage}
`; + console.log(this.messages) + return this.messages.filter(msg => msg['role'] != 'system') + .map((message) => { + return `
${message['content']}
`; }) .join(''); } @@ -109,17 +108,15 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { * Add a message to the chat log, get a response from Gemini, and update the webview. */ async addMessage(message: string, context?: string[]) { - // Add the message to the log - this.messages.push(`User: ${message}`); + this.messages.push({role: 'user', content: `${message}`}); - // Get Gemini's response try { - const botResponse = await this.chatService.getChatResponse(message, context); - this.messages.push(`Gemini: ${botResponse}`); + const botResponse = await this.chatService.getChatResponse(this.messages, context); + this.messages.push({role: 'assistant', content: `${botResponse}`}); this.updateWebviewContent(); - this.sendMessageToWebview(`Gemini: ${botResponse}`); + this.sendMessageToWebview(`${botResponse}`); } catch (error) { - this.messages.push('Error: Unable to get response from Gemini'); + this.messages.push({role: 'system', content: 'Error: Unable to get response from Gemini'}); this.updateWebviewContent(); } } diff --git a/src/views/activityBar/chatView/chatMessage.ts b/src/views/activityBar/chatView/chatMessage.ts new file mode 100644 index 00000000..f1b94333 --- /dev/null +++ b/src/views/activityBar/chatView/chatMessage.ts @@ -0,0 +1,17 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. + +export interface chatMessage { + role: string, + content: string, +} \ No newline at end of file From 115988a9ce62ebfe9f3645c3a61adff4321c3582 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Thu, 29 Aug 2024 13:35:17 -0400 Subject: [PATCH 034/138] feat: added context selector ui hard coded pipeline data for now --- resources/chat-view/chat.css | 51 +++++- resources/chat-view/chat.html | 157 +++++++++++++----- resources/chat-view/context.js | 94 +++++++++++ src/services/chatService.ts | 4 +- .../activityBar/chatView/ChatDataProvider.ts | 1 - 5 files changed, 258 insertions(+), 49 deletions(-) create mode 100644 resources/chat-view/context.js diff --git a/resources/chat-view/chat.css b/resources/chat-view/chat.css index 5d1a429d..323a4ac6 100644 --- a/resources/chat-view/chat.css +++ b/resources/chat-view/chat.css @@ -111,8 +111,8 @@ body { margin-right: 8px; vertical-align: middle; } -.show { - display: block; +.hide { + display: none; } #contextButton { background-color: #f0f0f0; @@ -123,4 +123,51 @@ body { } #contextButton:hover { background-color: #e0e0e0; +} + + +.tree-view { + background-color: #1e1e1e; + color: #ffffff; + width: 256px; + overflow: auto; +} +.tree-item { + display: flex; + align-items: center; + padding: 2px 8px; + cursor: pointer; +} +.tree-item:hover { + background-color: #2a2d2e; +} +.tree-item-content { + display: flex; + align-items: center; +} +.tree-item-icon { + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; +} +.tree-item-checkbox { + margin-right: 8px; +} +.tree-item-name { + font-size: 14px; + color: #cccccc; + margin-left: 4px; +} +.tree-item-children { + display: none; + width: 100%; +} +.tree-item-children.open { + display: block; +} +.tree-item-wrapper { + display: flex; + flex-direction: column; } \ No newline at end of file diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 098d5165..a2fe9164 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -14,29 +14,10 @@
- -
- - - - - -
+ +
@@ -47,28 +28,116 @@ + diff --git a/resources/chat-view/context.js b/resources/chat-view/context.js new file mode 100644 index 00000000..a9125d9e --- /dev/null +++ b/resources/chat-view/context.js @@ -0,0 +1,94 @@ +const treeData = [ + {name: 'Server'}, + {name: 'Environment'}, + { + name: 'Pipeline', + children : [ + { + name: 'training', + children: [ + {name: 'id: 85439137'}, + {name: 'completed: success'} + ] + } + ] + }, + {name: 'Stack'}, + {name: 'Stack Components'} +]; + +function createTreeView(items, parentEl, level = 0) { + items.forEach(item => { + const itemEl = document.createElement('div'); + itemEl.className = 'tree-item'; + itemEl.style.paddingLeft = `${level * 12}px`; + + const wrapperEl = document.createElement('div'); + wrapperEl.className = 'tree-item-wrapper'; + + const contentEl = document.createElement('div'); + contentEl.className = 'tree-item-content'; + + const checkboxEl = document.createElement('input'); + checkboxEl.type = 'checkbox'; + checkboxEl.className = 'tree-item-checkbox'; + + const chevronEl = document.createElement('span'); + chevronEl.className = 'tree-item-icon'; + + const isFolder = item.children && item.children.length > 0; + + chevronEl.innerHTML = isFolder + ? '' + : ''; + + const nameEl = document.createElement('span'); + nameEl.className = 'tree-item-name'; + nameEl.textContent = item.name; + + if (level < 2) { + contentEl.appendChild(checkboxEl); + } + contentEl.appendChild(chevronEl); + contentEl.appendChild(nameEl); + wrapperEl.appendChild(contentEl); + + if (isFolder) { + const childrenEl = document.createElement('div'); + childrenEl.className = 'tree-item-children'; + createTreeView(item.children, childrenEl, level + 1); + + contentEl.addEventListener('click', (e) => { + if (e.target !== checkboxEl && !childrenEl.contains(e.target)) { + e.stopPropagation(); + childrenEl.classList.toggle('open'); + if (childrenEl.classList.contains('open')) { + chevronEl.innerHTML = ''; + } else { + chevronEl.innerHTML = ''; + } + } + }); + + wrapperEl.appendChild(childrenEl); + } + + checkboxEl.addEventListener('click', (e) => { + e.stopPropagation(); + }); + + checkboxEl.addEventListener('change', (e) => { + if (e.target.checked) { + itemEl.style.backgroundColor = '#2a2d2e'; + } else { + itemEl.style.backgroundColor = ''; + } + }); + + itemEl.appendChild(wrapperEl); + parentEl.appendChild(itemEl); + }); +} + +const treeViewEl = document.getElementById('tree-view'); +createTreeView(treeData, treeViewEl); \ No newline at end of file diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 67ee3caa..800f20e8 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -54,7 +54,7 @@ export class ChatService { public async getChatResponse(messages: chatMessage[], context?: string[] | undefined): Promise { try { if (context) { - messages = this.addContext(messages, context); + messages = await this.addContext(messages, context); } const completion = await this.tokenjs.chat.completions.create({ @@ -70,7 +70,7 @@ export class ChatService { } } - private addContext(messages: chatMessage[], requestedContext: string[]): chatMessage[] { + private async addContext(messages: chatMessage[], requestedContext: string[]): Promise { let systemMessage: chatMessage = { role: 'system', content: 'Context:' }; if (requestedContext.includes('serverContext')) { systemMessage.content += this.getServerData(); diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index 70c7cbf1..0d033dff 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -84,7 +84,6 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { * Render the chat log as HTML. */ private renderChatLog(): string { - console.log(this.messages) return this.messages.filter(msg => msg['role'] != 'system') .map((message) => { return `
${message['content']}
`; From 8f0a4fe77ba8530e7ca94eff72334cb2df192fd5 Mon Sep 17 00:00:00 2001 From: Alex Sklar Date: Thu, 29 Aug 2024 11:51:31 -0700 Subject: [PATCH 035/138] refactor: change api key naming convention to `zenml.{provider}.key` --- src/commands/secrets/cmds.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index 99514123..e7819c65 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -32,7 +32,7 @@ const registerLLMAPIKey = async (context: ExtensionContext) => { } const model = selectedOption.label; - const secretKey = `${model.toUpperCase()}_API_KEY`; + const secretKey = `zenml.${model.toLowerCase()}.key`; let apiKey = await context.secrets.get(secretKey); From dcf67ce4dafae327f77658ec1cdcc4908e66a077 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 29 Aug 2024 14:59:24 -0400 Subject: [PATCH 036/138] feat: added renderChat command to render chat in the editor area --- package.json | 31 +++++++-- requirements.txt | 87 ++++++++++++++------------ src/extension.ts | 52 +++++++++++++++ src/test/python_tests/requirements.txt | 10 ++- 4 files changed, 135 insertions(+), 45 deletions(-) diff --git a/package.json b/package.json index 459bfd90..c589eacc 100644 --- a/package.json +++ b/package.json @@ -142,8 +142,14 @@ }, "commands": [ { - "command": "zenml.openChat", - "title": "Open ZenML Chat" + "command": "zenml.renderChat", + "title": "Render Chat", + "icon": "$(comment-discussion", + "category": "ZenML Chat" + }, + { + "command": "zenml.openChatView", + "title": "Open Chat View" }, { "command": "zenml.promptForInterpreter", @@ -333,6 +339,11 @@ "id": "zenml", "title": "ZenML", "icon": "resources/logo.png" + }, + { + "id": "chatbotView", + "title": "Chatbot", + "icon": "resources/chat.png" } ], "panel": [ @@ -340,6 +351,11 @@ "id": "zenmlPanel", "title": "ZenML", "icon": "resources/logo.png" + }, + { + "id": "chatPanel", + "title": "ZenML Chat", + "icon": "resources/chat.png" } ] }, @@ -381,13 +397,20 @@ "id": "zenmlPanelView", "name": "ZenML" } + ], + "chatPanel": [ + { + "id": "chatPanelView", + "name": "Chat", + "type": "webview" + } ] }, "menus": { "view/title": [ { - "command": "zenml.openChat", - "when": "view == zenmlChatView", + "command": "zenml.renderChat", + "when": "view == chatPanelView", "group": "navigation" }, { diff --git a/requirements.txt b/requirements.txt index a402d009..d6b824fb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --generate-hashes ./requirements.in @@ -10,12 +10,18 @@ attrs==24.2.0 \ # via # cattrs # lsprotocol -cattrs==23.2.3 \ - --hash=sha256:0341994d94971052e9ee70662542699a3162ea1e0c62f7ce1b4a57f563685108 \ - --hash=sha256:a934090d95abaa9e911dac357e3a8699e0b4b14f8529bcc7d2b1ad9d51672b9f +cattrs==24.1.0 \ + --hash=sha256:043bb8af72596432a7df63abcff0055ac0f198a4d2e95af8db5a936a7074a761 \ + --hash=sha256:8274f18b253bf7674a43da851e3096370d67088165d23138b04a1c04c8eaf48e # via # lsprotocol # pygls +exceptiongroup==1.2.0 ; python_version < "3.11" \ + --hash=sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14 \ + --hash=sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68 + # via + # -r ./requirements.in + # cattrs lsprotocol==2023.0.1 \ --hash=sha256:c75223c9e4af2f24272b14c6375787438279369236cd568f596d4951052a60f2 \ --hash=sha256:cc5c15130d2403c18b734304339e51242d3018a05c4f7d0f198ad6e0cd21861d @@ -87,40 +93,41 @@ types-pyyaml==6.0.12.20240808 \ --hash=sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af \ --hash=sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35 # via -r ./requirements.in -watchdog==4.0.2 \ - --hash=sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4 \ - --hash=sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19 \ - --hash=sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a \ - --hash=sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa \ - --hash=sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a \ - --hash=sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a \ - --hash=sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1 \ - --hash=sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc \ - --hash=sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9 \ - --hash=sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930 \ - --hash=sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73 \ - --hash=sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b \ - --hash=sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83 \ - --hash=sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7 \ - --hash=sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef \ - --hash=sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1 \ - --hash=sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040 \ - --hash=sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b \ - --hash=sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270 \ - --hash=sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c \ - --hash=sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d \ - --hash=sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8 \ - --hash=sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508 \ - --hash=sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b \ - --hash=sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503 \ - --hash=sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757 \ - --hash=sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b \ - --hash=sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29 \ - --hash=sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c \ - --hash=sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22 \ - --hash=sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578 \ - --hash=sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e \ - --hash=sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee \ - --hash=sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7 \ - --hash=sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3 +typing-extensions==4.12.2 ; python_version < "3.11" \ + --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ + --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8 + # via + # -r ./requirements.in + # cattrs +watchdog==5.0.0 \ + --hash=sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658 \ + --hash=sha256:01ab36cddc836a0f202c66267daaef92ba5c17c7d6436deff0587bb61234c5c9 \ + --hash=sha256:0710e9502727f688a7e06d48078545c54485b3d6eb53b171810879d8223c362a \ + --hash=sha256:0834c21efa3e767849b09e667274604c7cdfe30b49eb95d794565c53f4db3c1e \ + --hash=sha256:109daafc5b0f2a98d1fa9475ff9737eb3559d57b18129a36495e20c71de0b44f \ + --hash=sha256:1228cb097e855d1798b550be8f0e9f0cfbac4384f9a3e91f66d250d03e11294e \ + --hash=sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7 \ + --hash=sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c \ + --hash=sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d \ + --hash=sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7 \ + --hash=sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5 \ + --hash=sha256:3c177085c3d210d1c73cb4569442bdaef706ebebc423bd7aed9e90fc12b2e553 \ + --hash=sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4 \ + --hash=sha256:4fe6780915000743074236b21b6c37419aea71112af62237881bc265589fe463 \ + --hash=sha256:663b096368ed7831ac42259919fdb9e0a1f0a8994d972675dfbcca0225e74de1 \ + --hash=sha256:685931412978d00a91a193d9018fc9e394e565e8e7a0c275512a80e59c6e85f8 \ + --hash=sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45 \ + --hash=sha256:6e58eafe9cc5ceebe1562cdb89bacdcd0ef470896e8b0139fe677a5abec243da \ + --hash=sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9 \ + --hash=sha256:7e6b0e9b8a9dc3865d65888b5f5222da4ba9c4e09eab13cff5e305e7b7e7248f \ + --hash=sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e \ + --hash=sha256:b8d747bf6d8fe5ce89cb1a36c3724d1599bd4cde3f90fcba518e6260c7058a52 \ + --hash=sha256:bc16d448a74a929b896ed9578c25756b2125400b19b3258be8d9a681c7ae8e71 \ + --hash=sha256:bf3216ec994eabb2212df9861f19056ca0d4cd3516d56cb95801933876519bfe \ + --hash=sha256:c2b4d90962639ae7cee371ea3a8da506831945d4418eee090c53bc38e6648dc6 \ + --hash=sha256:cb59ad83a1700304fc1ac7bc53ae9e5cbe9d60a52ed9bba8e2e2d782a201bb2b \ + --hash=sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b \ + --hash=sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12 \ + --hash=sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8 \ + --hash=sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9 # via -r ./requirements.in diff --git a/src/extension.ts b/src/extension.ts index cd137b72..31e1ff76 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -46,6 +46,58 @@ export async function activate(context: vscode.ExtensionContext) { vscode.window.registerWebviewViewProvider('zenmlChatView', new ChatDataProvider(context)) ); + + /** + * Register's the renderChat command ********************************************************************************************** + */ + let renderChatCommand = vscode.commands.registerCommand('zenml.renderChat', () => { + // chatDataProvider.resolveWebviewView(fakeWebviewView, panel); + const panel = vscode.window.createWebviewPanel( + 'zenmlChat', // Identifies the type of the webview. Used internally + 'ZenML Chat', // Title of the panel displayed to the user + vscode.ViewColumn.One, // Editor column to show the new webview panel in + { + enableScripts: true, // Enable scripts in the webview + retainContextWhenHidden: true // Keep the webview's context when hidden + } + ); + + // Create an instance of ChatDataProvider + const chatDataProvider = new ChatDataProvider(context); + + // Fake WebviewViewResolveContext + const fakeContext = {} as vscode.WebviewViewResolveContext; + + const dummyCancellationToken: vscode.CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: (callback) => { + // No-op, as the token is never cancelled + return new vscode.Disposable(() => {}); + } + }; + // Use CancellationToken.None + // const cancellationToken = vscode.CancellationToken.None; + + // Fake WebviewView object + const fakeWebviewView = { + webview: panel.webview, + onDidDispose: panel.onDidDispose + } as vscode.WebviewView; + + // Call resolveWebviewView with all required arguments + chatDataProvider.resolveWebviewView(fakeWebviewView, fakeContext, dummyCancellationToken); + + // Ensure to dispose of the panel when not needed + panel.onDidDispose(() => { + // Clean up resources or perform any necessary actions when the panel is disposed + }); + }); + + context.subscriptions.push(renderChatCommand); + /** + * ****************************************************************************************************************************************** + */ + await ZenExtension.activate(context, lsClient); context.subscriptions.push( diff --git a/src/test/python_tests/requirements.txt b/src/test/python_tests/requirements.txt index 5741e600..5bb8718f 100644 --- a/src/test/python_tests/requirements.txt +++ b/src/test/python_tests/requirements.txt @@ -1,9 +1,13 @@ # -# This file is autogenerated by pip-compile with Python 3.11 +# This file is autogenerated by pip-compile with Python 3.10 # by the following command: # # pip-compile --generate-hashes ./src/test/python_tests/requirements.in # +exceptiongroup==1.2.2 \ + --hash=sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b \ + --hash=sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc + # via pytest iniconfig==2.0.0 \ --hash=sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3 \ --hash=sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374 @@ -28,6 +32,10 @@ python-jsonrpc-server==0.4.0 \ --hash=sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595 \ --hash=sha256:e5a908ff182e620aac07db5f57887eeb0afe33993008f57dc1b85b594cea250c # via -r ./src/test/python_tests/requirements.in +tomli==2.0.1 \ + --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \ + --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f + # via pytest ujson==5.10.0 \ --hash=sha256:0de4971a89a762398006e844ae394bd46991f7c385d7a6a3b93ba229e6dac17e \ --hash=sha256:129e39af3a6d85b9c26d5577169c21d53821d8cf68e079060602e861c6e5da1b \ From 52ef71742ded82cb258f3b3166119a41436d135a Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 29 Aug 2024 15:48:07 -0400 Subject: [PATCH 037/138] fix: fixed markdown parsing of chat messages --- src/views/activityBar/chatView/ChatDataProvider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index 0d033dff..e853ddd8 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -84,9 +84,10 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { * Render the chat log as HTML. */ private renderChatLog(): string { - return this.messages.filter(msg => msg['role'] != 'system') + return this.messages.filter(msg => msg['role'] !== 'system') .map((message) => { - return `
${message['content']}
`; + let content = marked.parse(message.content); + return `
${content}
`; }) .join(''); } From be3bb3aaf55ea4c455d6c286798be7393f6c78ea Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 29 Aug 2024 17:55:31 -0400 Subject: [PATCH 038/138] chore: remove unecessary commands and views from package.json --- package.json | 43 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index c589eacc..557562ee 100644 --- a/package.json +++ b/package.json @@ -141,16 +141,6 @@ } }, "commands": [ - { - "command": "zenml.renderChat", - "title": "Render Chat", - "icon": "$(comment-discussion", - "category": "ZenML Chat" - }, - { - "command": "zenml.openChatView", - "title": "Open Chat View" - }, { "command": "zenml.promptForInterpreter", "title": "Select Python Interpreter", @@ -290,6 +280,12 @@ "icon": "$(type-hierarchy)", "category": "ZenML Pipeline Runs" }, + { + "command": "zenml.renderChat", + "title": "Render Chat", + "icon": "$(comment-discussion)", + "category": "ZenML Chat" + }, { "command": "zenml.refreshEnvironmentView", "title": "Refresh Environment View", @@ -339,11 +335,6 @@ "id": "zenml", "title": "ZenML", "icon": "resources/logo.png" - }, - { - "id": "chatbotView", - "title": "Chatbot", - "icon": "resources/chat.png" } ], "panel": [ @@ -351,11 +342,6 @@ "id": "zenmlPanel", "title": "ZenML", "icon": "resources/logo.png" - }, - { - "id": "chatPanel", - "title": "ZenML Chat", - "icon": "resources/chat.png" } ] }, @@ -397,22 +383,10 @@ "id": "zenmlPanelView", "name": "ZenML" } - ], - "chatPanel": [ - { - "id": "chatPanelView", - "name": "Chat", - "type": "webview" - } ] }, "menus": { "view/title": [ - { - "command": "zenml.renderChat", - "when": "view == chatPanelView", - "group": "navigation" - }, { "when": "serverCommandsRegistered && view == zenmlServerView", "command": "zenml.connectServer", @@ -525,6 +499,11 @@ "command": "zenml.renderDag", "group": "inline@1" }, + { + "when": "view == chatPanelView", + "command": "zenml.renderChat", + "group": "navigation" + }, { "when": "environmentCommandsRegistered && view == zenmlEnvironmentView && viewItem == interpreter", "command": "zenml.setPythonInterpreter", From f1096552ff049495d622f56d4465be248f4b26f1 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Thu, 29 Aug 2024 23:48:12 -0400 Subject: [PATCH 039/138] feat: changed sidebar webview to an API registration panel --- src/extension.ts | 3 +- .../APIView/APIWebviewViewProvider.ts | 146 ++++++++++++++++++ 2 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/views/activityBar/APIView/APIWebviewViewProvider.ts diff --git a/src/extension.ts b/src/extension.ts index 31e1ff76..2530b9be 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -17,6 +17,7 @@ import { ZenExtension } from './services/ZenExtension'; import { refreshUIComponents } from './utils/refresh'; import { EnvironmentDataProvider } from './views/activityBar/environmentView/EnvironmentDataProvider'; import { ChatDataProvider } from './views/activityBar/chatView/ChatDataProvider'; +import { APIWebviewViewProvider } from './views/activityBar/APIView/APIWebviewViewProvider'; import { registerEnvironmentCommands } from './commands/environment/registry'; import { LSP_ZENML_CLIENT_INITIALIZED } from './utils/constants'; import { toggleCommands } from './utils/global'; @@ -43,7 +44,7 @@ export async function activate(context: vscode.ExtensionContext) { registerEnvironmentCommands(context); context.subscriptions.push( - vscode.window.registerWebviewViewProvider('zenmlChatView', new ChatDataProvider(context)) + vscode.window.registerWebviewViewProvider('zenmlChatView', new APIWebviewViewProvider(context)) ); diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts new file mode 100644 index 00000000..27dd42b4 --- /dev/null +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -0,0 +1,146 @@ +import * as vscode from 'vscode'; + +export class APIWebviewViewProvider implements vscode.WebviewViewProvider { + private _view?: vscode.WebviewView; + private _disposables: vscode.Disposable[] = []; + constructor(private readonly context: vscode.ExtensionContext) {} + + resolveWebviewView( + webviewView: vscode.WebviewView, + context: vscode.WebviewViewResolveContext, + token: vscode.CancellationToken + ): Thenable | void { + this._view = webviewView; + + webviewView.webview.options = { + enableScripts: true, + localResourceRoots: [this.context.extensionUri], + }; + + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); + + webviewView.webview.onDidReceiveMessage( + (message) => { + switch (message.command) { + case 'registerApiKey': + this._handleRegisterApiKey(message.provider); + break; + } + }, + ); + } + + private _getHtmlForWebview(webview: vscode.Webview): string { + const logoUri = webview.asWebviewUri( + vscode.Uri.joinPath(this.context.extensionUri, 'resources', 'zenml_logo.png') + ); + + return ` + + + + + + + Chat with your ZenML pipelines and data + + + + + + + + +
+
+
+ ZenML Logo +

Chat with your ZenML pipelines and data

+
+
+
+ +
+
+ + +
+
+
+
+ + + + + `; + } + + private _handleRegisterApiKey(provider: string): void { + switch (provider) { + case 'OpenAI': + vscode.commands.executeCommand('zenml.registerOpenAIAPIKey'); + break; + case 'Claude': + vscode.commands.executeCommand('zenml.registerClaudeAPIKey'); + break; + case 'Gemini': + vscode.commands.executeCommand('zenml.registerGeminiAPIKey'); + break; + default: + console.error(`Unsupported provider: ${provider}`); + } + } + + dispose(): void { + this._disposables.forEach((disposable) => disposable.dispose()); + } +} \ No newline at end of file From 7ca398bd22c23a471d9f91535c225470404e04e2 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 30 Aug 2024 01:28:50 -0400 Subject: [PATCH 040/138] feat: overhauled chat interface ui to be more suited to the editor window --- resources/chat-view/chat.html | 88 +++++++++++++------ .../activityBar/chatView/ChatDataProvider.ts | 12 ++- 2 files changed, 74 insertions(+), 26 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index a2fe9164..ee94e743 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -1,32 +1,70 @@ - - - Chat - + + + ZenML Chat + + + + - -
-
- ${chatLogHtml} -
-
- -
- -
+ +
+

ZenML Chat

+ +
+
+
+

Options

+ +
+
+
+
+ + + + +
+ +
+ ${chatLogHtml} +
- - - -
+
- - --> diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index e853ddd8..fdbcae94 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -87,7 +87,17 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { return this.messages.filter(msg => msg['role'] !== 'system') .map((message) => { let content = marked.parse(message.content); - return `
${content}
`; + if (message.role == 'assistant') { + return `
+

User

+

${content}

+
` + } else { + return `
+

ZenML Assistant

+

${content}

+
` + } }) .join(''); } From 367b9a06e5751e46295e588d211294bd0ab2232b Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 30 Aug 2024 11:17:41 -0400 Subject: [PATCH 041/138] feat: add openChat command to menu chore: got rid of unnecessary

tags and renamed renderChat to openChat --- package.json | 9 +++++++-- resources/chat-view/chat.html | 3 +++ src/extension.ts | 4 ++-- src/views/activityBar/chatView/ChatDataProvider.ts | 4 ++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 557562ee..1087958f 100644 --- a/package.json +++ b/package.json @@ -281,7 +281,7 @@ "category": "ZenML Pipeline Runs" }, { - "command": "zenml.renderChat", + "command": "zenml.openChat", "title": "Render Chat", "icon": "$(comment-discussion)", "category": "ZenML Chat" @@ -446,6 +446,11 @@ "when": "environmentCommandsRegistered && view == zenmlEnvironmentView", "command": "zenml.restartLspServer", "group": "navigation" + }, + { + "when": "view == zenmlChatView", + "command": "zenml.openChat", + "group": "navigation" } ], "view/item/context": [ @@ -501,7 +506,7 @@ }, { "when": "view == chatPanelView", - "command": "zenml.renderChat", + "command": "zenml.openChat", "group": "navigation" }, { diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index ee94e743..859f81ad 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -27,6 +27,9 @@ #messageInput { color: black; } + ul { + color:black !important; + } form { display: contents; } diff --git a/src/extension.ts b/src/extension.ts index 2530b9be..d0b5b600 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -49,9 +49,9 @@ export async function activate(context: vscode.ExtensionContext) { /** - * Register's the renderChat command ********************************************************************************************** + * Register's the openChat command ********************************************************************************************** */ - let renderChatCommand = vscode.commands.registerCommand('zenml.renderChat', () => { + let renderChatCommand = vscode.commands.registerCommand('zenml.openChat', () => { // chatDataProvider.resolveWebviewView(fakeWebviewView, panel); const panel = vscode.window.createWebviewPanel( 'zenmlChat', // Identifies the type of the webview. Used internally diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/activityBar/chatView/ChatDataProvider.ts index fdbcae94..94901728 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/activityBar/chatView/ChatDataProvider.ts @@ -90,12 +90,12 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { if (message.role == 'assistant') { return `

User

-

${content}

+ ${content}
` } else { return `

ZenML Assistant

-

${content}

+ ${content}
` } }) From 7a2b6eaec76c93ea0bf52679a58b073c1e225247 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 30 Aug 2024 14:02:13 -0400 Subject: [PATCH 042/138] feat: overhauled ui again to be more minamalistic --- resources/chat-view/chat.html | 182 ++++++++++++++++++++-------------- resources/chat-view/chat.js | 2 +- 2 files changed, 110 insertions(+), 74 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 859f81ad..905b06ba 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -33,58 +33,93 @@ form { display: contents; } + .tree-view { + background-color: white; + color: #333; + width: 256px; + max-height: 300px; + overflow: auto; + border: 1px solid #e2e8f0; + border-radius: 0.375rem; + box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); + } + .tree-item { + display: flex; + align-items: center; + cursor: pointer; + } + .tree-item:hover { + background-color: #f3f4f6; + } + .tree-item-content { + display: flex; + align-items: center; + flex-grow: 1; + padding: 2px 8px; + } + .tree-item-icon { + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; + } + .tree-item-name { + font-size: 14px; + color: #4b5563; + margin-left: 4px; + flex-grow: 1; + } + .tree-item-children { + display: none; + width: 100%; + } + .tree-item-children.open { + display: block; + } + .tree-item-wrapper { + display: flex; + flex-direction: column; + width: 100%; + } + .tree-item-checkbox { + width: 16px; + height: 16px; + margin-left: 8px; + }
-

ZenML Chat

- -
-
-
-

Options

- +

ZenML Chat

+ +
+
+
+
+ +
-
-
-
- - - - -
- -
- ${chatLogHtml} -
-
+ + +
+
${chatLogHtml}
+
+ - - + document.addEventListener('DOMContentLoaded', (event) => { + const treeViewEl = document.getElementById('tree-view'); + createTreeView(treeData, treeViewEl); + + const optionsToggle = document.getElementById('optionsToggle'); + const optionsDropdown = document.getElementById('optionsDropdown'); + + optionsToggle.addEventListener('click', () => { + optionsDropdown.classList.toggle('hidden'); + }); + + document.addEventListener('click', (event) => { + if (!optionsToggle.contains(event.target) && !optionsDropdown.contains(event.target)) { + optionsDropdown.classList.add('hidden'); + } + }); + }); + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index a6fb9e56..d9a11156 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -27,7 +27,7 @@ const messagesDiv = document.getElementById('messages'); const messageDiv = document.createElement('div'); messageDiv.className = 'message'; - messageDiv.innerHTML = marked(message); + messageDiv.innerHTML = `

${text}

`; messagesDiv.appendChild(messageDiv); } } From 1a8e34ae69948deec851ed4b50c5d081985283ff Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 30 Aug 2024 14:41:02 -0400 Subject: [PATCH 043/138] chore: update code structure and formatting - Added guard checks to improve code robustness - Included semicolons for consistency - Added license headers to relevant files - Reorganized directories and files for better structure --- resources/chat-view/chat.css | 12 ++++ resources/chat-view/chat.html | 12 ++++ resources/chat-view/chat.js | 12 ++++ resources/chat-view/context.js | 12 ++++ src/extension.ts | 2 +- src/services/chatService.ts | 68 +++++++++++++------ .../chatMessage.ts => types/ChatTypes.ts} | 2 +- .../APIView/APIWebviewViewProvider.ts | 12 ++++ .../chatView/ChatDataProvider.ts | 24 +++++-- 9 files changed, 127 insertions(+), 29 deletions(-) rename src/{views/activityBar/chatView/chatMessage.ts => types/ChatTypes.ts} (95%) rename src/views/{activityBar => }/chatView/ChatDataProvider.ts (83%) diff --git a/resources/chat-view/chat.css b/resources/chat-view/chat.css index 323a4ac6..6e5dd04c 100644 --- a/resources/chat-view/chat.css +++ b/resources/chat-view/chat.css @@ -1,3 +1,15 @@ +/* Copyright(c) ZenML GmbH 2024. All Rights Reserved. + Licensed under the Apache License, Version 2.0(the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at: + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + or implied.See the License for the specific language governing + permissions and limitations under the License. */ body { font-family: Arial, sans-serif; margin: 0; diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 905b06ba..7715b1ad 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -1,3 +1,15 @@ + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index d9a11156..9031b4bf 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -1,3 +1,15 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. (function () { const vscode = acquireVsCodeApi(); diff --git a/resources/chat-view/context.js b/resources/chat-view/context.js index a9125d9e..b927168b 100644 --- a/resources/chat-view/context.js +++ b/resources/chat-view/context.js @@ -1,3 +1,15 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. const treeData = [ {name: 'Server'}, {name: 'Environment'}, diff --git a/src/extension.ts b/src/extension.ts index d0b5b600..66169237 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,7 +16,7 @@ import { LSClient } from './services/LSClient'; import { ZenExtension } from './services/ZenExtension'; import { refreshUIComponents } from './utils/refresh'; import { EnvironmentDataProvider } from './views/activityBar/environmentView/EnvironmentDataProvider'; -import { ChatDataProvider } from './views/activityBar/chatView/ChatDataProvider'; +import { ChatDataProvider } from './views/chatView/ChatDataProvider'; import { APIWebviewViewProvider } from './views/activityBar/APIView/APIWebviewViewProvider'; import { registerEnvironmentCommands } from './commands/environment/registry'; import { LSP_ZENML_CLIENT_INITIALIZED } from './utils/constants'; diff --git a/src/services/chatService.ts b/src/services/chatService.ts index 800f20e8..f28af784 100644 --- a/src/services/chatService.ts +++ b/src/services/chatService.ts @@ -1,3 +1,15 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. import * as vscode from 'vscode'; import axios from 'axios'; import { @@ -15,7 +27,7 @@ import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; import { ZenmlGlobalConfigResp } from '../types/LSClientResponseTypes'; import { TreeItem } from 'vscode'; -import { chatMessage } from '../views/activityBar/chatView/chatMessage'; +import { ChatMessage } from '../types/ChatTypes'; export class ChatService { private static instance: ChatService; @@ -51,7 +63,7 @@ export class ChatService { } } - public async getChatResponse(messages: chatMessage[], context?: string[] | undefined): Promise { + public async getChatResponse(messages: ChatMessage[], context?: string[] | undefined): Promise { try { if (context) { messages = await this.addContext(messages, context); @@ -70,8 +82,8 @@ export class ChatService { } } - private async addContext(messages: chatMessage[], requestedContext: string[]): Promise { - let systemMessage: chatMessage = { role: 'system', content: 'Context:' }; + private async addContext(messages: ChatMessage[], requestedContext: string[]): Promise { + let systemMessage: ChatMessage = { role: 'system', content: 'Context:' }; if (requestedContext.includes('serverContext')) { systemMessage.content += this.getServerData(); } @@ -91,7 +103,7 @@ export class ChatService { systemMessage.content += this.getRecentPipelineRunData(); } messages.push(systemMessage); - return messages + return messages; } /** @@ -191,21 +203,31 @@ export class ChatService { let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url + let dashboardUrl = globalConfig.store.url; if (!apiToken) { throw new Error('API Token is not available in gloval configuration'); } let pipelineRunSteps = await this.getPipelineRunNodes('step'); + let logs = await Promise.all(pipelineRunSteps[0].map(async (step) => { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - 'accept': 'application/json' + if (step && step.id) { + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + 'accept': 'application/json' + } + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); } - }); - return response.data; + } else { + console.warn('Encountered a null or invalid step.'); + } + })); return logs; } @@ -227,22 +249,26 @@ export class ChatService { let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url + let dashboardUrl = globalConfig.store.url; if (!apiToken) { throw new Error('API Token is not available in gloval configuration'); } let logs = await Promise.all(stepData.map(async (step) => { - let validStep = step as JsonObject; // Type assertion. If we're not sure if step is not null, should change this to a guard check. - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - 'accept': 'application/json' - } - }); - return response.data; + if (step && typeof step === 'object' && 'id' in step) { + let validStep = step as JsonObject; + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + 'accept': 'application/json' + } + }); + return response.data; + } + return null; // returns null if step is invalid })); + logs = logs.filter(log => log !== null); // Filters out possible null logs. return logs; } } diff --git a/src/views/activityBar/chatView/chatMessage.ts b/src/types/ChatTypes.ts similarity index 95% rename from src/views/activityBar/chatView/chatMessage.ts rename to src/types/ChatTypes.ts index f1b94333..b4922d65 100644 --- a/src/views/activityBar/chatView/chatMessage.ts +++ b/src/types/ChatTypes.ts @@ -11,7 +11,7 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. -export interface chatMessage { +export interface ChatMessage { role: string, content: string, } \ No newline at end of file diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index 27dd42b4..a74d8ac1 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -1,3 +1,15 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. import * as vscode from 'vscode'; export class APIWebviewViewProvider implements vscode.WebviewViewProvider { diff --git a/src/views/activityBar/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts similarity index 83% rename from src/views/activityBar/chatView/ChatDataProvider.ts rename to src/views/chatView/ChatDataProvider.ts index 94901728..c05196bd 100644 --- a/src/views/activityBar/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -1,12 +1,24 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. import * as vscode from 'vscode'; import * as fs from 'fs'; import * as marked from 'marked'; -import { ChatService } from '../../../services/chatService'; -import { chatMessage } from './chatMessage'; +import { ChatService } from '../../services/chatService'; +import { ChatMessage } from '../../types/ChatTypes'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; - private messages: chatMessage[] = []; // Array to store chat messages + private messages: ChatMessage[] = []; // Array to store chat messages private chatService: ChatService; constructor(private readonly context: vscode.ExtensionContext) { @@ -87,16 +99,16 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { return this.messages.filter(msg => msg['role'] !== 'system') .map((message) => { let content = marked.parse(message.content); - if (message.role == 'assistant') { + if (message.role === 'assistant') { return `

User

${content} -
` +
`; } else { return `

ZenML Assistant

${content} -
` +
`; } }) .join(''); From 715167e1a1acebe85f71e075a5aa60ce6b6cdc67 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Fri, 30 Aug 2024 20:43:28 -0400 Subject: [PATCH 044/138] feat: connected context to selector for server/stack/components/environment --- resources/chat-view/chat.html | 10 +++++----- resources/chat-view/chat.js | 18 +++--------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 7715b1ad..5b6e052a 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -129,12 +129,11 @@

ZenML Chat

${chatLogHtml}
- diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index b4922d65..013ef6f9 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -14,4 +14,10 @@ export interface ChatMessage { role: string, content: string, +} + +export interface TreeItem { + name: string, + value?: string, + children?: TreeItem[] } \ No newline at end of file diff --git a/src/views/activityBar/pipelineView/PipelineDataProvider.ts b/src/views/activityBar/pipelineView/PipelineDataProvider.ts index eb30010e..ad9e26d7 100644 --- a/src/views/activityBar/pipelineView/PipelineDataProvider.ts +++ b/src/views/activityBar/pipelineView/PipelineDataProvider.ts @@ -35,6 +35,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { private eventBus = EventBus.getInstance(); private zenmlClientReady = false; private pipelineData: PipelineTreeItem[] = []; + public pipelineRuns: PipelineRun[] = []; constructor() { super(); @@ -138,6 +139,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { if ('runs' in result) { const { runs, total, total_pages, current_page, items_per_page } = result; + this.pipelineRuns = runs this.pagination = { currentPage: current_page, diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 930b36ba..4a691f89 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -14,7 +14,8 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as marked from 'marked'; import { ChatService } from '../../services/chatService'; -import { ChatMessage } from '../../types/ChatTypes'; +import { ChatMessage, TreeItem } from '../../types/ChatTypes'; +import { PipelineDataProvider } from '../activityBar'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; @@ -82,11 +83,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { // Chat log HTML const chatLogHtml = this.renderChatLog(); + let treeItemHtml = this.getTreeHtml() // Replace placeholders in the HTML with actual values html = html.replace('${cssUri}', cssUri.toString()); html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${markedUri}', markedUri.toString()); + html = html.replace('${treeItemHtml}', treeItemHtml) html = html.replace('${chatLogHtml}', chatLogHtml); return html; @@ -114,6 +117,76 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { .join(''); } + private getPipelineData(): TreeItem[] { + let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns + let pipelineTreeItems = pipelineRuns.map((run) => { + let formattedStartTime = new Date(run.startTime).toLocaleString(); + let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; + return { + name: run.name, + value: run.id, + children: [ + { name: run.status }, + { name: run.stackName }, + { name: formattedStartTime }, + { name: formattedEndTime }, + { name: `${run.os} ${run.osVersion}` }, + { name: run.pythonVersion }, + ] + } + }) + return pipelineTreeItems + } + + private getTreeData() { + let pipelineData = this.getPipelineData() + let treeData: TreeItem[] = [ + {name: 'Server', value: 'serverContext'}, + {name: 'Environment', value: 'environmentContext'}, + { + name: 'Pipeline Runs', + value: 'pipelineContext', + children : pipelineData + }, + {name: 'Stack', value: 'stackContext'}, + {name: 'Stack Components', value: 'stackComponentsContext'} + ]; + return treeData + } + + private convertTreeDataToHtml(treeData: TreeItem[], level = 0) { + let convertedTreeData = treeData.map((item) => { + let iconSvg = ''; + let childrenEl = ''; + + if (item.children) { + iconSvg = '' + childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
` + } + + let checkboxEl = level < 2 ? `` : '' + + return `
+
+
+ + ${iconSvg} + + ${item.name} + ${checkboxEl} +
+ ${childrenEl} +
+
` + }) + return convertedTreeData.join('\n') + } + + private getTreeHtml(): string { + let treeData = this.getTreeData() + return this.convertTreeDataToHtml(treeData) + } + /** * Update the webview with the latest content, including the chat message. */ From 8f4facf86841f22eca19af82baad0a941c35976f Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Sat, 31 Aug 2024 22:41:36 -0400 Subject: [PATCH 047/138] feat: hooked up individual pipeline runs metadata, logs, and panel data since it makes several requests there is some delay for the response --- resources/chat-view/chat.html | 79 -------------------------- resources/chat-view/chat.js | 5 +- src/services/chatService.ts | 49 ++++++++++------ src/views/chatView/ChatDataProvider.ts | 5 +- 4 files changed, 38 insertions(+), 100 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index d0695009..8a9a6172 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -131,85 +131,6 @@

ZenML Chat

- +

ZenML Chat

-
+
- -
-
+ +
+
+

Chat with your ZenML pipelines and data

+
+
+ + +

Please select a provider

diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 22b57e7d..9c28088e 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -83,13 +83,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { // Chat log HTML const chatLogHtml = this.renderChatLog(); - let treeItemHtml = this.getTreeHtml() + let treeItemHtml = this.getTreeHtml(); // Replace placeholders in the HTML with actual values html = html.replace('${cssUri}', cssUri.toString()); html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${markedUri}', markedUri.toString()); - html = html.replace('${treeItemHtml}', treeItemHtml) + html = html.replace('${treeItemHtml}', treeItemHtml); html = html.replace('${chatLogHtml}', chatLogHtml); return html; @@ -108,7 +108,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { ${content}
`; } else { - return `
+ return `

ZenML Assistant

${content}
`; @@ -118,11 +118,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } private getPipelineData(): TreeItem[] { - let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns + let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; let pipelineTreeItems = pipelineRuns.map((run) => { let formattedStartTime = new Date(run.startTime).toLocaleString(); let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; - let stringValue = `Pipeline run:${JSON.stringify(run)}` + let stringValue = `Pipeline run:${JSON.stringify(run)}`; return { name: run.name, value: stringValue, @@ -134,13 +134,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { { name: `${run.os} ${run.osVersion}` }, { name: run.pythonVersion }, ] - } - }) - return pipelineTreeItems + }; + }); + return pipelineTreeItems; } private getTreeData() { - let pipelineData = this.getPipelineData() + let pipelineData = this.getPipelineData(); let treeData: TreeItem[] = [ {name: 'Server', value: 'serverContext'}, {name: 'Environment', value: 'environmentContext'}, @@ -152,7 +152,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { {name: 'Stack', value: 'stackContext'}, {name: 'Stack Components', value: 'stackComponentsContext'} ]; - return treeData + return treeData; } private convertTreeDataToHtml(treeData: TreeItem[], level = 0) { @@ -161,11 +161,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { let childrenEl = ''; if (item.children) { - iconSvg = '' - childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
` + iconSvg = ''; + childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
`; } - let checkboxEl = level < 2 ? `` : '' + let checkboxEl = level < 2 ? `` : ''; return `
@@ -178,14 +178,14 @@ export class ChatDataProvider implements vscode.WebviewViewProvider {
${childrenEl}
-
` - }) - return convertedTreeData.join('\n') +
`; + }); + return convertedTreeData.join('\n'); } private getTreeHtml(): string { - let treeData = this.getTreeData() - return this.convertTreeDataToHtml(treeData) + let treeData = this.getTreeData(); + return this.convertTreeDataToHtml(treeData); } /** From 95c34889b33a2c1cb9d389fa3a184afaf47aabe2 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Wed, 4 Sep 2024 15:56:28 -0400 Subject: [PATCH 051/138] feat(api, chat): integrate API secrets registration and restructure chat service - Integrated the new secrets registration command into the API key view. - Used the new secrets naming convention to provide TokenJS with the correct API key. - Moved chatService.ts into the chatView directory for better organization. --- package.json | 6 +- src/extension.ts | 3 +- .../APIView/APIWebviewViewProvider.ts | 238 ++++++++---------- src/views/chatView/ChatDataProvider.ts | 2 +- .../chatView}/chatService.ts | 31 +-- 5 files changed, 125 insertions(+), 155 deletions(-) rename src/{services => views/chatView}/chatService.ts (89%) diff --git a/package.json b/package.json index 58457b46..41086389 100644 --- a/package.json +++ b/package.json @@ -355,8 +355,8 @@ "icon": "$(server-environment)" }, { - "id": "zenmlChatView", - "name": "Chat", + "id": "zenmlAPIView", + "name": "Chatbot", "type": "webview" } ], @@ -430,7 +430,7 @@ "group": "navigation" }, { - "when": "view == zenmlChatView", + "when": "view == zenmlAPIView", "command": "zenml.openChat", "group": "navigation" } diff --git a/src/extension.ts b/src/extension.ts index 66169237..a2efa3ec 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -43,8 +43,9 @@ export async function activate(context: vscode.ExtensionContext) { }); registerEnvironmentCommands(context); + const apiWebviewProvider = new APIWebviewViewProvider(context); context.subscriptions.push( - vscode.window.registerWebviewViewProvider('zenmlChatView', new APIWebviewViewProvider(context)) + vscode.window.registerWebviewViewProvider('zenmlAPIView', apiWebviewProvider) ); diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index 078388a9..11ebd473 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -21,163 +21,131 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, token: vscode.CancellationToken - ): Thenable | void { + ): void { this._view = webviewView; - + webviewView.webview.options = { enableScripts: true, localResourceRoots: [this.context.extensionUri], }; - + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - + + // Add this line to log console messages from the webview webviewView.webview.onDidReceiveMessage( (message) => { switch (message.command) { - case 'registerApiKey': - this._handleRegisterApiKey(message.provider); + case 'registerLLMAPIKey': + this._handleRegisterApiKey(); break; } }, ); + } private _getHtmlForWebview(webview: vscode.Webview): string { - const logoUri = webview.asWebviewUri( - vscode.Uri.joinPath(this.context.extensionUri, 'resources', 'zenml_logo.png') - ); - - return ` - - - - - - - Chat with your ZenML pipelines and data - - - -
-
-

Chat with your ZenML pipelines and data

-
-
- - -

Please select a provider

-
-
- - - - - `; + return ` + + + + + + + ZenML API View + + + +

Register AI API Key

+ + + + + `; + } - private _handleRegisterApiKey(provider: string): void { - switch (provider) { - case 'OpenAI': - vscode.commands.executeCommand('zenml.registerOpenAIAPIKey'); - break; - case 'Claude': - vscode.commands.executeCommand('zenml.registerClaudeAPIKey'); - break; - case 'Gemini': - vscode.commands.executeCommand('zenml.registerGeminiAPIKey'); - break; - default: - console.error(`Unsupported provider: ${provider}`); - } + private _handleRegisterApiKey(): void { + vscode.window.showInformationMessage('Registering LLM API Key'); + vscode.commands.executeCommand('zenml.registerLLMAPIKey'); } dispose(): void { this._disposables.forEach((disposable) => disposable.dispose()); } -} \ No newline at end of file +} + +function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 9c28088e..8c07dbf4 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -13,7 +13,7 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import * as marked from 'marked'; -import { ChatService } from '../../services/chatService'; +import { ChatService } from './chatService'; import { ChatMessage, TreeItem } from '../../types/ChatTypes'; import { PipelineDataProvider } from '../activityBar'; diff --git a/src/services/chatService.ts b/src/views/chatView/chatService.ts similarity index 89% rename from src/services/chatService.ts rename to src/views/chatView/chatService.ts index 860c8209..051ce831 100644 --- a/src/services/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -19,17 +19,15 @@ import { ServerDataProvider, StackDataProvider, StackComponentTreeItem, -} from '../views/activityBar'; -import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; -import { EnvironmentDataProvider } from '../views/activityBar/environmentView/EnvironmentDataProvider'; -import { LSClient } from './LSClient'; -import { DagArtifact, DagStep, PipelineRunDag } from '../types/PipelineTypes'; -import { JsonObject } from '../views/panel/panelView/PanelTreeItem'; -import { ZenmlGlobalConfigResp } from '../types/LSClientResponseTypes'; +} from '../activityBar'; +import { ComponentDataProvider } from '../activityBar/componentView/ComponentDataProvider'; +import { EnvironmentDataProvider } from '../activityBar/environmentView/EnvironmentDataProvider'; +import { LSClient } from '../../services/LSClient'; +import { DagArtifact, DagStep, PipelineRunDag } from '../../types/PipelineTypes'; +import { JsonObject } from '../panel/panelView/PanelTreeItem'; +import { ZenmlGlobalConfigResp } from '../../types/LSClientResponseTypes'; import { TreeItem } from 'vscode'; -import { ChatMessage } from '../types/ChatTypes'; -import { PipelineRun } from '../types/PipelineTypes'; -import { node } from 'webpack'; +import { ChatMessage } from '../../types/ChatTypes'; export class ChatService { private static instance: ChatService; @@ -52,16 +50,15 @@ export class ChatService { try { const module = await import('token.js'); const { TokenJS } = module; - - // Use context to access secrets - const apiKey = await this.context.secrets.get('API_KEY'); + let provider = 'Gemini'; // this is the only provider available at the moment + const apiKey = await this.context.secrets.get(`zenml.${provider.toLowerCase()}.key`); if (!apiKey) { vscode.window.showErrorMessage('No Gemini API key found. Please register one'); - } this.tokenjs = new TokenJS({ apiKey }); } catch (error) { console.error('Error loading TokenJS:', error); + vscode.window.showErrorMessage(`Failed to initialize ChatService: ${error instanceof Error ? error.message : 'Unknown error'}`); } } @@ -80,7 +77,11 @@ export class ChatService { return completion.choices[0]?.message?.content || 'No content'; } catch (error) { console.error('Error with Gemini API:', error); - return 'Error: Unable to get a response from Gemini.'; + if (error instanceof Error) { + return `Error: ${error.message}. Please check your API key and network connection.`; + } else { + return 'Error: An unexpected error occurred while getting a response from Gemini.'; + } } } From df46ea65456e23323ce512d8f44690009a75f61e Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Wed, 4 Sep 2024 15:10:26 -0700 Subject: [PATCH 052/138] feat: Added clearchat button to clear the chat logs --- resources/chat-view/chat.html | 3 +++ resources/chat-view/chat.js | 9 +++++++++ src/views/chatView/ChatDataProvider.ts | 9 +++++++++ 3 files changed, 21 insertions(+) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index d14ef9e6..1b1f0c7f 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -148,6 +148,9 @@

ZenML Chat

+
${chatLogHtml}
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 8104e97d..9a1da3aa 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -36,4 +36,13 @@ } document.getElementById('chatForm').addEventListener('submit', sendMessage); + + // Clears Chat Log + function clearChatLog() { + vscode.postMessage({ + command: 'clearChat', + }); + } + + document.getElementById('clearChat').addEventListener('click', clearChatLog); })(); diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 8c07dbf4..e715c685 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -60,6 +60,10 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { if (message.command === 'sendMessage' && message.text?.trim()) { await this.addMessage(message.text, message.context); } + + if (message.command === 'clearChat') { + await this.clearChatLog(); + } } /** @@ -117,6 +121,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { .join(''); } + private clearChatLog(): void { + this.messages.length = 0; + this.updateWebviewContent(); + } + private getPipelineData(): TreeItem[] { let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; let pipelineTreeItems = pipelineRuns.map((run) => { From 5f0532c239f76b7f287c2a259bd34597d7c4a7d4 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Wed, 4 Sep 2024 19:22:36 -0400 Subject: [PATCH 053/138] chore: changed dropdown direction, removed redundant header, changed sample question text color to be readable --- resources/chat-view/chat.html | 15 ++++++--------- src/commands/secrets/cmds.ts | 2 +- .../activityBar/APIView/APIWebviewViewProvider.ts | 3 +-- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 1b1f0c7f..154f4caf 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -58,14 +58,11 @@ } .rounded-lg { background-color: var(--vscode-button-background); - color: var(--vscode-button-foreground); + color: var(--vscode-editor-foreground); } .rounded-lg:hover { background-color: var(--vscode-button-hoverBackground); } - .text-mutedText { - color: var(--vscode-descriptionForeground); - } .hide { display: none; } @@ -138,7 +135,7 @@

ZenML Chat

- @@ -156,10 +153,10 @@

ZenML Chat

${chatLogHtml}
-
+
-
+

What can this chat do?

@@ -169,7 +166,7 @@

ZenML Chat

-
+

Help me improve my stack

@@ -179,7 +176,7 @@

ZenML Chat

-
+

Generate a summary of my stats

diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index e7819c65..8c4b0289 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -22,7 +22,7 @@ const registerLLMAPIKey = async (context: ExtensionContext) => { ]; const selectedOption = await vscode.window.showQuickPick(options, { - placeHolder: 'Please select an LLM.', + placeHolder: 'Please select a provider.', canPickMany: false, }); diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index 11ebd473..59c6b567 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -116,8 +116,7 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { -

Register AI API Key

- + - + \ No newline at end of file From 27905576c09dd258f52c94b2fa738a6f02b62e9c Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Wed, 4 Sep 2024 20:58:15 -0400 Subject: [PATCH 055/138] feat: hooked up model dropdown to backend the info for which model is passed along with the context as the first string in the array --- resources/chat-view/chat.html | 9 ++++++--- resources/chat-view/chat.js | 4 +++- src/views/chatView/chatService.ts | 11 +++++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 5e007f38..43b2219f 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -165,9 +165,12 @@

ZenML Chat

diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 9a1da3aa..846356c4 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -19,7 +19,9 @@ const formData = new FormData(event.target); const text = formData.get('messageInput').trim(); const checkedBoxes = document.querySelectorAll('#tree-view input[type="checkbox"]:checked'); - const checkedValues = Array.from(checkedBoxes).map(checkbox => checkbox.value); + const model = document.querySelector('.model-dropdown') + let checkedValues = Array.from(checkedBoxes).map(checkbox => checkbox.value); + checkedValues.unshift(model.value) // const sampleQuestions = document.querySelector('#sampleQuestions') if (text) { diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts index 051ce831..2d73e49c 100644 --- a/src/views/chatView/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -33,6 +33,13 @@ export class ChatService { private static instance: ChatService; private tokenjs: any; private context: vscode.ExtensionContext; + private providers: Record = { + 'claude-3-5-sonnet-20240620': 'anthropic', + 'claude-3-opus-20240229': 'anthropic', + 'gpt-4o': 'openai', + 'gemini-1.5-pro': 'gemini', + 'gemini-1.5-flash': 'gemini' + }; private constructor(context: vscode.ExtensionContext) { this.context = context; @@ -69,8 +76,8 @@ export class ChatService { } const completion = await this.tokenjs.chat.completions.create({ - provider: 'gemini', - model: 'gemini-1.5-flash', + provider: this.providers[context[0]], + model: context[0], messages: messages, }); From 494b35c0495574f06542f9a378e7da9be0f6006d Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Wed, 4 Sep 2024 22:00:24 -0400 Subject: [PATCH 056/138] feat: set up streaming text response from AI provider - Implemented streaming functionality for receiving text responses from the AI provider. - Improved response handling to support real-time updates as data is streamed. --- resources/chat-view/chat.html | 4 +- resources/chat-view/chat.js | 39 ++++++++++- src/views/chatView/ChatDataProvider.ts | 89 ++++++++++++++++---------- src/views/chatView/chatService.ts | 20 ++++-- 4 files changed, 109 insertions(+), 43 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 43b2219f..c241aa95 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -5,6 +5,7 @@ ZenML Chat + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 846356c4..f1e78220 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -19,9 +19,9 @@ const formData = new FormData(event.target); const text = formData.get('messageInput').trim(); const checkedBoxes = document.querySelectorAll('#tree-view input[type="checkbox"]:checked'); - const model = document.querySelector('.model-dropdown') + const model = document.querySelector('.model-dropdown'); let checkedValues = Array.from(checkedBoxes).map(checkbox => checkbox.value); - checkedValues.unshift(model.value) + checkedValues.unshift(model.value); // const sampleQuestions = document.querySelector('#sampleQuestions') if (text) { @@ -32,6 +32,7 @@ }); event.target.reset(); + appendToChat(text, 'user'); // sampleQuestions.classList.remove('flex') // sampleQuestions.classList.add('hide') } @@ -47,4 +48,38 @@ } document.getElementById('clearChat').addEventListener('click', clearChatLog); + function appendToChat(text, role) { + const chatMessages = document.getElementById('chatMessages'); + let messageDiv; + + if (role === 'assistant') { + messageDiv = chatMessages.querySelector('div[data-role="assistant"]:last-child') || + chatMessages.lastElementChild; + + if (!messageDiv || messageDiv.getAttribute('data-role') !== 'assistant') { + messageDiv = document.createElement('div'); + messageDiv.className = 'p-4 rounded-lg mb-2'; + messageDiv.setAttribute('data-role', 'assistant'); + messageDiv.innerHTML = `

ZenML Assistant

`; + chatMessages.appendChild(messageDiv); + currentAssistantMessage = ''; + } + + currentAssistantMessage += text; + const contentDiv = messageDiv.querySelector('.message-content'); + + requestAnimationFrame(() => { + contentDiv.innerHTML = marked.parse(currentAssistantMessage); + chatMessages.scrollTop = chatMessages.scrollHeight; + }); + } + } + + window.addEventListener('message', event => { + const message = event.data; + console.log('Received message:', message); + if (message.command === 'receiveMessage') { + appendToChat(message.text, 'assistant'); + } + }); })(); diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index e715c685..2ab8ad77 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -97,29 +97,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { html = html.replace('${chatLogHtml}', chatLogHtml); return html; - } - - /** - * Render the chat log as HTML. - */ - private renderChatLog(): string { - return this.messages.filter(msg => msg['role'] !== 'system') - .map((message) => { - let content = marked.parse(message.content); - if (message.role === 'user') { - return `
-

User

- ${content} -
`; - } else { - return `
-

ZenML Assistant

- ${content} -
`; - } - }) - .join(''); - } + }; private clearChatLog(): void { this.messages.length = 0; @@ -210,28 +188,73 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } /** - * Add a message to the chat log, get a response from Gemini, and update the webview. + * Add a message to the chat log, get a response from AI provider and update the webview. */ + private streamingMessage: ChatMessage | null = null; + async addMessage(message: string, context?: string[]) { - this.messages.push({role: 'user', content: `${message}`}); + this.messages.push({role: 'user', content: message}); + this.updateWebviewContent(); try { - const botResponse = await this.chatService.getChatResponse(this.messages, context); - this.messages.push({role: 'assistant', content: `${botResponse}`}); + const responseGenerator = this.chatService.getChatResponse(this.messages, context); + this.streamingMessage = {role: 'assistant', content: ''}; + + for await (const partialResponse of responseGenerator) { + for (const letter of partialResponse) { + this.streamingMessage.content += letter; + this.sendMessageToWebview(letter); + // Add a tiny delay between characters to simulate typing + await new Promise(resolve => setTimeout(resolve, 5)); // Adjust delay as needed + } + } + + this.messages.push(this.streamingMessage); + this.streamingMessage = null; this.updateWebviewContent(); - this.sendMessageToWebview(`${botResponse}`); } catch (error) { - this.messages.push({role: 'system', content: 'Error: Unable to get response from Gemini'}); - this.updateWebviewContent(); + console.error('Error in addMessage:', error); + this.sendMessageToWebview('Error: Unable to get response from Gemini'); } } /** - * Send a message from Gemini back to the webview + * Render the chat log as HTML. + */ + private renderChatLog(): string { + return this.messages.filter(msg => msg['role'] !== 'system') + .map((message) => { + let content = marked.parse(message.content); + if (message.role === 'user') { + return `
+

User

+ ${content} +
`; + } else { + return `
+

ZenML Assistant

+ ${content} +
`; + } + }) + .join('') + this.renderStreamingMessage(); + } + + private renderStreamingMessage(): string { + if (!this.streamingMessage) {return '';} + let content = marked.parse(this.streamingMessage.content); + return `
+

ZenML Assistant

+ ${content} +
`; + } + + /** + * Send a message from AI provider back to the webview */ - private sendMessageToWebview(message: string) { + private sendMessageToWebview(text: string) { if (this._view) { - this._view.webview.postMessage({ command: 'recieveMessage', text: message }); + this._view.webview.postMessage({ command: 'receiveMessage', text }); } } } diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts index 2d73e49c..69a4e86a 100644 --- a/src/views/chatView/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -69,25 +69,31 @@ export class ChatService { } } - public async getChatResponse(messages: ChatMessage[], context?: string[] | undefined): Promise { + public async *getChatResponse(messages: ChatMessage[], context?: string[] | undefined): AsyncGenerator { try { - if (context) { + if (context && context.length > 0) { messages = await this.addContext(messages, context); - } + const completion = await this.tokenjs.chat.completions.create({ + streaming: true, provider: this.providers[context[0]], model: context[0], messages: messages, }); - - return completion.choices[0]?.message?.content || 'No content'; + + for await (const part of completion) { + yield part.choices[0]?.delta?.content || ''; + } + } else { + throw new Error("Context is either undefined or empty."); + } } catch (error) { console.error('Error with Gemini API:', error); if (error instanceof Error) { - return `Error: ${error.message}. Please check your API key and network connection.`; + yield `Error: ${error.message}. Please check your API key and network connection.`; } else { - return 'Error: An unexpected error occurred while getting a response from Gemini.'; + yield 'Error: An unexpected error occurred while getting a response from Gemini.'; } } } From f7514b9d633e560fd3c05431f5b3a4547a5a0b53 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 5 Sep 2024 00:12:07 -0400 Subject: [PATCH 057/138] fix: re-added clear chat button and cleaned up 'getChatResponse' method --- resources/chat-view/chat.html | 2 ++ resources/chat-view/chat.js | 6 ++++-- src/views/chatView/ChatDataProvider.ts | 2 +- src/views/chatView/chatService.ts | 26 +++++++++++++++----------- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index c241aa95..d4ec122a 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -180,6 +180,7 @@

ZenML Chat

+
@@ -216,6 +217,7 @@

ZenML Chat

${chatLogHtml}
+
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index f1e78220..a661ca56 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -38,16 +38,18 @@ } } - document.getElementById('chatForm').addEventListener('submit', sendMessage); + document.getElementById('chatForm').addEventListener('submit', sendMessage); + // Clears Chat Log function clearChatLog() { vscode.postMessage({ command: 'clearChat', }); } - + document.getElementById('clearChat').addEventListener('click', clearChatLog); + function appendToChat(text, role) { const chatMessages = document.getElementById('chatMessages'); let messageDiv; diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 2ab8ad77..4a5232f5 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -197,7 +197,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.updateWebviewContent(); try { - const responseGenerator = this.chatService.getChatResponse(this.messages, context); + const responseGenerator = this.chatService.getChatResponse(this.messages, context || []); this.streamingMessage = {role: 'assistant', content: ''}; for await (const partialResponse of responseGenerator) { diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts index 69a4e86a..2970d9a5 100644 --- a/src/views/chatView/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -69,25 +69,29 @@ export class ChatService { } } - public async *getChatResponse(messages: ChatMessage[], context?: string[] | undefined): AsyncGenerator { + public async *getChatResponse(messages: ChatMessage[], context: string[]): AsyncGenerator { try { - if (context && context.length > 0) { + if (context) { messages = await this.addContext(messages, context); - + } const completion = await this.tokenjs.chat.completions.create({ streaming: true, - provider: this.providers[context[0]], - model: context[0], + provider: this.providers[context[0]] ?? '', + model: context[0] ?? '', messages: messages, }); - - for await (const part of completion) { - yield part.choices[0]?.delta?.content || ''; + + if (Symbol.asyncIterator in completion) { + for await (const part of completion) { + yield part.choices[0]?.delta?.content || ''; + } + } else if (completion.choices && completion.choices.length > 0) { + yield completion.choices[0].message.content || ''; + } else { + throw new Error('Unexpected response from API'); } - } else { - throw new Error("Context is either undefined or empty."); - } + } catch (error) { console.error('Error with Gemini API:', error); if (error instanceof Error) { From 53ba0df1e5ba20e709879ee01fd2d4582982e164 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Thu, 5 Sep 2024 02:44:05 -0400 Subject: [PATCH 058/138] feat/style/fix: added state to localstorage for model/context dropdowns, made updateWebview only update the chat messages, fixed markdown rendering by making the input a text area, making a custom renderer function for inline codeblocks, and styling, styled buttons and chat messages to follow vscode theme --- resources/chat-view/chat.html | 101 ++++++++++++++++++++++--- resources/chat-view/chat.js | 61 ++++++++++++--- src/views/chatView/ChatDataProvider.ts | 63 ++++++++++++--- 3 files changed, 195 insertions(+), 30 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index d4ec122a..8d84bd08 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -47,7 +47,7 @@ background-color: var(--vscode-input-background); } .rounded-lg { - background-color: var(--vscode-button-background); + background-color: var(--vscode-editor-background); color: var(--vscode-editor-foreground); } .rounded-lg:hover { @@ -131,12 +131,13 @@ } .context-button { font-size: 0.75rem; - padding: 0.25rem 0.5rem; - background-color: var(--vscode-button-background); - color: var(--vscode-button-foreground); + padding: 0.25rem; + background-color: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); border: none; border-radius: 0.25rem; cursor: pointer; + margin-right: 0.5rem; } .context-button:hover { background-color: var(--vscode-button-hoverBackground); @@ -148,6 +149,57 @@ margin-top: 0.25rem; z-index: 10; } + #chatMessages h1, #chatMessages h2, #chatMessages h3, #chatMessages h4, #chatMessages h5, #chatMessages h6 { + color: var(--vscode-editor-foreground); + font-weight: bold; + } + + #chatMessages p { + color: var(--vscode-editor-foreground); + } + + #chatMessages ul, #chatMessages ol { + color: var(--vscode-editor-foreground); + } + + #chatMessages li { + color: var(--vscode-editor-foreground); + } + + #chatMessages pre { + padding: 1em; + border-radius: 5px; + } + + #chatMessages code { + padding: 0.2em 0.4em; + border-radius: 3px; + } + + #chatMessages pre code { + display: block; + background-color: var(--vscode-textCodeBlock-background); + color: var(--vscode-editor-foreground); + padding: 1em; + border-radius: 5px; + } + + #chatMessages a { + color: var(--vscode-textLink-foreground); + text-decoration: none; + } + + #chatMessages strong { + font-weight: bold; + } + + #chatMessages em { + font-style: italic; + } + + .assistant { + background-color: var(--vscode-input-background); + } @@ -157,7 +209,7 @@

ZenML Chat

- +
+
+ +
-
- -
+
@@ -217,7 +272,6 @@

ZenML Chat

${chatLogHtml}
-
@@ -258,6 +312,35 @@

ZenML Chat

} }); }); + const textarea = document.getElementById('messageInput') + + textarea.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + // Prevent the default behavior of the Enter key + e.preventDefault(); + + // Get the form element + const form = document.getElementById('chatForm'); + + // Create a new SubmitEvent + const event = new SubmitEvent('submit', { + bubbles: true, + cancelable: true, + }); + + // Dispatch the event on the form + form.dispatchEvent(event); + } + }); + + textarea.addEventListener('keypress', (e) => { + if (e.key === 'Enter' && e.shiftKey) { + // Insert a new line when Shift+Enter is pressed + e.preventDefault(); + textarea.value += '\n'; + textarea.scrollTop = textarea.scrollHeight; + } + }); }); diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index a661ca56..cfbccfc1 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -13,16 +13,42 @@ (function () { const vscode = acquireVsCodeApi(); + // Function to save the current state + function saveState() { + const model = document.querySelector('.model-dropdown').value; + const checkedBoxes = Array.from(document.querySelectorAll('#tree-view input[type="checkbox"]:checked')) + .map(checkbox => checkbox.value); + + localStorage.setItem('selectedModel', model); + localStorage.setItem('checkedContexts', JSON.stringify(checkedBoxes)); + } + + // Function to restore the saved state + function restoreState() { + const selectedModel = localStorage.getItem('selectedModel'); + const checkedContexts = JSON.parse(localStorage.getItem('checkedContexts')) || []; + + if (selectedModel) { + document.querySelector('.model-dropdown').value = selectedModel; + } + + checkedContexts.forEach(value => { + const checkbox = document.querySelector(`#tree-view input[type="checkbox"][value="${value}"]`); + if (checkbox) { + checkbox.checked = true; + } + }); + } + // Function to send the message function sendMessage(event) { event.preventDefault(); const formData = new FormData(event.target); - const text = formData.get('messageInput').trim(); + const text = formData.get('messageInput'); const checkedBoxes = document.querySelectorAll('#tree-view input[type="checkbox"]:checked'); - const model = document.querySelector('.model-dropdown'); + const model = document.querySelector('.model-dropdown').value; let checkedValues = Array.from(checkedBoxes).map(checkbox => checkbox.value); - checkedValues.unshift(model.value); - // const sampleQuestions = document.querySelector('#sampleQuestions') + checkedValues.unshift(model); if (text) { vscode.postMessage({ @@ -32,13 +58,20 @@ }); event.target.reset(); - appendToChat(text, 'user'); - // sampleQuestions.classList.remove('flex') - // sampleQuestions.classList.add('hide') + saveState(); // Save state before refresh } } - document.getElementById('chatForm').addEventListener('submit', sendMessage); + window.addEventListener('message', event => { + const message = event.data; + switch (message.command) { + case 'updateChatLog': + document.getElementById('chatMessages').innerHTML = message.chatLogHtml; + break; + } + }); + + document.getElementById('chatForm').addEventListener('submit', sendMessage); // Clears Chat Log @@ -46,6 +79,7 @@ vscode.postMessage({ command: 'clearChat', }); + saveState(); // Save state before refresh } document.getElementById('clearChat').addEventListener('click', clearChatLog); @@ -60,7 +94,7 @@ if (!messageDiv || messageDiv.getAttribute('data-role') !== 'assistant') { messageDiv = document.createElement('div'); - messageDiv.className = 'p-4 rounded-lg mb-2'; + messageDiv.className = 'p-4 assistant'; messageDiv.setAttribute('data-role', 'assistant'); messageDiv.innerHTML = `

ZenML Assistant

`; chatMessages.appendChild(messageDiv); @@ -84,4 +118,13 @@ appendToChat(message.text, 'assistant'); } }); + + // Add event listeners to save state when dropdown or checkboxes change + document.querySelector('.model-dropdown').addEventListener('change', saveState); + document.querySelectorAll('#tree-view input[type="checkbox"]').forEach(checkbox => { + checkbox.addEventListener('change', saveState); + }); + + // Restore state when page loads + document.addEventListener('DOMContentLoaded', restoreState); })(); diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 4a5232f5..ca0acd6a 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -12,7 +12,7 @@ // permissions and limitations under the License. import * as vscode from 'vscode'; import * as fs from 'fs'; -import * as marked from 'marked'; +import { marked } from 'marked'; import { ChatService } from './chatService'; import { ChatMessage, TreeItem } from '../../types/ChatTypes'; import { PipelineDataProvider } from '../activityBar'; @@ -36,7 +36,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { ) { this._view = webviewView; this.configureWebViewOptions(webviewView.webview); - this.updateWebviewContent(); + this.loadWebviewContent(); // Handle messages received from the webview webviewView.webview.onDidReceiveMessage(async message => { @@ -57,7 +57,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { * Handle incoming messages from the webview. */ private async handleWebviewMessage(message: any) { - if (message.command === 'sendMessage' && message.text?.trim()) { + if (message.command === 'sendMessage' && message.text) { await this.addMessage(message.text, message.context); } @@ -81,9 +81,6 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { const jsUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') ); - const markedUri = webview.asWebviewUri( - vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'marked.min.js') - ); // Chat log HTML const chatLogHtml = this.renderChatLog(); @@ -92,7 +89,6 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { // Replace placeholders in the HTML with actual values html = html.replace('${cssUri}', cssUri.toString()); html = html.replace('${jsUri}', jsUri.toString()); - html = html.replace('${markedUri}', markedUri.toString()); html = html.replace('${treeItemHtml}', treeItemHtml); html = html.replace('${chatLogHtml}', chatLogHtml); @@ -175,10 +171,20 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { return this.convertTreeDataToHtml(treeData); } + private updateWebviewContent() { + if (this._view) { + const chatLogHtml = this.renderChatLog(); + this._view.webview.postMessage({ + command: 'updateChatLog', + chatLogHtml: chatLogHtml + }); + } + } + /** * Update the webview with the latest content, including the chat message. */ - private updateWebviewContent() { + private loadWebviewContent() { if (this._view) { this._view.webview.html = this.getWebviewContent( this._view.webview, @@ -217,21 +223,37 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.sendMessageToWebview('Error: Unable to get response from Gemini'); } } - + /** * Render the chat log as HTML. */ private renderChatLog(): string { + const renderer = { + code({ text, lang, escaped, isInline }) { + const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); + + if (isInline) { + return `${code}`; + } + + return '
'
+          + code
+          + '
\n'; + } + }; + + marked.use({ renderer }); + return this.messages.filter(msg => msg['role'] !== 'system') .map((message) => { let content = marked.parse(message.content); if (message.role === 'user') { - return `
+ return `

User

${content}
`; } else { - return `
+ return `

ZenML Assistant

${content}
`; @@ -242,8 +264,25 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private renderStreamingMessage(): string { if (!this.streamingMessage) {return '';} + + const renderer = { + code({ text, lang, escaped, isInline }) { + const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); + + if (isInline) { + return `${code}`; + } + + return '
'
+          + code
+          + '
\n'; + } + }; + + marked.use({ renderer }); + let content = marked.parse(this.streamingMessage.content); - return `
+ return `

ZenML Assistant

${content}
`; From 4da1dea2d3439a3e8e2a7e8d46d4f35e496398ec Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Thu, 5 Sep 2024 12:05:34 -0400 Subject: [PATCH 059/138] feat: added tooltips for context selector --- resources/chat-view/chat.html | 2 +- src/types/ChatTypes.ts | 3 ++- src/views/chatView/ChatDataProvider.ts | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 8d84bd08..cb102fe9 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -226,7 +226,7 @@

ZenML Chat

- + diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 013ef6f9..105c6a18 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -19,5 +19,6 @@ export interface ChatMessage { export interface TreeItem { name: string, value?: string, - children?: TreeItem[] + children?: TreeItem[], + title?: string } \ No newline at end of file diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index ca0acd6a..cc4f4b31 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -109,6 +109,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { return { name: run.name, value: stringValue, + title: "Includes all code, logs, and metadata for a specific pipeline run with message", children: [ { name: run.status }, { name: run.stackName }, @@ -125,15 +126,16 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getTreeData() { let pipelineData = this.getPipelineData(); let treeData: TreeItem[] = [ - {name: 'Server', value: 'serverContext'}, - {name: 'Environment', value: 'environmentContext'}, + {name: 'Server', value: 'serverContext', title: 'Includes all server metadata with message'}, + {name: 'Environment', value: 'environmentContext', title: 'Includes all server metadata with message'}, { name: 'Pipeline Runs', value: 'pipelineContext', + title: 'Includes all code, logs, and metadata for pipeline runs with message', children : pipelineData }, - {name: 'Stack', value: 'stackContext'}, - {name: 'Stack Components', value: 'stackComponentsContext'} + {name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message'}, + {name: 'Stack Components', value: 'stackComponentsContext', title: 'Includes all stack component metadata with message'} ]; return treeData; } @@ -142,12 +144,17 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { let convertedTreeData = treeData.map((item) => { let iconSvg = ''; let childrenEl = ''; + let title = '' if (item.children) { iconSvg = ''; childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
`; } + if (item.title) { + title = item.title + } + let checkboxEl = level < 2 ? `` : ''; return `
@@ -156,7 +163,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { ${iconSvg} - ${item.name} + ${item.name} ${checkboxEl}
${childrenEl} From 54cd2241cc2f40df419f1b464772c81359dc452d Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 5 Sep 2024 13:57:20 -0400 Subject: [PATCH 060/138] feat: reverse message display order in main webview --- resources/chat-view/chat.js | 7 ++++--- src/views/chatView/ChatDataProvider.ts | 11 ++++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index cfbccfc1..94e35f8a 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -90,14 +90,15 @@ if (role === 'assistant') { messageDiv = chatMessages.querySelector('div[data-role="assistant"]:last-child') || - chatMessages.lastElementChild; + chatMessages.firstElementChild; if (!messageDiv || messageDiv.getAttribute('data-role') !== 'assistant') { messageDiv = document.createElement('div'); messageDiv.className = 'p-4 assistant'; messageDiv.setAttribute('data-role', 'assistant'); messageDiv.innerHTML = `

ZenML Assistant

`; - chatMessages.appendChild(messageDiv); + + chatMessages.insertBefore(messageDiv, chatMessages.firstChild); currentAssistantMessage = ''; } @@ -106,7 +107,7 @@ requestAnimationFrame(() => { contentDiv.innerHTML = marked.parse(currentAssistantMessage); - chatMessages.scrollTop = chatMessages.scrollHeight; + chatMessages.scrollTop = 0; }); } } diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index cc4f4b31..0e08765e 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -144,7 +144,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { let convertedTreeData = treeData.map((item) => { let iconSvg = ''; let childrenEl = ''; - let title = '' + let title = ''; if (item.children) { iconSvg = ''; @@ -152,7 +152,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } if (item.title) { - title = item.title + title = item.title; } let checkboxEl = level < 2 ? `` : ''; @@ -218,7 +218,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.streamingMessage.content += letter; this.sendMessageToWebview(letter); // Add a tiny delay between characters to simulate typing - await new Promise(resolve => setTimeout(resolve, 5)); // Adjust delay as needed + await new Promise(resolve => setTimeout(resolve, 1)); // Adjust delay as needed } } @@ -236,6 +236,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { */ private renderChatLog(): string { const renderer = { + // @ts-ignore code({ text, lang, escaped, isInline }) { const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); @@ -249,9 +250,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } }; + // @ts-ignore marked.use({ renderer }); return this.messages.filter(msg => msg['role'] !== 'system') + .reverse() .map((message) => { let content = marked.parse(message.content); if (message.role === 'user') { @@ -273,6 +276,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { if (!this.streamingMessage) {return '';} const renderer = { + // @ts-ignore code({ text, lang, escaped, isInline }) { const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); @@ -286,6 +290,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } }; + // @ts-ignore marked.use({ renderer }); let content = marked.parse(this.streamingMessage.content); From 2ce39bca69d0064456417eb07c37ee86b721140e Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 5 Sep 2024 17:16:35 -0400 Subject: [PATCH 061/138] feat(chat): disable message submission during AI response streaming --- resources/chat-view/chat.html | 36 ++++++++++++++++++++++++++ resources/chat-view/chat.js | 22 +++++++++++++++- src/views/chatView/ChatDataProvider.ts | 6 ++++- 3 files changed, 62 insertions(+), 2 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index cb102fe9..369e5744 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -313,6 +313,17 @@

ZenML Chat

}); }); const textarea = document.getElementById('messageInput') + const sendButton = document.getElementById('sendMessage'); + + function disableInput() { + isInputDisabled = true; + sendButton.disabled = true; + } + + function enableInput() { + isInputDisabled = false; + sendButton.disabled = false; + } textarea.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { @@ -333,6 +344,31 @@

ZenML Chat

} }); + // Listen for messages from the extension + window.addEventListener('message', event => { + const message = event.data; + if (message.command === 'disableInput') { + disableInput(); + } else if (message.command === 'enableInput') { + enableInput(); + } + }); + + textarea.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + + if (!isInputDisabled) { + const form = document.getElementById('chatForm'); + const event = new SubmitEvent('submit', { + bubbles: true, + cancelable: true, + }); + form.dispatchEvent(event); + } + } + }); + textarea.addEventListener('keypress', (e) => { if (e.key === 'Enter' && e.shiftKey) { // Insert a new line when Shift+Enter is pressed diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 94e35f8a..ae9fb3ea 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -43,6 +43,8 @@ // Function to send the message function sendMessage(event) { event.preventDefault(); + if (isInputDisabled) { return; } + const formData = new FormData(event.target); const text = formData.get('messageInput'); const checkedBoxes = document.querySelectorAll('#tree-view input[type="checkbox"]:checked'); @@ -112,11 +114,29 @@ } } + let isInputDisabled = false; + + function disableInput() { + isInputDisabled = true; + document.getElementById('sendMessage').disabled = true; + } + + function enableInput() { + isInputDisabled = false; + document.getElementById('sendMessage').disabled = false; + } + window.addEventListener('message', event => { const message = event.data; console.log('Received message:', message); if (message.command === 'receiveMessage') { - appendToChat(message.text, 'assistant'); + if (message.text === 'disableInput') { + disableInput(); + } else if (message.text === 'enableInput') { + enableInput(); + } else { + appendToChat(message.text, 'assistant'); + } } }); diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 0e08765e..4dcbf3cf 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -213,11 +213,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { const responseGenerator = this.chatService.getChatResponse(this.messages, context || []); this.streamingMessage = {role: 'assistant', content: ''}; + // Send message to disable input + this.sendMessageToWebview('disableInput'); + for await (const partialResponse of responseGenerator) { for (const letter of partialResponse) { this.streamingMessage.content += letter; this.sendMessageToWebview(letter); - // Add a tiny delay between characters to simulate typing await new Promise(resolve => setTimeout(resolve, 1)); // Adjust delay as needed } } @@ -225,9 +227,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.messages.push(this.streamingMessage); this.streamingMessage = null; this.updateWebviewContent(); + this.sendMessageToWebview('enableInput'); } catch (error) { console.error('Error in addMessage:', error); this.sendMessageToWebview('Error: Unable to get response from Gemini'); + this.sendMessageToWebview('enableInput'); } } From 2e93dacfb250b4935e7f31200d5706f6885bfd60 Mon Sep 17 00:00:00 2001 From: Nathaniel Xu Date: Thu, 5 Sep 2024 18:58:32 -0400 Subject: [PATCH 062/138] feat: pipline runs context displays first ten and has an expand button to show more --- resources/chat-view/chat.html | 4 +++ resources/chat-view/chat.js | 34 ++++++++++++++++++++------ src/types/ChatTypes.ts | 3 ++- src/views/chatView/ChatDataProvider.ts | 19 ++++++++------ 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 369e5744..3d2740a5 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -200,6 +200,10 @@ .assistant { background-color: var(--vscode-input-background); } + + .expand span { + color: var(--vscode-textLink-foreground) + } diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index ae9fb3ea..a53ef417 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -128,14 +128,18 @@ window.addEventListener('message', event => { const message = event.data; - console.log('Received message:', message); - if (message.command === 'receiveMessage') { - if (message.text === 'disableInput') { - disableInput(); - } else if (message.text === 'enableInput') { - enableInput(); - } else { - appendToChat(message.text, 'assistant'); + switch (message.command) { + case 'updateChatLog': + document.getElementById('chatMessages').innerHTML = message.chatLogHtml; + break; + case 'receiveMessage': { + if (message.text === 'disableInput') { + disableInput(); + } else if (message.text === 'enableInput') { + enableInput(); + } else { + appendToChat(message.text, 'assistant'); + } } } }); @@ -148,4 +152,18 @@ // Restore state when page loads document.addEventListener('DOMContentLoaded', restoreState); + + const expandElement = document.querySelector('.expand') + + function expandList(event) { + event.preventDefault() + event.stopPropagation() + const hiddenElements = expandElement.parentElement.querySelectorAll('.hidden') + hiddenElements.forEach(element => { + element.classList.remove('hidden') + }) + expandElement.remove() + } + + expandElement.addEventListener('click', expandList) })(); diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 105c6a18..1de0198d 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -20,5 +20,6 @@ export interface TreeItem { name: string, value?: string, children?: TreeItem[], - title?: string + title?: string, + hidden?: boolean } \ No newline at end of file diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 4dcbf3cf..5ad8d1d1 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -102,7 +102,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getPipelineData(): TreeItem[] { let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; - let pipelineTreeItems = pipelineRuns.map((run) => { + let pipelineTreeItems: TreeItem[] = pipelineRuns.map((run, index) => { let formattedStartTime = new Date(run.startTime).toLocaleString(); let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; let stringValue = `Pipeline run:${JSON.stringify(run)}`; @@ -110,6 +110,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { name: run.name, value: stringValue, title: "Includes all code, logs, and metadata for a specific pipeline run with message", + hidden: index > 9, children: [ { name: run.status }, { name: run.stackName }, @@ -120,6 +121,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { ] }; }); + if (pipelineTreeItems.length > 9) { pipelineTreeItems.push({ name: 'Expand' })} return pipelineTreeItems; } @@ -143,21 +145,24 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private convertTreeDataToHtml(treeData: TreeItem[], level = 0) { let convertedTreeData = treeData.map((item) => { let iconSvg = ''; - let childrenEl = ''; - let title = ''; + let childrenEl= '', title = '', hidden = ''; if (item.children) { iconSvg = ''; childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
`; } - if (item.title) { - title = item.title; - } + if (item.title) { title = item.title } + if (item.hidden) { hidden = " hidden" } let checkboxEl = level < 2 ? `` : ''; - return `
+ if (item.name == 'Expand') { + hidden += ' expand'; + checkboxEl = '' + } + + return `
From 8adf61ee9a6f90fee823506d3682b5c60db6ab60 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Thu, 5 Sep 2024 18:08:03 -0700 Subject: [PATCH 063/138] feat: Added button functionality for sample questions --- resources/chat-view/chat.html | 12 +- resources/chat-view/chat.js | 70 ++++++-- src/extension.ts | 21 ++- src/types/ChatTypes.ts | 14 +- src/types/LSClientResponseTypes.ts | 22 +-- .../APIView/APIWebviewViewProvider.ts | 27 ++- .../pipelineView/PipelineDataProvider.ts | 2 +- src/views/chatView/ChatDataProvider.ts | 98 ++++++----- src/views/chatView/chatService.ts | 156 +++++++++++------- 9 files changed, 262 insertions(+), 160 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index cb102fe9..351d5ebb 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -244,7 +244,9 @@

ZenML Chat

-

What can this chat do?

+

ZenML Chat

-

Help me improve my stack

+

ZenML Chat

-

Generate a summary of my stats

+
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 94e35f8a..7f3e1c1e 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -16,9 +16,10 @@ // Function to save the current state function saveState() { const model = document.querySelector('.model-dropdown').value; - const checkedBoxes = Array.from(document.querySelectorAll('#tree-view input[type="checkbox"]:checked')) - .map(checkbox => checkbox.value); - + const checkedBoxes = Array.from( + document.querySelectorAll('#tree-view input[type="checkbox"]:checked') + ).map(checkbox => checkbox.value); + localStorage.setItem('selectedModel', model); localStorage.setItem('checkedContexts', JSON.stringify(checkedBoxes)); } @@ -33,7 +34,9 @@ } checkedContexts.forEach(value => { - const checkbox = document.querySelector(`#tree-view input[type="checkbox"][value="${value}"]`); + const checkbox = document.querySelector( + `#tree-view input[type="checkbox"][value="${value}"]` + ); if (checkbox) { checkbox.checked = true; } @@ -73,25 +76,25 @@ document.getElementById('chatForm').addEventListener('submit', sendMessage); - - // Clears Chat Log + // Clears chat log function clearChatLog() { vscode.postMessage({ command: 'clearChat', }); saveState(); // Save state before refresh } - + document.getElementById('clearChat').addEventListener('click', clearChatLog); - + function appendToChat(text, role) { const chatMessages = document.getElementById('chatMessages'); let messageDiv; if (role === 'assistant') { - messageDiv = chatMessages.querySelector('div[data-role="assistant"]:last-child') || - chatMessages.firstElementChild; - + messageDiv = + chatMessages.querySelector('div[data-role="assistant"]:last-child') || + chatMessages.firstElementChild; + if (!messageDiv || messageDiv.getAttribute('data-role') !== 'assistant') { messageDiv = document.createElement('div'); messageDiv.className = 'p-4 assistant'; @@ -101,10 +104,10 @@ chatMessages.insertBefore(messageDiv, chatMessages.firstChild); currentAssistantMessage = ''; } - + currentAssistantMessage += text; const contentDiv = messageDiv.querySelector('.message-content'); - + requestAnimationFrame(() => { contentDiv.innerHTML = marked.parse(currentAssistantMessage); chatMessages.scrollTop = 0; @@ -128,4 +131,45 @@ // Restore state when page loads document.addEventListener('DOMContentLoaded', restoreState); + + // Send a pre-determined message when the button is clicked + function sendSampleMessage() { + const model = document.querySelector('.model-dropdown').value; + let context = [model]; + let buttonValue = this.value; + let message; + + switch (buttonValue) { + case 'aboutChat': + message = 'What can this chat do?'; + break; + case 'improveStack': + message = 'Help me improve my stack.'; + context.push('stackContext'); + context.push('stackComponentsContext'); + break; + case 'summarizeStats': + message = 'Generate a summary of my stats.'; + context.push('serverContext'); + context.push('environmentContext'); + context.push('pipelineContext'); + context.push('stackContext'); + context.push('stackComponentsContext'); + break; + default: + break; + } + + vscode.postMessage({ + command: 'sendMessage', + text: message, + context: context, + }); + + saveState(); + } + + document.querySelectorAll('.sampleQuestions').forEach(button => { + button.addEventListener('click', sendSampleMessage); + }); })(); diff --git a/src/extension.ts b/src/extension.ts index a2efa3ec..f985eed8 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -48,7 +48,6 @@ export async function activate(context: vscode.ExtensionContext) { vscode.window.registerWebviewViewProvider('zenmlAPIView', apiWebviewProvider) ); - /** * Register's the openChat command ********************************************************************************************** */ @@ -59,8 +58,8 @@ export async function activate(context: vscode.ExtensionContext) { 'ZenML Chat', // Title of the panel displayed to the user vscode.ViewColumn.One, // Editor column to show the new webview panel in { - enableScripts: true, // Enable scripts in the webview - retainContextWhenHidden: true // Keep the webview's context when hidden + enableScripts: true, // Enable scripts in the webview + retainContextWhenHidden: true, // Keep the webview's context when hidden } ); @@ -72,18 +71,18 @@ export async function activate(context: vscode.ExtensionContext) { const dummyCancellationToken: vscode.CancellationToken = { isCancellationRequested: false, - onCancellationRequested: (callback) => { - // No-op, as the token is never cancelled - return new vscode.Disposable(() => {}); - } - }; + onCancellationRequested: callback => { + // No-op, as the token is never cancelled + return new vscode.Disposable(() => {}); + }, + }; // Use CancellationToken.None // const cancellationToken = vscode.CancellationToken.None; // Fake WebviewView object const fakeWebviewView = { - webview: panel.webview, - onDidDispose: panel.onDidDispose + webview: panel.webview, + onDidDispose: panel.onDidDispose, } as vscode.WebviewView; // Call resolveWebviewView with all required arguments @@ -91,7 +90,7 @@ export async function activate(context: vscode.ExtensionContext) { // Ensure to dispose of the panel when not needed panel.onDidDispose(() => { - // Clean up resources or perform any necessary actions when the panel is disposed + // Clean up resources or perform any necessary actions when the panel is disposed }); }); diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 105c6a18..05d6e418 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -12,13 +12,13 @@ // permissions and limitations under the License. export interface ChatMessage { - role: string, - content: string, + role: string; + content: string; } export interface TreeItem { - name: string, - value?: string, - children?: TreeItem[], - title?: string -} \ No newline at end of file + name: string; + value?: string; + children?: TreeItem[]; + title?: string; +} diff --git a/src/types/LSClientResponseTypes.ts b/src/types/LSClientResponseTypes.ts index 30bd79ff..8d9ea8bb 100644 --- a/src/types/LSClientResponseTypes.ts +++ b/src/types/LSClientResponseTypes.ts @@ -56,17 +56,17 @@ export type GetActiveStackResponse = ActiveStackResponse | ErrorMessageResponse; /***** Global Config Type *****/ export interface ZenmlGlobalConfigResp { - user_id: string, - user_email: string, - analytics_opt_in: boolean, - version: string, - active_stack_id: string, - active_workspace_name: string, - store: ZenmlStoreConfig + user_id: string; + user_email: string; + analytics_opt_in: boolean; + version: string; + active_stack_id: string; + active_workspace_name: string; + store: ZenmlStoreConfig; } export interface ZenmlStoreConfig { - type: string, - url: string, - api_token?: string -} \ No newline at end of file + type: string; + url: string; + api_token?: string; +} diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index 59c6b567..a3b6e114 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -23,29 +23,25 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { token: vscode.CancellationToken ): void { this._view = webviewView; - + webviewView.webview.options = { enableScripts: true, localResourceRoots: [this.context.extensionUri], }; - + webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - + // Add this line to log console messages from the webview - webviewView.webview.onDidReceiveMessage( - (message) => { - switch (message.command) { - case 'registerLLMAPIKey': - this._handleRegisterApiKey(); - break; - } - }, - ); - + webviewView.webview.onDidReceiveMessage(message => { + switch (message.command) { + case 'registerLLMAPIKey': + this._handleRegisterApiKey(); + break; + } + }); } private _getHtmlForWebview(webview: vscode.Webview): string { - const nonce = getNonce(); return ` @@ -127,7 +123,6 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { `; - } private _handleRegisterApiKey(): void { @@ -136,7 +131,7 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { } dispose(): void { - this._disposables.forEach((disposable) => disposable.dispose()); + this._disposables.forEach(disposable => disposable.dispose()); } } diff --git a/src/views/activityBar/pipelineView/PipelineDataProvider.ts b/src/views/activityBar/pipelineView/PipelineDataProvider.ts index ad9e26d7..273ca5d5 100644 --- a/src/views/activityBar/pipelineView/PipelineDataProvider.ts +++ b/src/views/activityBar/pipelineView/PipelineDataProvider.ts @@ -139,7 +139,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { if ('runs' in result) { const { runs, total, total_pages, current_page, items_per_page } = result; - this.pipelineRuns = runs + this.pipelineRuns = runs; this.pagination = { currentPage: current_page, diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 0e08765e..1457db3a 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -93,7 +93,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { html = html.replace('${chatLogHtml}', chatLogHtml); return html; - }; + } private clearChatLog(): void { this.messages.length = 0; @@ -102,14 +102,14 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getPipelineData(): TreeItem[] { let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; - let pipelineTreeItems = pipelineRuns.map((run) => { + let pipelineTreeItems = pipelineRuns.map(run => { let formattedStartTime = new Date(run.startTime).toLocaleString(); let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; let stringValue = `Pipeline run:${JSON.stringify(run)}`; return { name: run.name, value: stringValue, - title: "Includes all code, logs, and metadata for a specific pipeline run with message", + title: 'Includes all code, logs, and metadata for a specific pipeline run with message', children: [ { name: run.status }, { name: run.stackName }, @@ -117,7 +117,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { { name: formattedEndTime }, { name: `${run.os} ${run.osVersion}` }, { name: run.pythonVersion }, - ] + ], }; }); return pipelineTreeItems; @@ -126,28 +126,42 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getTreeData() { let pipelineData = this.getPipelineData(); let treeData: TreeItem[] = [ - {name: 'Server', value: 'serverContext', title: 'Includes all server metadata with message'}, - {name: 'Environment', value: 'environmentContext', title: 'Includes all server metadata with message'}, + { + name: 'Server', + value: 'serverContext', + title: 'Includes all server metadata with message', + }, + { + name: 'Environment', + value: 'environmentContext', + title: 'Includes all server metadata with message', + }, { name: 'Pipeline Runs', value: 'pipelineContext', title: 'Includes all code, logs, and metadata for pipeline runs with message', - children : pipelineData + children: pipelineData, + }, + { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, + { + name: 'Stack Components', + value: 'stackComponentsContext', + title: 'Includes all stack component metadata with message', }, - {name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message'}, - {name: 'Stack Components', value: 'stackComponentsContext', title: 'Includes all stack component metadata with message'} ]; return treeData; } private convertTreeDataToHtml(treeData: TreeItem[], level = 0) { - let convertedTreeData = treeData.map((item) => { - let iconSvg = ''; + let convertedTreeData = treeData.map(item => { + let iconSvg = + ''; let childrenEl = ''; let title = ''; if (item.children) { - iconSvg = ''; + iconSvg = + ''; childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
`; } @@ -155,7 +169,8 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { title = item.title; } - let checkboxEl = level < 2 ? `` : ''; + let checkboxEl = + level < 2 ? `` : ''; return `
@@ -183,7 +198,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { const chatLogHtml = this.renderChatLog(); this._view.webview.postMessage({ command: 'updateChatLog', - chatLogHtml: chatLogHtml + chatLogHtml: chatLogHtml, }); } } @@ -206,12 +221,12 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private streamingMessage: ChatMessage | null = null; async addMessage(message: string, context?: string[]) { - this.messages.push({role: 'user', content: message}); + this.messages.push({ role: 'user', content: message }); this.updateWebviewContent(); try { const responseGenerator = this.chatService.getChatResponse(this.messages, context || []); - this.streamingMessage = {role: 'assistant', content: ''}; + this.streamingMessage = { role: 'assistant', content: '' }; for await (const partialResponse of responseGenerator) { for (const letter of partialResponse) { @@ -230,7 +245,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.sendMessageToWebview('Error: Unable to get response from Gemini'); } } - + /** * Render the chat log as HTML. */ @@ -239,55 +254,56 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { // @ts-ignore code({ text, lang, escaped, isInline }) { const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); - + if (isInline) { return `${code}`; } - - return '
'
-          + code
-          + '
\n'; - } + + return '
' + code + '
\n'; + }, }; // @ts-ignore marked.use({ renderer }); - return this.messages.filter(msg => msg['role'] !== 'system') - .reverse() - .map((message) => { - let content = marked.parse(message.content); - if (message.role === 'user') { - return `
+ return ( + this.messages + .filter(msg => msg['role'] !== 'system') + .reverse() + .map(message => { + let content = marked.parse(message.content); + if (message.role === 'user') { + return `

User

${content}
`; - } else { - return `
+ } else { + return `

ZenML Assistant

${content}
`; - } - }) - .join('') + this.renderStreamingMessage(); + } + }) + .join('') + this.renderStreamingMessage() + ); } private renderStreamingMessage(): string { - if (!this.streamingMessage) {return '';} + if (!this.streamingMessage) { + return ''; + } const renderer = { // @ts-ignore code({ text, lang, escaped, isInline }) { const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); - + if (isInline) { return `${code}`; } - - return '
'
-          + code
-          + '
\n'; - } + + return '
' + code + '
\n'; + }, }; // @ts-ignore diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts index 2970d9a5..2e2e9c03 100644 --- a/src/views/chatView/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -38,7 +38,7 @@ export class ChatService { 'claude-3-opus-20240229': 'anthropic', 'gpt-4o': 'openai', 'gemini-1.5-pro': 'gemini', - 'gemini-1.5-flash': 'gemini' + 'gemini-1.5-flash': 'gemini', }; private constructor(context: vscode.ExtensionContext) { @@ -65,16 +65,21 @@ export class ChatService { this.tokenjs = new TokenJS({ apiKey }); } catch (error) { console.error('Error loading TokenJS:', error); - vscode.window.showErrorMessage(`Failed to initialize ChatService: ${error instanceof Error ? error.message : 'Unknown error'}`); + vscode.window.showErrorMessage( + `Failed to initialize ChatService: ${error instanceof Error ? error.message : 'Unknown error'}` + ); } } - public async *getChatResponse(messages: ChatMessage[], context: string[]): AsyncGenerator { + public async *getChatResponse( + messages: ChatMessage[], + context: string[] + ): AsyncGenerator { try { if (context) { messages = await this.addContext(messages, context); } - + const completion = await this.tokenjs.chat.completions.create({ streaming: true, provider: this.providers[context[0]] ?? '', @@ -91,7 +96,6 @@ export class ChatService { } else { throw new Error('Unexpected response from API'); } - } catch (error) { console.error('Error with Gemini API:', error); if (error instanceof Error) { @@ -102,7 +106,10 @@ export class ChatService { } } - private async addContext(messages: ChatMessage[], requestedContext: any[]): Promise { + private async addContext( + messages: ChatMessage[], + requestedContext: any[] + ): Promise { let systemMessage: ChatMessage = { role: 'system', content: 'Context: ' }; for (let context of requestedContext) { // TODO possibly create interface for context, change requestContext type (currently any[]) @@ -213,94 +220,129 @@ export class ChatService { //change back to just step or add artifact command let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); let lsClient = LSClient.getInstance(); - let dagData = await Promise.all(pipelineData.map(async (node: PipelineTreeItem) => { - let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - return dag; - })); - - let stepData = await Promise.all(dagData.map(async (dag: PipelineRunDag) => { - let filteredNodes = await Promise.all(dag.nodes.map(async (node: DagArtifact|DagStep) => { - if (type === "all" || node.type === type) { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - })); - return filteredNodes.filter((value) => value !== null); - })); + let dagData = await Promise.all( + pipelineData.map(async (node: PipelineTreeItem) => { + let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [ + node.id, + ]); + return dag; + }) + ); + + let stepData = await Promise.all( + dagData.map(async (dag: PipelineRunDag) => { + let filteredNodes = await Promise.all( + dag.nodes.map(async (node: DagArtifact | DagStep) => { + if (type === 'all' || node.type === type) { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [ + node.id, + ]); + } + return null; + }) + ); + return filteredNodes.filter(value => value !== null); + }) + ); return stepData; } private async getLogData() { let lsClient = LSClient.getInstance(); - + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; let dashboardUrl = globalConfig.store.url; if (!apiToken) { - throw new Error('API Token is not available in gloval configuration'); + throw new Error('API Token is not available in global configuration'); } let pipelineRunSteps = await this.getPipelineRunNodes('step'); - let logs = await Promise.all(pipelineRunSteps[0].map(async (step) => { - if (step && step.id) { - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - 'accept': 'application/json' - } - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${step.id}`, error); + let logs = await Promise.all( + pipelineRunSteps[0].map(async step => { + if (step && step.id) { + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); + } + } else { + console.warn('Encountered a null or invalid step.'); } - } else { - console.warn('Encountered a null or invalid step.'); - } - - })); + }) + ); return logs; } - private async getPipelineRunLogs(id:string) { + private async getPipelineRunLogs(id: string) { let lsClient = LSClient.getInstance(); let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); - let stepData = await Promise.all(dagData.nodes.map(async (node: DagArtifact|DagStep) => { - if (node.type === "step") { + let stepData = await Promise.all( + dagData.nodes.map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); } return null; }) ); - stepData = stepData.filter((value) => value !== null); + stepData = stepData.filter(value => value !== null); let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); let apiToken = globalConfig.store.api_token; let dashboardUrl = globalConfig.store.url; if (!apiToken) { - throw new Error('API Token is not available in gloval configuration'); + throw new Error('API Token is not available in global configuration'); } - let logs = await Promise.all(stepData.map(async (step) => { - if (step && typeof step === 'object' && 'id' in step) { - let validStep = step as JsonObject; - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - 'accept': 'application/json' - } - }); - return response.data; - } - return null; // returns null if step is invalid - })); + let logs = await Promise.all( + stepData.map(async step => { + if (step && typeof step === 'object' && 'id' in step) { + let validStep = step as JsonObject; + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } + return null; // returns null if step is invalid + }) + ); logs = logs.filter(log => log !== null); // Filters out possible null logs. return logs; } + + private async getMetadata() { + let lsClient = LSClient.getInstance(); + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url; + + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } + + // Grabs a list of metadata IDs + // Eventually should be reformatted to grab individual metadata based on the metadata ID + let metadata = await axios.get(`${dashboardUrl}/api/v1/run-metadata`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + } } From 623ee0a252821af9640ae5a28fd47c419cd76023 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 6 Sep 2024 12:22:38 -0700 Subject: [PATCH 064/138] feat: Added sample question --- resources/chat-view/chat.html | 9 +++++++++ resources/chat-view/chat.js | 3 +++ src/views/chatView/chatService.ts | 3 +++ 3 files changed, 15 insertions(+) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index f229c993..e4a0b3e6 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -278,6 +278,15 @@

ZenML Chat

+
+
+
+ +
+
+
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index fc233990..793460a4 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -182,6 +182,9 @@ context.push('stackContext'); context.push('stackComponentsContext'); break; + case 'summarizeLogs': + message = 'Generate a summary of my logs.'; + context.push('logsContext'); default: break; } diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts index 2e2e9c03..ad73b157 100644 --- a/src/views/chatView/chatService.ts +++ b/src/views/chatView/chatService.ts @@ -129,6 +129,9 @@ export class ChatService { case 'stackComponentsContext': systemMessage.content += this.getStackComponentData(); break; + case 'logsContext': + systemMessage.content += this.getLogData(); + break; default: if (context.includes('Pipeline run:')) { systemMessage.content += context; From 536f1bf91c6754b3fee19a2d187c0cab898bb8e2 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 8 Sep 2024 17:09:04 -0400 Subject: [PATCH 065/138] refactor(chat): reorganize chat code for better clarity and consistency - Moved code from 'chatService.ts' and some code from 'ChatDataProvider.ts' into 'chatRenderer.ts', 'chatMessageHandler.ts', and 'utils.ts'. - Removed 'chatService.ts' after migrating its functionality to new files in src/views/chatView. - Moved chat command registration from 'extension.ts' to 'registry.ts' and 'cmds.ts' in src/commands/chat. - Added a usage warning message to the open chat button. Improves overall code clarity and structure. --- package.json | 2 +- src/commands/chat/cmds.ts | 52 +++ src/commands/chat/registry.ts | 34 ++ src/extension.ts | 52 +-- src/services/ZenExtension.ts | 3 +- .../APIView/APIWebviewViewProvider.ts | 10 +- src/views/chatView/ChatDataProvider.ts | 287 ++------------ src/views/chatView/chatMessageHandler.ts | 23 ++ src/views/chatView/chatRenderer.ts | 129 +++++++ src/views/chatView/chatService.ts | 351 ------------------ src/views/chatView/utils.ts | 346 +++++++++++++++++ 11 files changed, 618 insertions(+), 671 deletions(-) create mode 100644 src/commands/chat/cmds.ts create mode 100644 src/commands/chat/registry.ts create mode 100644 src/views/chatView/chatMessageHandler.ts create mode 100644 src/views/chatView/chatRenderer.ts delete mode 100644 src/views/chatView/chatService.ts create mode 100644 src/views/chatView/utils.ts diff --git a/package.json b/package.json index 1e7ae211..3f28151f 100644 --- a/package.json +++ b/package.json @@ -282,7 +282,7 @@ }, { "command": "zenml.openChat", - "title": "Open Chat", + "title": "Open Chat. *Using this feature will incure charges as normal with your LLM provider.", "icon": "$(comment-discussion)", "category": "ZenML Chat" }, diff --git a/src/commands/chat/cmds.ts b/src/commands/chat/cmds.ts new file mode 100644 index 00000000..f3131d86 --- /dev/null +++ b/src/commands/chat/cmds.ts @@ -0,0 +1,52 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import * as vscode from 'vscode'; +import { ChatDataProvider } from '../../views/chatView/ChatDataProvider'; + +const openChat = (context: vscode.ExtensionContext) => { + const panel = vscode.window.createWebviewPanel( + 'zenmlChat', + 'ZenML Chat', + vscode.ViewColumn.One, + { + enableScripts: true, + retainContextWhenHidden: true, + } + ); + + const chatDataProvider = new ChatDataProvider(context); + + const fakeContext = {} as vscode.WebviewViewResolveContext; + + const dummyCancellationToken: vscode.CancellationToken = { + isCancellationRequested: false, + onCancellationRequested: callback => { + return new vscode.Disposable(() => {}); + }, + }; + + const fakeWebviewView = { + webview: panel.webview, + onDidDispose: panel.onDidDispose, + } as vscode.WebviewView; + + chatDataProvider.resolveWebviewView(fakeWebviewView, fakeContext, dummyCancellationToken); + + panel.onDidDispose(() => { + // Clean up resources or perform any necessary actions when the panel is disposed + }); +}; + +export const chatCommands = { + openChat, +}; \ No newline at end of file diff --git a/src/commands/chat/registry.ts b/src/commands/chat/registry.ts new file mode 100644 index 00000000..5ca4cf56 --- /dev/null +++ b/src/commands/chat/registry.ts @@ -0,0 +1,34 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import { chatCommands } from './cmds'; +import { registerCommand } from '../../common/vscodeapi'; +import { ZenExtension } from '../../services/ZenExtension'; +import { ExtensionContext, commands } from 'vscode'; + +export const registerChatCommands = (context: ExtensionContext) => { + try { + const registeredCommands = [ + registerCommand('zenml.openChat', () => chatCommands.openChat(context)), + ]; + + registeredCommands.forEach(cmd => { + context.subscriptions.push(cmd); + ZenExtension.commandDisposables.push(cmd); + }); + + commands.executeCommand('setContext', 'chatCommandsRegistered', true); + } catch (error) { + console.error('Error registering chat commands:', error); + commands.executeCommand('setContext', 'chatCommandsRegistered', false); + } +}; \ No newline at end of file diff --git a/src/extension.ts b/src/extension.ts index f985eed8..c978f549 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -23,6 +23,7 @@ import { LSP_ZENML_CLIENT_INITIALIZED } from './utils/constants'; import { toggleCommands } from './utils/global'; import DagRenderer from './commands/pipelines/DagRender'; import WebviewBase from './common/WebviewBase'; +import { registerChatCommands } from './commands/chat/registry'; export async function activate(context: vscode.ExtensionContext) { const eventBus = EventBus.getInstance(); @@ -48,57 +49,6 @@ export async function activate(context: vscode.ExtensionContext) { vscode.window.registerWebviewViewProvider('zenmlAPIView', apiWebviewProvider) ); - /** - * Register's the openChat command ********************************************************************************************** - */ - let renderChatCommand = vscode.commands.registerCommand('zenml.openChat', () => { - // chatDataProvider.resolveWebviewView(fakeWebviewView, panel); - const panel = vscode.window.createWebviewPanel( - 'zenmlChat', // Identifies the type of the webview. Used internally - 'ZenML Chat', // Title of the panel displayed to the user - vscode.ViewColumn.One, // Editor column to show the new webview panel in - { - enableScripts: true, // Enable scripts in the webview - retainContextWhenHidden: true, // Keep the webview's context when hidden - } - ); - - // Create an instance of ChatDataProvider - const chatDataProvider = new ChatDataProvider(context); - - // Fake WebviewViewResolveContext - const fakeContext = {} as vscode.WebviewViewResolveContext; - - const dummyCancellationToken: vscode.CancellationToken = { - isCancellationRequested: false, - onCancellationRequested: callback => { - // No-op, as the token is never cancelled - return new vscode.Disposable(() => {}); - }, - }; - // Use CancellationToken.None - // const cancellationToken = vscode.CancellationToken.None; - - // Fake WebviewView object - const fakeWebviewView = { - webview: panel.webview, - onDidDispose: panel.onDidDispose, - } as vscode.WebviewView; - - // Call resolveWebviewView with all required arguments - chatDataProvider.resolveWebviewView(fakeWebviewView, fakeContext, dummyCancellationToken); - - // Ensure to dispose of the panel when not needed - panel.onDidDispose(() => { - // Clean up resources or perform any necessary actions when the panel is disposed - }); - }); - - context.subscriptions.push(renderChatCommand); - /** - * ****************************************************************************************************************************************** - */ - await ZenExtension.activate(context, lsClient); context.subscriptions.push( diff --git a/src/services/ZenExtension.ts b/src/services/ZenExtension.ts index 68e5f6eb..39f99db0 100644 --- a/src/services/ZenExtension.ts +++ b/src/services/ZenExtension.ts @@ -44,7 +44,7 @@ import { toggleCommands } from '../utils/global'; import { PanelDataProvider } from '../views/panel/panelView/PanelDataProvider'; import { ComponentDataProvider } from '../views/activityBar/componentView/ComponentDataProvider'; import { registerComponentCommands } from '../commands/components/registry'; - +import { registerChatCommands } from '../commands/chat/registry'; export interface IServerInfo { name: string; module: string; @@ -75,6 +75,7 @@ export class ZenExtension { registerComponentCommands, registerPipelineCommands, registerSecretsCommands, + registerChatCommands, ]; /** diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index a3b6e114..e4471766 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -41,6 +41,11 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { }); } + private _handleRegisterApiKey(): void { + vscode.window.showInformationMessage('Registering LLM API Key'); + vscode.commands.executeCommand('zenml.registerLLMAPIKey'); + } + private _getHtmlForWebview(webview: vscode.Webview): string { const nonce = getNonce(); @@ -125,11 +130,6 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { `; } - private _handleRegisterApiKey(): void { - vscode.window.showInformationMessage('Registering LLM API Key'); - vscode.commands.executeCommand('zenml.registerLLMAPIKey'); - } - dispose(): void { this._disposables.forEach(disposable => disposable.dispose()); } diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index ac576b57..f152279e 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -11,24 +11,20 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. import * as vscode from 'vscode'; -import * as fs from 'fs'; -import { marked } from 'marked'; -import { ChatService } from './chatService'; -import { ChatMessage, TreeItem } from '../../types/ChatTypes'; -import { PipelineDataProvider } from '../activityBar'; +import { ChatMessage } from '../../types/ChatTypes'; +import { getChatResponse, initializeTokenJS } from './utils'; +import { renderChatLog, getWebviewContent } from './chatRenderer'; +import { handleWebviewMessage } from './chatMessageHandler'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; - private messages: ChatMessage[] = []; // Array to store chat messages - private chatService: ChatService; + private messages: ChatMessage[] = []; + private streamingMessage: ChatMessage | null = null; constructor(private readonly context: vscode.ExtensionContext) { - this.chatService = ChatService.getInstance(this.context); + initializeTokenJS(context); } - /** - * Called when the webview is resolved. Initializes the webview content and sets up the message handling - */ resolveWebviewView( webviewView: vscode.WebviewView, context: vscode.WebviewViewResolveContext, @@ -38,217 +34,42 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.configureWebViewOptions(webviewView.webview); this.loadWebviewContent(); - // Handle messages received from the webview webviewView.webview.onDidReceiveMessage(async message => { - await this.handleWebviewMessage(message); + await handleWebviewMessage(message, this); }); } - /** - * Configure the webview to allow scripts to run - */ private configureWebViewOptions(webview: vscode.Webview) { webview.options = { enableScripts: true, }; } - /** - * Handle incoming messages from the webview. - */ - private async handleWebviewMessage(message: any) { - if (message.command === 'sendMessage' && message.text) { - await this.addMessage(message.text, message.context); - } - - if (message.command === 'clearChat') { - await this.clearChatLog(); - } - } - - /** - * Generate the webview HTML content, including the chat log and the input elements. - */ - private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri): string { - // Path to HTML file - const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); - let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); - - // Webview URIs for CSS and JS - const cssUri = webview.asWebviewUri( - vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css') - ); - const jsUri = webview.asWebviewUri( - vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') - ); - - // Chat log HTML - const chatLogHtml = this.renderChatLog(); - let treeItemHtml = this.getTreeHtml(); - - // Replace placeholders in the HTML with actual values - html = html.replace('${cssUri}', cssUri.toString()); - html = html.replace('${jsUri}', jsUri.toString()); - html = html.replace('${treeItemHtml}', treeItemHtml); - html = html.replace('${chatLogHtml}', chatLogHtml); - - return html; - } - - private clearChatLog(): void { - this.messages.length = 0; - this.updateWebviewContent(); - } - - private getPipelineData(): TreeItem[] { - let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; - let pipelineTreeItems: TreeItem[] = pipelineRuns.map((run, index) => { - let formattedStartTime = new Date(run.startTime).toLocaleString(); - let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; - let stringValue = `Pipeline run:${JSON.stringify(run)}`; - return { - name: run.name, - value: stringValue, - title: 'Includes all code, logs, and metadata for a specific pipeline run with message', - hidden: index > 9, - children: [ - { name: run.status }, - { name: run.stackName }, - { name: formattedStartTime }, - { name: formattedEndTime }, - { name: `${run.os} ${run.osVersion}` }, - { name: run.pythonVersion }, - ], - }; - }); - if (pipelineTreeItems.length > 9) { - pipelineTreeItems.push({ name: 'Expand' }); - } - return pipelineTreeItems; - } - - private getTreeData() { - let pipelineData = this.getPipelineData(); - let treeData: TreeItem[] = [ - { - name: 'Server', - value: 'serverContext', - title: 'Includes all server metadata with message', - }, - { - name: 'Environment', - value: 'environmentContext', - title: 'Includes all server metadata with message', - }, - { - name: 'Pipeline Runs', - value: 'pipelineContext', - title: 'Includes all code, logs, and metadata for pipeline runs with message', - children: pipelineData, - }, - { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, - { - name: 'Stack Components', - value: 'stackComponentsContext', - title: 'Includes all stack component metadata with message', - }, - ]; - return treeData; - } - - private convertTreeDataToHtml(treeData: TreeItem[], level = 0) { - let convertedTreeData = treeData.map(item => { - let iconSvg = - ''; - let childrenEl = '', - title = '', - hidden = ''; - - if (item.children) { - iconSvg = - ''; - childrenEl = `
${this.convertTreeDataToHtml(item.children, level + 1)}
`; - } - - if (item.title) { - title = item.title; - } - if (item.hidden) { - hidden = ' hidden'; - } - - let checkboxEl = - level < 2 ? `` : ''; - - if (item.name == 'Expand') { - hidden += ' expand'; - checkboxEl = ''; - } - - return `
-
-
- - ${iconSvg} - - ${item.name} - ${checkboxEl} -
- ${childrenEl} -
-
`; - }); - return convertedTreeData.join('\n'); - } - - private getTreeHtml(): string { - let treeData = this.getTreeData(); - return this.convertTreeDataToHtml(treeData); - } - - private updateWebviewContent() { - if (this._view) { - const chatLogHtml = this.renderChatLog(); - this._view.webview.postMessage({ - command: 'updateChatLog', - chatLogHtml: chatLogHtml, - }); - } - } - - /** - * Update the webview with the latest content, including the chat message. - */ private loadWebviewContent() { if (this._view) { - this._view.webview.html = this.getWebviewContent( + this._view.webview.html = getWebviewContent( this._view.webview, - this.context.extensionUri + this.context.extensionUri, + this.messages ); } } - /** - * Add a message to the chat log, get a response from AI provider and update the webview. - */ - private streamingMessage: ChatMessage | null = null; - async addMessage(message: string, context?: string[]) { this.messages.push({ role: 'user', content: message }); this.updateWebviewContent(); try { - const responseGenerator = this.chatService.getChatResponse(this.messages, context || []); + const responseGenerator = getChatResponse(this.messages, context || []); this.streamingMessage = { role: 'assistant', content: '' }; - // Send message to disable input this.sendMessageToWebview('disableInput'); for await (const partialResponse of responseGenerator) { for (const letter of partialResponse) { this.streamingMessage.content += letter; this.sendMessageToWebview(letter); - await new Promise(resolve => setTimeout(resolve, 1)); // Adjust delay as needed + await new Promise(resolve => setTimeout(resolve, 1)); } } @@ -263,82 +84,24 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } } - /** - * Render the chat log as HTML. - */ - private renderChatLog(): string { - const renderer = { - // @ts-ignore - code({ text, lang, escaped, isInline }) { - const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); - - if (isInline) { - return `${code}`; - } - - return '
' + code + '
\n'; - }, - }; - - // @ts-ignore - marked.use({ renderer }); - - return ( - this.messages - .filter(msg => msg['role'] !== 'system') - .reverse() - .map(message => { - let content = marked.parse(message.content); - if (message.role === 'user') { - return `
-

User

- ${content} -
`; - } else { - return `
-

ZenML Assistant

- ${content} -
`; - } - }) - .join('') + this.renderStreamingMessage() - ); + clearChatLog(): void { + this.messages.length = 0; + this.updateWebviewContent(); } - private renderStreamingMessage(): string { - if (!this.streamingMessage) { - return ''; + updateWebviewContent() { + if (this._view) { + const chatLogHtml = renderChatLog(this.messages, this.streamingMessage); + this._view.webview.postMessage({ + command: 'updateChatLog', + chatLogHtml: chatLogHtml, + }); } - - const renderer = { - // @ts-ignore - code({ text, lang, escaped, isInline }) { - const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); - - if (isInline) { - return `${code}`; - } - - return '
' + code + '
\n'; - }, - }; - - // @ts-ignore - marked.use({ renderer }); - - let content = marked.parse(this.streamingMessage.content); - return `
-

ZenML Assistant

- ${content} -
`; } - /** - * Send a message from AI provider back to the webview - */ - private sendMessageToWebview(text: string) { + sendMessageToWebview(text: string) { if (this._view) { this._view.webview.postMessage({ command: 'receiveMessage', text }); } } -} +} \ No newline at end of file diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts new file mode 100644 index 00000000..2bca2d02 --- /dev/null +++ b/src/views/chatView/chatMessageHandler.ts @@ -0,0 +1,23 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import { ChatDataProvider } from './ChatDataProvider'; + +export async function handleWebviewMessage(message: any, chatDataProvider: ChatDataProvider) { + if (message.command === 'sendMessage' && message.text) { + await chatDataProvider.addMessage(message.text, message.context); + } + + if (message.command === 'clearChat') { + await chatDataProvider.clearChatLog(); + } +} \ No newline at end of file diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts new file mode 100644 index 00000000..42060feb --- /dev/null +++ b/src/views/chatView/chatRenderer.ts @@ -0,0 +1,129 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import * as vscode from 'vscode'; +import * as fs from 'fs'; +import { marked } from 'marked'; +import { ChatMessage, TreeItem } from '../../types/ChatTypes'; +import { getTreeData } from './utils'; + +export function getWebviewContent( + webview: vscode.Webview, + extensionUri: vscode.Uri, + messages: ChatMessage[] +): string { + const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); + let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + + const cssUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css') + ); + const jsUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') + ); + + const chatLogHtml = renderChatLog(messages); + let treeItemHtml = getTreeHtml(); + + html = html.replace('${cssUri}', cssUri.toString()); + html = html.replace('${jsUri}', jsUri.toString()); + html = html.replace('${treeItemHtml}', treeItemHtml); + html = html.replace('${chatLogHtml}', chatLogHtml); + + return html; +} + +export function renderChatLog(messages: ChatMessage[], streamingMessage: ChatMessage | null = null): string { + const renderer = { + // @ts-ignore + code({ text, lang, escaped, isInline }) { + const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); + return isInline ? `${code}` : '
' + code + '
\n'; + }, + }; + + // @ts-ignore + marked.use({ renderer }); + + const renderedMessages = messages + .filter(msg => msg['role'] !== 'system') + .reverse() + .map(message => { + let content = marked.parse(message.content); + const roleClass = message.role === 'user' ? 'user' : 'assistant'; + const roleName = message.role === 'user' ? 'User' : 'ZenML Assistant'; + return `
+

${roleName}

+ ${content} +
`; + }) + .join(''); + + const streamingContent = streamingMessage + ? `
+

ZenML Assistant

+ ${marked.parse(streamingMessage.content)} +
` + : ''; + + return renderedMessages + streamingContent; +} + +function getTreeHtml(): string { + let treeData = getTreeData(); + return convertTreeDataToHtml(treeData); +} + +function convertTreeDataToHtml(treeData: TreeItem[], level = 0): string { + let convertedTreeData = treeData.map(item => { + let iconSvg = + ''; + let childrenEl = '', + title = '', + hidden = ''; + + if (item.children) { + iconSvg = + ''; + childrenEl = `
${convertTreeDataToHtml(item.children, level + 1)}
`; + } + + if (item.title) { + title = item.title; + } + if (item.hidden) { + hidden = ' hidden'; + } + + let checkboxEl = + level < 2 ? `` : ''; + + if (item.name === 'Expand') { + hidden += ' expand'; + checkboxEl = ''; + } + + return `
+
+
+ + ${iconSvg} + + ${item.name} + ${checkboxEl} +
+ ${childrenEl} +
+
`; + }); + return convertedTreeData.join('\n'); +} \ No newline at end of file diff --git a/src/views/chatView/chatService.ts b/src/views/chatView/chatService.ts deleted file mode 100644 index ad73b157..00000000 --- a/src/views/chatView/chatService.ts +++ /dev/null @@ -1,351 +0,0 @@ -// Copyright(c) ZenML GmbH 2024. All Rights Reserved. -// Licensed under the Apache License, Version 2.0(the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -// or implied.See the License for the specific language governing -// permissions and limitations under the License. -import * as vscode from 'vscode'; -import axios from 'axios'; -import { - PipelineDataProvider, - PipelineRunTreeItem, - PipelineTreeItem, - ServerDataProvider, - StackDataProvider, - StackComponentTreeItem, -} from '../activityBar'; -import { ComponentDataProvider } from '../activityBar/componentView/ComponentDataProvider'; -import { EnvironmentDataProvider } from '../activityBar/environmentView/EnvironmentDataProvider'; -import { LSClient } from '../../services/LSClient'; -import { DagArtifact, DagStep, PipelineRunDag } from '../../types/PipelineTypes'; -import { JsonObject } from '../panel/panelView/PanelTreeItem'; -import { ZenmlGlobalConfigResp } from '../../types/LSClientResponseTypes'; -import { TreeItem } from 'vscode'; -import { ChatMessage } from '../../types/ChatTypes'; - -export class ChatService { - private static instance: ChatService; - private tokenjs: any; - private context: vscode.ExtensionContext; - private providers: Record = { - 'claude-3-5-sonnet-20240620': 'anthropic', - 'claude-3-opus-20240229': 'anthropic', - 'gpt-4o': 'openai', - 'gemini-1.5-pro': 'gemini', - 'gemini-1.5-flash': 'gemini', - }; - - private constructor(context: vscode.ExtensionContext) { - this.context = context; - this.initialize(); - } - - public static getInstance(context: vscode.ExtensionContext): ChatService { - if (!ChatService.instance) { - ChatService.instance = new ChatService(context); - } - return ChatService.instance; - } - - private async initialize() { - try { - const module = await import('token.js'); - const { TokenJS } = module; - let provider = 'Gemini'; // this is the only provider available at the moment - const apiKey = await this.context.secrets.get(`zenml.${provider.toLowerCase()}.key`); - if (!apiKey) { - vscode.window.showErrorMessage('No Gemini API key found. Please register one'); - } - this.tokenjs = new TokenJS({ apiKey }); - } catch (error) { - console.error('Error loading TokenJS:', error); - vscode.window.showErrorMessage( - `Failed to initialize ChatService: ${error instanceof Error ? error.message : 'Unknown error'}` - ); - } - } - - public async *getChatResponse( - messages: ChatMessage[], - context: string[] - ): AsyncGenerator { - try { - if (context) { - messages = await this.addContext(messages, context); - } - - const completion = await this.tokenjs.chat.completions.create({ - streaming: true, - provider: this.providers[context[0]] ?? '', - model: context[0] ?? '', - messages: messages, - }); - - if (Symbol.asyncIterator in completion) { - for await (const part of completion) { - yield part.choices[0]?.delta?.content || ''; - } - } else if (completion.choices && completion.choices.length > 0) { - yield completion.choices[0].message.content || ''; - } else { - throw new Error('Unexpected response from API'); - } - } catch (error) { - console.error('Error with Gemini API:', error); - if (error instanceof Error) { - yield `Error: ${error.message}. Please check your API key and network connection.`; - } else { - yield 'Error: An unexpected error occurred while getting a response from Gemini.'; - } - } - } - - private async addContext( - messages: ChatMessage[], - requestedContext: any[] - ): Promise { - let systemMessage: ChatMessage = { role: 'system', content: 'Context: ' }; - for (let context of requestedContext) { - // TODO possibly create interface for context, change requestContext type (currently any[]) - switch (context) { - case 'serverContext': - systemMessage.content += this.getServerData(); - break; - case 'environmentContext': - systemMessage.content += await this.getEnvironmentData(); - break; - case 'pipelineContext': - systemMessage.content += this.getPipelineData(); - break; - case 'stackContext': - systemMessage.content += this.getStackData(); - break; - case 'stackComponentsContext': - systemMessage.content += this.getStackComponentData(); - break; - case 'logsContext': - systemMessage.content += this.getLogData(); - break; - default: - if (context.includes('Pipeline run:')) { - systemMessage.content += context; - context = JSON.parse(context.replace('Pipeline run:', '')); - let logs = await this.getPipelineRunLogs(context.id); - let nodeData = await this.getPipelineRunNodes('step'); - systemMessage.content += `Step Data: ${JSON.stringify(nodeData)}`; - systemMessage.content += `Logs: ${logs}`; - } - break; - } - } - messages.push(systemMessage); - return messages; - } - - /** - * - * @returns A parsed string containing the information of the server. - */ - private getServerData(): string { - let serverData = ServerDataProvider.getInstance().getCurrentStatus(); - return `Server Status Data:\n${JSON.stringify(serverData)}\n`; - } - - private getStackComponentData(): string { - let components = ComponentDataProvider.getInstance().items; - let componentData = components - .map((item: vscode.TreeItem) => { - if (item instanceof StackComponentTreeItem) { - let { name, type, flavor, id } = item.component; - let stackId = item.stackId; - let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; - let componentId = ` - Component ID: ${id}`; - return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; - } else { - return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; - } - }) - .join('\n'); - return `Stack Component Data:\n${componentData}\n`; - } - - private async getEnvironmentData(): Promise { - let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); - let contextString = environmentData - .map(item => `${item.label}: ${item.description || ''}`) - .join('\n'); - return `Environment Data:\n${contextString}\n`; - } - - private getPipelineData(): string { - //Check if this.items works instead - let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); - let contextString = pipelineData - .map((pipelineRun: PipelineTreeItem) => { - return ( - `Pipeline Run:\n` + - pipelineRun.children - ?.map((item: PipelineRunTreeItem) => { - return `${item.tooltip}`; - }) - .join('\n') + - `\n${pipelineRun.description}` - ); - }) - .join('\n'); - return `Pipeline Data:\n${contextString}\n`; - } - - private getRecentPipelineRunData() { - let pipelineData = PipelineDataProvider.getInstance().getPipelineData()[0]; - let contextString = JSON.stringify(pipelineData); - return `Pipeline Data:\n${contextString}\n`; - } - - private getStackData(): string { - let stackData = StackDataProvider.getInstance().items; - let contextString = stackData - .map(item => { - let stackItem = item as TreeItem & { isActive: boolean; id: string }; - return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; - }) - .join('\n'); - return `Stack Data:\n${contextString}\n`; - } - - private async getPipelineRunNodes(type: string) { - //change back to just step or add artifact command - let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); - let lsClient = LSClient.getInstance(); - let dagData = await Promise.all( - pipelineData.map(async (node: PipelineTreeItem) => { - let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [ - node.id, - ]); - return dag; - }) - ); - - let stepData = await Promise.all( - dagData.map(async (dag: PipelineRunDag) => { - let filteredNodes = await Promise.all( - dag.nodes.map(async (node: DagArtifact | DagStep) => { - if (type === 'all' || node.type === type) { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [ - node.id, - ]); - } - return null; - }) - ); - return filteredNodes.filter(value => value !== null); - }) - ); - return stepData; - } - - private async getLogData() { - let lsClient = LSClient.getInstance(); - - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; - - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } - - let pipelineRunSteps = await this.getPipelineRunNodes('step'); - - let logs = await Promise.all( - pipelineRunSteps[0].map(async step => { - if (step && step.id) { - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${step.id}`, error); - } - } else { - console.warn('Encountered a null or invalid step.'); - } - }) - ); - return logs; - } - - private async getPipelineRunLogs(id: string) { - let lsClient = LSClient.getInstance(); - - let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); - - let stepData = await Promise.all( - dagData.nodes.map(async (node: DagArtifact | DagStep) => { - if (node.type === 'step') { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - }) - ); - - stepData = stepData.filter(value => value !== null); - - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; - - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } - - let logs = await Promise.all( - stepData.map(async step => { - if (step && typeof step === 'object' && 'id' in step) { - let validStep = step as JsonObject; - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } - return null; // returns null if step is invalid - }) - ); - logs = logs.filter(log => log !== null); // Filters out possible null logs. - return logs; - } - - private async getMetadata() { - let lsClient = LSClient.getInstance(); - - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; - - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } - - // Grabs a list of metadata IDs - // Eventually should be reformatted to grab individual metadata based on the metadata ID - let metadata = await axios.get(`${dashboardUrl}/api/v1/run-metadata`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - } -} diff --git a/src/views/chatView/utils.ts b/src/views/chatView/utils.ts new file mode 100644 index 00000000..0ec130dd --- /dev/null +++ b/src/views/chatView/utils.ts @@ -0,0 +1,346 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import * as vscode from 'vscode'; +import axios from 'axios'; +import { ChatMessage, TreeItem } from '../../types/ChatTypes'; +import { PipelineDataProvider, ServerDataProvider, StackDataProvider } from '../activityBar'; +import { ComponentDataProvider } from '../activityBar/componentView/ComponentDataProvider'; +import { EnvironmentDataProvider } from '../activityBar/environmentView/EnvironmentDataProvider'; +import { LSClient } from '../../services/LSClient'; +import { ZenmlGlobalConfigResp } from '../../types/LSClientResponseTypes'; +import { PipelineRunDag, DagArtifact, DagStep } from '../../types/PipelineTypes'; +import { JsonObject } from '../panel/panelView/PanelTreeItem'; +import { StackComponentTreeItem } from '../activityBar'; + +const providers: Record = { + 'claude-3-5-sonnet-20240620': 'anthropic', + 'claude-3-opus-20240229': 'anthropic', + 'gpt-4o': 'openai', + 'gemini-1.5-pro': 'gemini', + 'gemini-1.5-flash': 'gemini', +}; + +let tokenjs: any; + +export async function initializeTokenJS(context: vscode.ExtensionContext) { + try { + const module = await import('token.js'); + const { TokenJS } = module; + let provider = 'Gemini'; + const apiKey = await context.secrets.get(`zenml.${provider.toLowerCase()}.key`); + if (!apiKey) { + vscode.window.showErrorMessage('No Gemini API key found. Please register one'); + } + tokenjs = new TokenJS({ apiKey }); + } catch (error) { + console.error('Error loading TokenJS:', error); + vscode.window.showErrorMessage( + `Failed to initialize ChatService: ${error instanceof Error ? error.message : 'Unknown error'}` + ); + } +} + +export async function* getChatResponse( + messages: ChatMessage[], + context: string[] +): AsyncGenerator { + try { + if (context) { + messages = await addContext(messages, context); + } + + const completion = await tokenjs.chat.completions.create({ + streaming: true, + provider: providers[context[0]] ?? '', + model: context[0] ?? '', + messages: messages, + }); + + if (Symbol.asyncIterator in completion) { + for await (const part of completion) { + yield part.choices[0]?.delta?.content || ''; + } + } else if (completion.choices && completion.choices.length > 0) { + yield completion.choices[0].message.content || ''; + } else { + throw new Error('Unexpected response from API'); + } + } catch (error) { + console.error('Error with Gemini API:', error); + if (error instanceof Error) { + yield `Error: ${error.message}. Please check your API key and network connection.`; + } else { + yield 'Error: An unexpected error occurred while getting a response from Gemini.'; + } + } +} + +async function addContext(messages: ChatMessage[], requestedContext: any[]): Promise { + let systemMessage: ChatMessage = { role: 'system', content: 'Context: ' }; + for (let context of requestedContext) { + switch (context) { + case 'serverContext': + systemMessage.content += getServerData(); + break; + case 'environmentContext': + systemMessage.content += await getEnvironmentData(); + break; + case 'pipelineContext': + systemMessage.content += getPipelineData(); + break; + case 'stackContext': + systemMessage.content += getStackData(); + break; + case 'stackComponentsContext': + systemMessage.content += getStackComponentData(); + break; + case 'logsContext': + systemMessage.content += await getLogData(); + break; + default: + if (context.includes('Pipeline run:')) { + systemMessage.content += context; + context = JSON.parse(context.replace('Pipeline run:', '')); + let logs = await getPipelineRunLogs(context.id); + let nodeData = await getPipelineRunNodes('step'); + systemMessage.content += `Step Data: ${JSON.stringify(nodeData)}`; + systemMessage.content += `Logs: ${logs}`; + } + break; + } + } + messages.push(systemMessage); + return messages; +} + +function getServerData(): string { + let serverData = ServerDataProvider.getInstance().getCurrentStatus(); + return `Server Status Data:\n${JSON.stringify(serverData)}\n`; +} + +function getStackComponentData(): string { + let components = ComponentDataProvider.getInstance().items; + let componentData = components + .map((item: vscode.TreeItem) => { + if (item instanceof StackComponentTreeItem) { + let { name, type, flavor, id } = item.component; + let stackId = item.stackId; + let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; + let componentId = ` - Component ID: ${id}`; + return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; + } else { + return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; + } + }) + .join('\n'); + return `Stack Component Data:\n${componentData}\n`; +} + +async function getEnvironmentData(): Promise { + let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); + let contextString = environmentData + .map(item => `${item.label}: ${item.description || ''}`) + .join('\n'); + return `Environment Data:\n${contextString}\n`; +} + +function getStackData(): string { + let stackData = StackDataProvider.getInstance().items; + let contextString = stackData + .map(item => { + let stackItem = item as vscode.TreeItem & { isActive: boolean; id: string }; + return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; + }) + .join('\n'); + return `Stack Data:\n${contextString}\n`; +} + +async function getPipelineRunNodes(type: string) { + let pipelineData = PipelineDataProvider.getInstance().getPipelineData(); + let lsClient = LSClient.getInstance(); + let dagData = await Promise.all( + pipelineData.map(async node => { + let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [ + node.id, + ]); + return dag; + }) + ); + + let stepData = await Promise.all( + dagData.map(async (dag: PipelineRunDag) => { + let filteredNodes = await Promise.all( + dag.nodes.map(async (node: DagArtifact | DagStep) => { + if (type === 'all' || node.type === type) { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [ + node.id, + ]); + } + return null; + }) + ); + return filteredNodes.filter(value => value !== null); + }) + ); + return stepData; +} + +async function getLogData() { + let lsClient = LSClient.getInstance(); + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url; + + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } + + let pipelineRunSteps = await getPipelineRunNodes('step'); + + let logs = await Promise.all( + pipelineRunSteps[0].map(async step => { + if (step && step.id) { + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); + } + } else { + console.warn('Encountered a null or invalid step.'); + } + }) + ); + return logs; +} + +async function getPipelineRunLogs(id: string) { + let lsClient = LSClient.getInstance(); + + let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); + + let stepData = await Promise.all( + dagData.nodes.map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + ); + + stepData = stepData.filter(value => value !== null); + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url; + + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } + + let logs = await Promise.all( + stepData.map(async step => { + if (step && typeof step === 'object' && 'id' in step) { + let validStep = step as JsonObject; + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } + return null; + }) + ); + logs = logs.filter(log => log !== null); + return logs; +} + +function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { + let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; + let contextString = ''; + let treeItems: TreeItem[] = []; + + pipelineRuns.forEach((run, index) => { + let formattedStartTime = new Date(run.startTime).toLocaleString(); + let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; + + contextString += `Pipeline Run:\n` + + `Name: ${run.name}\n` + + `Status: ${run.status}\n` + + `Stack Name: ${run.stackName}\n` + + `Start Time: ${formattedStartTime}\n` + + `End Time: ${formattedEndTime}\n` + + `OS: ${run.os} ${run.osVersion}\n` + + `Python Version: ${run.pythonVersion}\n\n`; + + let stringValue = `Pipeline run:${JSON.stringify(run)}`; + let treeItem: TreeItem = { + name: run.name, + value: stringValue, + title: 'Includes all code, logs, and metadata for a specific pipeline run with message', + hidden: index > 9, + children: [ + { name: run.status }, + { name: run.stackName }, + { name: formattedStartTime }, + { name: formattedEndTime }, + { name: `${run.os} ${run.osVersion}` }, + { name: run.pythonVersion }, + ], + }; + treeItems.push(treeItem); + }); + + if (treeItems.length > 9) { + treeItems.push({ name: 'Expand' }); + } + + return { contextString, treeItems }; +} + +export function getTreeData(): TreeItem[] { + let { contextString, treeItems } = getPipelineData(); + let treeData: TreeItem[] = [ + { + name: 'Server', + value: 'serverContext', + title: 'Includes all server metadata with message', + }, + { + name: 'Environment', + value: 'environmentContext', + title: 'Includes all server metadata with message', + }, + { + name: 'Pipeline Runs', + value: 'pipelineContext', + title: 'Includes all code, logs, and metadata for pipeline runs with message', + children: treeItems, + }, + { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, + { + name: 'Stack Components', + value: 'stackComponentsContext', + title: 'Includes all stack component metadata with message', + }, + ]; + return treeData; +} From bccfe21f4e8487b0cbdec260fe036ef4befe0b83 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Sun, 8 Sep 2024 22:43:09 -0400 Subject: [PATCH 066/138] feat: add formatting directions for model via system role --- src/views/chatView/utils.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/views/chatView/utils.ts b/src/views/chatView/utils.ts index 0ec130dd..a378ce30 100644 --- a/src/views/chatView/utils.ts +++ b/src/views/chatView/utils.ts @@ -55,6 +55,12 @@ export async function* getChatResponse( context: string[] ): AsyncGenerator { try { + const systemMessage: ChatMessage = { + role: 'system', + content: + "Format the response to look nice, create

between sections. Obvious JSON, objects, or other code should be in a code block." + }; + messages.push(systemMessage); if (context) { messages = await addContext(messages, context); } From 8ddfedae5c49146dbb832c5cec8b7dd0d1e2c18c Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Mon, 9 Sep 2024 14:18:48 -0400 Subject: [PATCH 067/138] feat(chat): rearrange webview to resemble ChatGPT layout - Moved input bar to the bottom of the chat interface. - Adjusted message order so new messages appear below previous ones. --- resources/chat-view/chat.html | 138 +++++++++++++++-------------- resources/chat-view/chat.js | 6 +- src/views/chatView/chatRenderer.ts | 1 - 3 files changed, 73 insertions(+), 72 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index e4a0b3e6..7342565f 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -146,7 +146,8 @@ position: absolute; left: 0; right: 0; - margin-top: 0.25rem; + bottom: 100%; + margin-bottom: 0.25rem; z-index: 10; } #chatMessages h1, #chatMessages h2, #chatMessages h3, #chatMessages h4, #chatMessages h5, #chatMessages h6 { @@ -209,88 +210,89 @@

ZenML Chat

- -
-
- - - - -
- -
- - -
-
- + +
+ +
+ + +
+
+ +
-
-
-
-
-
-
-
- +
+
+
+
+
+
+ +
+
- -
-
-
-
- +
+
+
+ +
+
- -
-
-
-
- +
+
+
+ +
-
-
-
-
- +
+
+
+ +
-
${chatLogHtml}
diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 793460a4..eb4caf7b 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -97,7 +97,7 @@ if (role === 'assistant') { messageDiv = chatMessages.querySelector('div[data-role="assistant"]:last-child') || - chatMessages.firstElementChild; + chatMessages.lastElementChild; if (!messageDiv || messageDiv.getAttribute('data-role') !== 'assistant') { messageDiv = document.createElement('div'); @@ -105,7 +105,7 @@ messageDiv.setAttribute('data-role', 'assistant'); messageDiv.innerHTML = `

ZenML Assistant

`; - chatMessages.insertBefore(messageDiv, chatMessages.firstChild); + chatMessages.appendChild(messageDiv, chatMessages.firstChild); currentAssistantMessage = ''; } @@ -114,7 +114,7 @@ requestAnimationFrame(() => { contentDiv.innerHTML = marked.parse(currentAssistantMessage); - chatMessages.scrollTop = 0; + chatMessages.scrollTop = chatMessages.scrollHeight; }); } } diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 42060feb..7d7405f8 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -56,7 +56,6 @@ export function renderChatLog(messages: ChatMessage[], streamingMessage: ChatMes const renderedMessages = messages .filter(msg => msg['role'] !== 'system') - .reverse() .map(message => { let content = marked.parse(message.content); const roleClass = message.role === 'user' ? 'user' : 'assistant'; From c93106cd30bc23ee8bc25de0d27de3ead241439f Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Mon, 9 Sep 2024 14:05:45 -0700 Subject: [PATCH 068/138] perf: Added priming and additional context for more accurate and faster responses --- src/views/chatView/utils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/chatView/utils.ts b/src/views/chatView/utils.ts index a378ce30..afd19c2c 100644 --- a/src/views/chatView/utils.ts +++ b/src/views/chatView/utils.ts @@ -58,7 +58,7 @@ export async function* getChatResponse( const systemMessage: ChatMessage = { role: 'system', content: - "Format the response to look nice, create

between sections. Obvious JSON, objects, or other code should be in a code block." + "Format every response to look nice. Add

between sections for easier readability, and put code in code blocks. You are an assistant that summarizes information, problem solves, or optimizes code." }; messages.push(systemMessage); if (context) { @@ -103,6 +103,7 @@ async function addContext(messages: ChatMessage[], requestedContext: any[]): Pro break; case 'pipelineContext': systemMessage.content += getPipelineData(); + systemMessage.content += "\n A pipeline is a series of steps in a machine learning workflow."; break; case 'stackContext': systemMessage.content += getStackData(); From da1fd58c8995e7e636fdb290c4eb5c713bf056f0 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Mon, 9 Sep 2024 17:35:15 -0400 Subject: [PATCH 069/138] feat(ui): swap positions of Open Chat and Register/Change AI API Key buttons --- package.json | 8 ++++---- src/views/activityBar/APIView/APIWebviewViewProvider.ts | 8 +++++--- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 3f28151f..8e79a1c8 100644 --- a/package.json +++ b/package.json @@ -282,7 +282,7 @@ }, { "command": "zenml.openChat", - "title": "Open Chat. *Using this feature will incure charges as normal with your LLM provider.", + "title": "Open ZenML Chat View", "icon": "$(comment-discussion)", "category": "ZenML Chat" }, @@ -306,8 +306,8 @@ }, { "command": "zenml.registerLLMAPIKey", - "title": "Register LLM API Key", - "icon": "$(add)", + "title": "Register/Change LLM API Key", + "icon": "$(comment-discussion)", "category": "ZenML Secrets" } ], @@ -431,7 +431,7 @@ }, { "when": "view == zenmlAPIView", - "command": "zenml.openChat", + "command": "zenml.registerLLMAPIKey", "group": "navigation" } ], diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index e4471766..0438ec6e 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -42,8 +42,9 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { } private _handleRegisterApiKey(): void { - vscode.window.showInformationMessage('Registering LLM API Key'); - vscode.commands.executeCommand('zenml.registerLLMAPIKey'); + // vscode.window.showInformationMessage('Registering LLM API Key'); + // vscode.commands.executeCommand('zenml.registerLLMAPIKey'); + vscode.commands.executeCommand('zenml.openChat'); } private _getHtmlForWebview(webview: vscode.Webview): string { @@ -117,7 +118,8 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { - + +

Using the chat feature will incur charges from your model provider as normal.

- \ No newline at end of file diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 7749c1db..3938491d 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -66,7 +66,16 @@ } }); }); - } + + const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; + let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; + + if (isContextPipelineRunsDisplayed === true) { + pipelineRunsDropdown.classList.add('open'); + } else { + pipelineRunsDropdown.classList.remove('open'); + } + } function sendMessage(event) { event.preventDefault(); @@ -318,4 +327,173 @@ vscode.postMessage({ command: 'nextPage' }); }); } + + + + +document.addEventListener('DOMContentLoaded', (event) => { + const contextButton = document.getElementById('contextButton'); + const optionsDropdown = document.getElementById('optionsDropdown'); + + let isContextDisplayed = localStorage.getItem('displayContext') === 'true'; + if (isContextDisplayed) { + optionsDropdown.classList.remove('hidden'); + localStorage.setItem('displayContext', "true"); + } else { + optionsDropdown.classList.add('hidden'); + localStorage.setItem('displayContext', "false"); + } + + contextButton.addEventListener('click', (event) => { + event.stopPropagation(); + optionsDropdown.classList.toggle('hidden'); + isContextDisplayed = !isContextDisplayed; + localStorage.setItem('displayContext', String(isContextDisplayed)); + }); + + document.addEventListener('click', (event) => { + if (!contextButton.contains(event.target) && !optionsDropdown.contains(event.target)) { + optionsDropdown.classList.add('hidden'); + localStorage.setItem('displayContext', "false"); + } + }); + + const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; + let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; + if (isContextPipelineRunsDisplayed === true && isContextDisplayed === true) { + pipelineRunsDropdown.classList.add('open'); + } else { + pipelineRunsDropdown.classList.remove('open'); + } + + const treeItemsWithChildren = Array.from(document.querySelectorAll('div.tree-item-wrapper')) + .filter(treeItemsWithChildren => treeItemsWithChildren.querySelector('div.tree-item-children') !== null); + + treeItemsWithChildren.forEach(wrapper => { + wrapper.querySelector('div.tree-item-content').addEventListener('click', function(event) { + const checkboxEl = this.querySelector('input[type="checkbox"]'); + const childrenEl = this.parentNode.querySelector('div.tree-item-children'); + const chevronEl = this.querySelector('span.tree-item-icon'); + const prevPageButton = document.getElementById('prevPage'); + const nextPageButton = document.getElementById('nextPage'); + + if (event.target !== checkboxEl && event.target !== nextPageButton && event.target !== prevPageButton) { + event.stopPropagation(); + childrenEl.classList.toggle('open'); + isContextPipelineRunsDisplayed = !isContextPipelineRunsDisplayed; + localStorage.setItem('displayContextPipelineRuns', String(isContextPipelineRunsDisplayed)); + + if (childrenEl.classList.contains('open')) { + chevronEl.innerHTML = ''; + } else { + chevronEl.innerHTML = ''; + } + } + }); + }); + const textarea = document.getElementById('messageInput'); + const sendButton = document.getElementById('sendMessage'); + const loader = document.getElementById('loader'); + let isInputDisabled = false; + + function showLoader() { + loader.classList.add('loader'); + } + + function hideLoader() { + loader.classList.remove('loader'); + } + + function disableInput() { + isInputDisabled = true; + sendButton.disabled = true; + } + + function enableInput() { + isInputDisabled = false; + sendButton.disabled = false; + } + + // Listen for messages from the extension + window.addEventListener('message', event => { + const message = event.data; + + switch (message.text) { + case 'disableInput': + disableInput(); + break; + case 'enableInput': + enableInput(); + break; + default: + break; + } + + if (message.command === 'hideLoader') { + hideLoader(); + } + }); + + textarea.addEventListener('keydown', (e) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + + if (!isInputDisabled) { + const form = document.getElementById('chatForm'); + const event = new SubmitEvent('submit', { + bubbles: true, + cancelable: true, + }); + form.dispatchEvent(event); + showLoader(); + } + } + }); + + textarea.addEventListener('keypress', (e) => { + if (e.key === 'Enter' && e.shiftKey) { + // Insert a new line when Shift+Enter is pressed + e.preventDefault(); + textarea.value += '\n'; + textarea.scrollTop = textarea.scrollHeight; + } + }); + + // Restore dropdown state + restoreState(); +}); + +// Function to restore the saved state +function restoreState() { + const selectedProvider = localStorage.getItem('selectedProvider'); + const selectedModel = localStorage.getItem('selectedModel'); + const selectedContexts = JSON.parse(localStorage.getItem('selectedContexts')) || []; + + if (selectedProvider) { + document.querySelector('#provider-dropdown').value = selectedProvider; + } + if (selectedModel) { + document.querySelector('#model-dropdown').value = selectedModel; + } + + const allCheckboxes = document.querySelectorAll('#tree-view input[type="checkbox"]'); + + selectedContexts.forEach(savedValue => { + allCheckboxes.forEach(checkbox => { + if (checkbox.value === savedValue) { + checkbox.checked = true; + } + }); + }); + + const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; + let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; + + if (isContextPipelineRunsDisplayed === true) { + pipelineRunsDropdown.classList.add('open'); + } else { + pipelineRunsDropdown.classList.remove('open'); + } + } + })(); diff --git a/resources/chat-view/context.js b/resources/chat-view/context.js deleted file mode 100644 index b927168b..00000000 --- a/resources/chat-view/context.js +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright(c) ZenML GmbH 2024. All Rights Reserved. -// Licensed under the Apache License, Version 2.0(the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -// or implied.See the License for the specific language governing -// permissions and limitations under the License. -const treeData = [ - {name: 'Server'}, - {name: 'Environment'}, - { - name: 'Pipeline', - children : [ - { - name: 'training', - children: [ - {name: 'id: 85439137'}, - {name: 'completed: success'} - ] - } - ] - }, - {name: 'Stack'}, - {name: 'Stack Components'} -]; - -function createTreeView(items, parentEl, level = 0) { - items.forEach(item => { - const itemEl = document.createElement('div'); - itemEl.className = 'tree-item'; - itemEl.style.paddingLeft = `${level * 12}px`; - - const wrapperEl = document.createElement('div'); - wrapperEl.className = 'tree-item-wrapper'; - - const contentEl = document.createElement('div'); - contentEl.className = 'tree-item-content'; - - const checkboxEl = document.createElement('input'); - checkboxEl.type = 'checkbox'; - checkboxEl.className = 'tree-item-checkbox'; - - const chevronEl = document.createElement('span'); - chevronEl.className = 'tree-item-icon'; - - const isFolder = item.children && item.children.length > 0; - - chevronEl.innerHTML = isFolder - ? '' - : ''; - - const nameEl = document.createElement('span'); - nameEl.className = 'tree-item-name'; - nameEl.textContent = item.name; - - if (level < 2) { - contentEl.appendChild(checkboxEl); - } - contentEl.appendChild(chevronEl); - contentEl.appendChild(nameEl); - wrapperEl.appendChild(contentEl); - - if (isFolder) { - const childrenEl = document.createElement('div'); - childrenEl.className = 'tree-item-children'; - createTreeView(item.children, childrenEl, level + 1); - - contentEl.addEventListener('click', (e) => { - if (e.target !== checkboxEl && !childrenEl.contains(e.target)) { - e.stopPropagation(); - childrenEl.classList.toggle('open'); - if (childrenEl.classList.contains('open')) { - chevronEl.innerHTML = ''; - } else { - chevronEl.innerHTML = ''; - } - } - }); - - wrapperEl.appendChild(childrenEl); - } - - checkboxEl.addEventListener('click', (e) => { - e.stopPropagation(); - }); - - checkboxEl.addEventListener('change', (e) => { - if (e.target.checked) { - itemEl.style.backgroundColor = '#2a2d2e'; - } else { - itemEl.style.backgroundColor = ''; - } - }); - - itemEl.appendChild(wrapperEl); - parentEl.appendChild(itemEl); - }); -} - -const treeViewEl = document.getElementById('tree-view'); -createTreeView(treeData, treeViewEl); \ No newline at end of file diff --git a/src/views/activityBar/common/PaginatedDataProvider.ts b/src/views/activityBar/common/PaginatedDataProvider.ts index 1d5a3bf5..041e1b71 100644 --- a/src/views/activityBar/common/PaginatedDataProvider.ts +++ b/src/views/activityBar/common/PaginatedDataProvider.ts @@ -32,7 +32,7 @@ export class PaginatedDataProvider implements TreeDataProvider { totalPages: number; } = { currentPage: 1, - itemsPerPage: 3, + itemsPerPage: 10, totalItems: 0, totalPages: 0, }; diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 6a130478..7c504611 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -12,11 +12,11 @@ // permissions and limitations under the License. import * as vscode from 'vscode'; import { ChatMessage } from '../../types/ChatTypes'; -import { getChatResponse, initializeTokenJS } from './utils'; import { renderChatLog, getWebviewContent } from './chatRenderer'; import { handleWebviewMessage } from './chatMessageHandler'; import { EventBus } from '../../services/EventBus'; import { LSP_ZENML_STACK_CHANGED } from '../../utils/constants'; +import { getChatResponse, initializeTokenJS } from './utils/TokenUtils'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index b9492553..c16dd920 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -14,7 +14,7 @@ import * as vscode from 'vscode'; import * as fs from 'fs'; import { marked } from 'marked'; import { ChatMessage, TreeItem } from '../../types/ChatTypes'; -import { getTreeData } from './utils'; +import { getTreeData } from './utils/PipelineUtils'; export function getWebviewContent( webview: vscode.Webview, diff --git a/src/views/chatView/utils.ts b/src/views/chatView/utils.ts deleted file mode 100644 index 53b406a4..00000000 --- a/src/views/chatView/utils.ts +++ /dev/null @@ -1,410 +0,0 @@ -// Copyright(c) ZenML GmbH 2024. All Rights Reserved. -// Licensed under the Apache License, Version 2.0(the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at: -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express -// or implied.See the License for the specific language governing -// permissions and limitations under the License. -import * as vscode from 'vscode'; -import axios from 'axios'; -import { ChatMessage, TreeItem } from '../../types/ChatTypes'; -import { PipelineDataProvider, ServerDataProvider, StackDataProvider } from '../activityBar'; -import { ComponentDataProvider } from '../activityBar/componentView/ComponentDataProvider'; -import { EnvironmentDataProvider } from '../activityBar/environmentView/EnvironmentDataProvider'; -import { LSClient } from '../../services/LSClient'; -import { ZenmlGlobalConfigResp } from '../../types/LSClientResponseTypes'; -import { PipelineRunDag, DagArtifact, DagStep } from '../../types/PipelineTypes'; -import { JsonObject } from '../panel/panelView/PanelTreeItem'; -import { StackComponentTreeItem } from '../activityBar'; - -let tokenjs: any; - -export async function initializeTokenJS(context: vscode.ExtensionContext, provider: string) { - const apiKeySecret = `zenml.${provider.toLowerCase()}.key`; - const apiKey = await context.secrets.get(apiKeySecret); - - if (!apiKey) { - throw new Error( - `API key for ${provider} not found. Please set the ${apiKeySecret} in VS Code secrets.` - ); - } - const config: Record = {}; - config['apiKey'] = apiKey; - const module = await import('token.js'); - const { TokenJS } = module; - tokenjs = new TokenJS(config); -} - -export async function* getChatResponse( - messages: ChatMessage[], - context: string[], - provider: string, - model: string -): AsyncGenerator { - // testing out formatting - const alans = - 'Format every response to look nice. Add

between sections for easier readability, and put code in code blocks. You are an assistant that summarizes information, problem solves, or optimizes code.'; - const wills = - 'Format the response using full markdown, create

between sections and newline characters and use indendation. Obvious JSON, objects, or other code should be in a code block or blockquotes (and make sure to add newline characters when appropriate). Use ordered and unordered lists as much as possible (use

before lists).'; - const combined = `Format every response to look nice using full markdown. Add

between sections and use newline characters with indentation where appropriate. Obvious JSON, objects, or other code should be in code blocks or blockquotes, and make sure to add newline characters when needed. Use ordered and unordered lists as much as possible (with

before lists). You are an assistant that summarizes information, problem solves, or optimizes code.`; - const optimized = `Format responses using full markdown for readability. Separate sections with

and use newline characters when needed for clarity. Present all code, JSON, and data structures in code blocks or blockquotes. Use ordered and unordered lists whenever applicable, with spacing before lists. Your role is to summarize information, solve problems, and optimize code.`; - const revised = `Format every response using full markdown. Add

between sections for better readability. Use newline characters and indentation where appropriate. Ensure that all JSON, objects, and other code are enclosed in code blocks, and blockquotes when necessary. Before ordered or unordered lists, insert

for clarity. You are an assistant that summarizes information, solves problems, or optimizes code, ensuring clarity and structure in all responses.`; - const template = ` - You are a ZenML assistant that summarizes users' ZenML information, problem solves users' ZenML problems, or optimizes users' code in their ZenML pipeline runs. - - Structure (with markdown) the output like this: - -
-

Category 1

-
- Key 1-1 - value 1-1 -
- Key 1-2 - value 1-2 -

-
-

Category 2

-
- Key 2-1 - value 2-1 -
- Key 2-2 - value 2-2 - - To bold words, use tags. Do not ever use asterisks for formatting. - To write code blocks, use tags. - if there's an explanation at the end, add it like: - -

-
-

Summary

-
-
- Explanation -
  • point 1
  • -
  • point 2
  • - `; - if (!tokenjs) { - throw new Error('TokenJS not initialized'); - } - - console.log(`getChatResponse called with provider: ${provider}, model: ${model}`); - - const fullMessages = [ - { role: 'system', content: template }, - { role: 'user', content: await addContext(context) }, - ...messages, - ]; - try { - const stream = await tokenjs.chat.completions.create({ - stream: true, - provider: provider.toLowerCase(), - model: model, - messages: fullMessages.map(msg => ({ - role: msg.role as 'system' | 'user' | 'assistant', - content: msg.content, - })), - }); - - for await (const part of stream) { - if (part.choices[0]?.delta?.content) { - yield part.choices[0].delta.content; - } - } - } catch (error: any) { - console.error('Error in getChatResponse:', error); - throw new Error(`Error with ${provider} API: ${error.message}`); - } -} - -export async function addContext(requestedContext: string[]): Promise { - let systemMessage = 'Context: '; - for (let context of requestedContext) { - switch (context) { - case 'serverContext': - systemMessage += getServerData(); - break; - case 'environmentContext': - systemMessage += await getEnvironmentData(); - break; - case 'pipelineContext': - systemMessage += getPipelineData().contextString; - systemMessage += '\n A pipeline is a series of steps in a machine learning workflow.'; - break; - case 'stackContext': - systemMessage += getStackData(); - break; - case 'stackComponentsContext': - systemMessage += getStackComponentData(); - break; - case 'logsContext': - systemMessage += await getLogData(); - break; - default: - if (context.includes('Pipeline Run:')) { - let runData = JSON.parse(context.replace('Pipeline Run:', '')); - let logs = await getPipelineRunLogs(runData.id); - let nodeData = await getPipelineRunNodes('step', runData.id); - systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; - systemMessage += `Logs: ${logs}\n`; - systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; - } - break; - } - } - return systemMessage; -} - -function getServerData(): string { - let serverData = ServerDataProvider.getInstance().getCurrentStatus(); - return `Server Status Data:\n${JSON.stringify(serverData)}\n`; -} - -function getStackComponentData(): string { - let components = ComponentDataProvider.getInstance().items; - let componentData = components - .map((item: vscode.TreeItem) => { - if (item instanceof StackComponentTreeItem) { - let { name, type, flavor, id } = item.component; - let stackId = item.stackId; - let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; - let componentId = ` - Component ID: ${id}`; - return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; - } else { - return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; - } - }) - .join('\n'); - return `Stack Component Data:\n${componentData}\n`; -} - -async function getEnvironmentData(): Promise { - let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); - let contextString = environmentData - .map(item => `${item.label}: ${item.description || ''}`) - .join('\n'); - return `Environment Data:\n${contextString}\n`; -} - -function getStackData(): string { - let stackData = StackDataProvider.getInstance().items; - let contextString = stackData - .map(item => { - let stackItem = item as vscode.TreeItem & { isActive: boolean; id: string }; - return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; - }) - .join('\n'); - return `Stack Data:\n${contextString}\n`; -} - -async function getPipelineRunNodes(type: string, id?: string) { - let pipelineRuns = PipelineDataProvider.getInstance().getPipelineData(); - let pipelineData; - - if (id) { - pipelineData = pipelineRuns.filter((pipelineRun) => pipelineRun.id === id);; - } else { - pipelineData = pipelineRuns; - } - - let lsClient = LSClient.getInstance(); - let dagData = await Promise.all( - pipelineData.map(async node => { - let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - return dag; - }) - ); - - let stepData = await Promise.all( - dagData.map(async (dag: PipelineRunDag) => { - let filteredNodes = await Promise.all( - dag.nodes.map(async (node: DagArtifact | DagStep) => { - if (type === 'all' || node.type === type) { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - }) - ); - return filteredNodes.filter(value => value !== null); - }) - ); - return stepData; -} - -async function getLogData() { - let lsClient = LSClient.getInstance(); - - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; - - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } - - let pipelineRunSteps = await getPipelineRunNodes('step'); - - let logs = await Promise.all( - pipelineRunSteps[0].map(async step => { - if (step && step.id) { - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${step.id}`, error); - } - } else { - console.warn('Encountered a null or invalid step.'); - } - }) - ); - return logs; -} - -async function getPipelineRunLogs(id: string) { - let lsClient = LSClient.getInstance(); - - let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); - - let stepData = await Promise.all( - dagData.nodes.map(async (node: DagArtifact | DagStep) => { - if (node.type === 'step') { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - }) - ); - - stepData = stepData.filter(value => value !== null); - - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; - - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } - - let logs = await Promise.all( - stepData.map(async step => { - if (step && typeof step === 'object' && 'id' in step) { - let validStep = step as JsonObject; - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } - return null; - }) - ); - logs = logs.filter(log => log !== null); - return logs; -} - -function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { - let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; - let contextString = ''; - let treeItems: TreeItem[] = []; - - pipelineRuns.forEach(run => { - let formattedStartTime = new Date(run.startTime).toLocaleString(); - let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; - - contextString += - `Pipeline Run:\n` + - `Name: ${run.name}\n` + - `Status: ${run.status}\n` + - `Stack Name: ${run.stackName}\n` + - `Start Time: ${formattedStartTime}\n` + - `End Time: ${formattedEndTime}\n` + - `OS: ${run.os} ${run.osVersion}\n` + - `Python Version: ${run.pythonVersion}\n\n`; - - let stringValue = `Pipeline Run:${JSON.stringify(run)}`; - let treeItem: TreeItem = { - name: run.name, - value: stringValue, - title: 'Includes all code, logs, and metadata for a specific pipeline run with message', - children: [ - { name: run.status }, - { name: run.stackName }, - { name: formattedStartTime }, - { name: formattedEndTime }, - { name: `${run.os} ${run.osVersion}` }, - { name: run.pythonVersion }, - ], - }; - treeItems.push(treeItem); - }); - - return { contextString, treeItems }; -} - -function getPaginatedTreeData(): TreeItem[] { - let { treeItems } = getPipelineData(); - let paginatedTreeItems = []; - let pagination = PipelineDataProvider.getInstance().pagination; - let paginatedTreeItem = { title: "pagination", name: `${pagination.currentPage} of ${pagination.totalPages}`, firstPage: false, lastPage: false }; - - for (let i = 0; i < treeItems.length; i++) { - paginatedTreeItems.push(treeItems[i]); - } - - if (pagination.currentPage === 1) { - paginatedTreeItem.firstPage = true; - } else if (pagination.currentPage === pagination.totalPages) { - paginatedTreeItem.lastPage = true; - } else { - paginatedTreeItem.firstPage = false; - paginatedTreeItem.lastPage = false; - } - - if (pagination.totalItems > pagination.itemsPerPage) { - paginatedTreeItems.push(paginatedTreeItem); - } - - return paginatedTreeItems; -} - -export function getTreeData(): TreeItem[] { - let treeItems = getPaginatedTreeData(); - let treeData: TreeItem[] = [ - { - name: 'Server', - value: 'serverContext', - title: 'Includes all server metadata with message', - }, - { - name: 'Environment', - value: 'environmentContext', - title: 'Includes all server metadata with message', - }, - { - name: 'Pipeline Runs', - value: 'pipelineContext', - title: 'Includes all code, logs, and metadata for pipeline runs with message', - children: treeItems, - }, - { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, - { - name: 'Stack Components', - value: 'stackComponentsContext', - title: 'Includes all stack component metadata with message', - }, - ]; - return treeData; -} diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts new file mode 100644 index 00000000..4cc7bc2e --- /dev/null +++ b/src/views/chatView/utils/ContextUtils.ts @@ -0,0 +1,215 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import { PipelineDataProvider, ServerDataProvider, StackDataProvider } from '../../activityBar'; +import { LSClient } from '../../../services/LSClient'; +import { ZenmlGlobalConfigResp } from '../../../types/LSClientResponseTypes'; +import { PipelineRunDag, DagStep, DagArtifact } from '../../../types/PipelineTypes'; +import { JsonObject } from '../../panel/panelView/PanelTreeItem'; +import { StackComponentTreeItem } from '../../activityBar'; +import axios from 'axios'; +import * as vscode from 'vscode'; +import { getPipelineData } from './PipelineUtils'; +import { ComponentDataProvider } from '../../activityBar/componentView/ComponentDataProvider'; +import { EnvironmentDataProvider } from '../../activityBar/environmentView/EnvironmentDataProvider'; + +export async function addContext(requestedContext: string[]): Promise { + let systemMessage = 'Context:\n'; + for (let context of requestedContext) { + switch (context) { + case 'serverContext': + systemMessage += getServerData(); + break; + case 'environmentContext': + systemMessage += await getEnvironmentData(); + break; + case 'pipelineContext': + systemMessage += getPipelineData().contextString; + systemMessage += '\n A pipeline is a series of steps in a machine learning workflow.'; + break; + case 'stackContext': + systemMessage += getStackData(); + break; + case 'stackComponentsContext': + systemMessage += getStackComponentData(); + break; + case 'logsContext': + systemMessage += await getLogData(); + break; + default: + if (context.includes('Pipeline Run:')) { + let runData = JSON.parse(context.replace('Pipeline Run:', '')); + let logs = await getPipelineRunLogs(runData.id); + let nodeData = await getPipelineRunNodes('step', runData.id); + systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; + systemMessage += `Logs: ${logs}\n`; + systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; + } + break; + } + } + return systemMessage; +} + +function getServerData(): string { + let serverData = ServerDataProvider.getInstance().getCurrentStatus(); + return `Server Status Data:\n${JSON.stringify(serverData)}\n`; +} + +function getStackComponentData(): string { + let components = ComponentDataProvider.getInstance().items; + let componentData = components + .map((item: vscode.TreeItem) => { + if (item instanceof StackComponentTreeItem) { + let { name, type, flavor, id } = item.component; + let stackId = item.stackId; + let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; + let componentId = ` - Component ID: ${id}`; + return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; + } else { + return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; + } + }) + .join('\n'); + return `Stack Component Data:\n${componentData}\n`; +} + +async function getEnvironmentData(): Promise { + let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); + let contextString = environmentData + .map(item => `${item.label}: ${item.description || ''}`) + .join('\n'); + return `Environment Data:\n${contextString}\n`; +} + +function getStackData(): string { + let stackData = StackDataProvider.getInstance().items; + let contextString = stackData + .map(item => { + let stackItem = item as vscode.TreeItem & { isActive: boolean; id: string }; + return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; + }) + .join('\n'); + return `Stack Data:\n${contextString}\n`; +} + +async function getLogData() { + let lsClient = LSClient.getInstance(); + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url; + + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } + + let pipelineRunSteps = await getPipelineRunNodes('step'); + + let logs = await Promise.all( + pipelineRunSteps[0].map(async step => { + if (step && step.id) { + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); + } + } else { + console.warn('Encountered a null or invalid step.'); + } + }) + ); + return logs; +} + +async function getPipelineRunLogs(id: string) { + let lsClient = LSClient.getInstance(); + + let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); + + let stepData = await Promise.all( + dagData.nodes.map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + ); + + stepData = stepData.filter(value => value !== null); + + let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + let apiToken = globalConfig.store.api_token; + let dashboardUrl = globalConfig.store.url; + + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } + + let logs = await Promise.all( + stepData.map(async step => { + if (step && typeof step === 'object' && 'id' in step) { + let validStep = step as JsonObject; + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } + return null; + }) + ); + logs = logs.filter(log => log !== null); + return logs; +} + +async function getPipelineRunNodes(type: string, id?: string) { + let pipelineRuns = PipelineDataProvider.getInstance().getPipelineData(); + let pipelineData; + + if (id) { + pipelineData = pipelineRuns.filter((pipelineRun) => pipelineRun.id === id);; + } else { + pipelineData = pipelineRuns; + } + + let lsClient = LSClient.getInstance(); + let dagData = await Promise.all( + pipelineData.map(async node => { + let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); + return dag; + }) + ); + + let stepData = await Promise.all( + dagData.map(async (dag: PipelineRunDag) => { + let filteredNodes = await Promise.all( + dag.nodes.map(async (node: DagArtifact | DagStep) => { + if (type === 'all' || node.type === type) { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + ); + return filteredNodes.filter(value => value !== null); + }) + ); + return stepData; +} \ No newline at end of file diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts new file mode 100644 index 00000000..4496d528 --- /dev/null +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -0,0 +1,109 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import { PipelineDataProvider } from '../../activityBar'; +import { TreeItem } from '../../../types/ChatTypes'; + +export function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { + let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; + let contextString = ''; + let treeItems: TreeItem[] = []; + + pipelineRuns.forEach(run => { + let formattedStartTime = new Date(run.startTime).toLocaleString(); + let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; + + contextString += + `Pipeline Run:\n` + + `Name: ${run.name}\n` + + `Status: ${run.status}\n` + + `Stack Name: ${run.stackName}\n` + + `Start Time: ${formattedStartTime}\n` + + `End Time: ${formattedEndTime}\n` + + `OS: ${run.os} ${run.osVersion}\n` + + `Python Version: ${run.pythonVersion}\n\n`; + + let stringValue = `Pipeline Run:${JSON.stringify(run)}`; + let treeItem: TreeItem = { + name: run.name, + value: stringValue, + title: 'Includes all code, logs, and metadata for a specific pipeline run with message', + children: [ + { name: `run name: ${run.name}` }, + { name: `status: ${run.status}` }, + { name: `stack: ${run.stackName}` }, + { name: `start time: ${formattedStartTime}` }, + { name: `end time: ${formattedEndTime}` }, + { name: `os: ${run.os} ${run.osVersion}` }, + { name: `python version: ${run.pythonVersion}` }, + ], + }; + treeItems.push(treeItem); + }); + + return { contextString, treeItems }; +} + +export function getPaginatedTreeData(): TreeItem[] { + let { treeItems } = getPipelineData(); + let paginatedTreeItems = []; + let pagination = PipelineDataProvider.getInstance().pagination; + let paginatedTreeItem = { title: "pagination", name: `${pagination.currentPage} of ${pagination.totalPages}`, firstPage: false, lastPage: false }; + + for (let i = 0; i < treeItems.length; i++) { + paginatedTreeItems.push(treeItems[i]); + } + + if (pagination.currentPage === 1) { + paginatedTreeItem.firstPage = true; + } else if (pagination.currentPage === pagination.totalPages) { + paginatedTreeItem.lastPage = true; + } else { + paginatedTreeItem.firstPage = false; + paginatedTreeItem.lastPage = false; + } + + if (pagination.totalItems > pagination.itemsPerPage) { + paginatedTreeItems.push(paginatedTreeItem); + } + + return paginatedTreeItems; +} + +export function getTreeData(): TreeItem[] { + let treeItems = getPaginatedTreeData(); + let treeData: TreeItem[] = [ + { + name: 'Server', + value: 'serverContext', + title: 'Includes all server metadata with message', + }, + { + name: 'Environment', + value: 'environmentContext', + title: 'Includes all server metadata with message', + }, + { + name: 'Pipeline Runs', + value: 'pipelineContext', + title: 'Includes all code, logs, and metadata for pipeline runs with message', + children: treeItems, + }, + { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, + { + name: 'Stack Components', + value: 'stackComponentsContext', + title: 'Includes all stack component metadata with message', + }, + ]; + return treeData; +} \ No newline at end of file diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts new file mode 100644 index 00000000..eea6a9b0 --- /dev/null +++ b/src/views/chatView/utils/TokenUtils.ts @@ -0,0 +1,110 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. +import * as vscode from 'vscode'; +import { ChatMessage } from '../../../types/ChatTypes'; +import { addContext } from './ContextUtils'; + +let tokenjs: any; + +export async function initializeTokenJS(context: vscode.ExtensionContext, provider: string) { + const apiKeySecret = `zenml.${provider.toLowerCase()}.key`; + const apiKey = await context.secrets.get(apiKeySecret); + + if (!apiKey) { + throw new Error( + `API key for ${provider} not found. Please set the ${apiKeySecret} in VS Code secrets.` + ); + } + const config: Record = {}; + config['apiKey'] = apiKey; + const module = await import('token.js'); + const { TokenJS } = module; + tokenjs = new TokenJS(config); +} + +export async function* getChatResponse( + messages: ChatMessage[], + context: string[], + provider: string, + model: string +): AsyncGenerator { + const template = ` + You are a ZenML assistant that summarizes users' ZenML information, problem solves users' ZenML problems, or optimizes users' code in their ZenML pipeline runs. + + Every time you get a user message, check the message for Context. + + Structure (with markdown) the output like this: + +
    +

    Category 1

    +
    + Key 1-1 + value 1-1 +
    + Key 1-2 + value 1-2 +

    +
    +

    Category 2

    +
    + Key 2-1 + value 2-1 +
    + Key 2-2 + value 2-2 + + To bold words, use tags. Do not ever use asterisks for formatting. + To write code blocks, use tags. + if there's an explanation at the end, add it like: + +

    +
    +

    Summary

    +
    +
    + Explanation +
  • point 1
  • +
  • point 2
  • + `; + if (!tokenjs) { + throw new Error('TokenJS not initialized'); + } + + console.log(`getChatResponse called with provider: ${provider}, model: ${model}`); + + const fullMessages = [ + { role: 'system', content: template }, + { role: 'user', content: await addContext(context) }, + ...messages, + ]; + try { + const stream = await tokenjs.chat.completions.create({ + stream: true, + provider: provider.toLowerCase(), + model: model, + messages: fullMessages.map(msg => ({ + role: msg.role as 'system' | 'user' | 'assistant', + content: msg.content, + })), + }); + + for await (const part of stream) { + if (part.choices[0]?.delta?.content) { + yield part.choices[0].delta.content; + } + } + } catch (error: any) { + console.error('Error in getChatResponse:', error); + throw new Error(`Error with ${provider} API: ${error.message}`); + } +} \ No newline at end of file From a03e8c6ee8d845e0184796d0e77c6460b78c687d Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Sun, 22 Sep 2024 22:03:04 -0700 Subject: [PATCH 079/138] refactor: fix context menu bug after sending message --- package.json | 2 +- resources/chat-view/chat.html | 259 ++++++++++++------- resources/chat-view/chat.js | 292 +++++++++++----------- src/views/chatView/chatRenderer.ts | 3 +- src/views/chatView/utils/ContextUtils.ts | 4 +- src/views/chatView/utils/PipelineUtils.ts | 11 +- src/views/chatView/utils/TokenUtils.ts | 2 +- 7 files changed, 330 insertions(+), 243 deletions(-) diff --git a/package.json b/package.json index 8e79a1c8..964e99ae 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "compile": "webpack", "compile-tests": "tsc -p . --outDir out", "deploy": "vsce publish", - "format": "prettier --ignore-path .gitignore --write \"**/*.+(ts|json)\"", + "format": "prettier --ignore-path .gitignore --write \"**/*.+(ts|json|js|html)\"", "format-check": "prettier --ignore-path .gitignore --check \"**/*.+(ts|json)\"", "install": "pip install -r requirements.txt --target bundled/libs", "lint": "eslint src --ext ts", diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index c07b7c0b..e39e56aa 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -10,30 +10,33 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions and limitations under the License. --> - + - - - + + + ZenML Chat - + - - -
    -

    ZenML Chat

    -
    -
    ${chatLogHtml}
    -
    -
    -
    -
    -
    -
    -
    - - -
    -
    -
    - ${providerDropdownHtml} -
    -
    - ${modelDropdownHtml} -
    -
    - -
    -
    ${treeItemHtml}
    -
    -
    -
    - + +
    +
    ${providerDropdownHtml}
    +
    ${modelDropdownHtml}
    +
    + +
    +
    ${treeItemHtml}
    +
    +
    +
    + +
    -
    -
    -
    -
    -
    -
    -
    - +
    +
    +
    +
    +
    +
    + +
    +
    - -
    -
    -
    -
    - +
    +
    +
    + +
    +
    - -
    -
    -
    -
    - +
    +
    +
    + +
    @@ -352,7 +422,6 @@

    ZenML Chat

    -
    - - - \ No newline at end of file + + + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 3938491d..079a2cd2 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -68,14 +68,15 @@ }); const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; - let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; + let isContextPipelineRunsDisplayed = + localStorage.getItem('displayContextPipelineRuns') === 'true'; if (isContextPipelineRunsDisplayed === true) { - pipelineRunsDropdown.classList.add('open'); - } else { - pipelineRunsDropdown.classList.remove('open'); - } + pipelineRunsDropdown.classList.add('open'); + } else { + pipelineRunsDropdown.classList.remove('open'); } + } function sendMessage(event) { event.preventDefault(); @@ -328,172 +329,183 @@ }); } + document.addEventListener('DOMContentLoaded', event => { + const contextButton = document.getElementById('contextButton'); + const optionsDropdown = document.getElementById('optionsDropdown'); - - -document.addEventListener('DOMContentLoaded', (event) => { - const contextButton = document.getElementById('contextButton'); - const optionsDropdown = document.getElementById('optionsDropdown'); - - let isContextDisplayed = localStorage.getItem('displayContext') === 'true'; - if (isContextDisplayed) { - optionsDropdown.classList.remove('hidden'); - localStorage.setItem('displayContext', "true"); - } else { - optionsDropdown.classList.add('hidden'); - localStorage.setItem('displayContext', "false"); - } - - contextButton.addEventListener('click', (event) => { - event.stopPropagation(); - optionsDropdown.classList.toggle('hidden'); - isContextDisplayed = !isContextDisplayed; - localStorage.setItem('displayContext', String(isContextDisplayed)); - }); - - document.addEventListener('click', (event) => { - if (!contextButton.contains(event.target) && !optionsDropdown.contains(event.target)) { + let isContextDisplayed = localStorage.getItem('displayContext') === 'true'; + if (isContextDisplayed) { + optionsDropdown.classList.remove('hidden'); + } else { optionsDropdown.classList.add('hidden'); - localStorage.setItem('displayContext', "false"); } - }); - - const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; - let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; - if (isContextPipelineRunsDisplayed === true && isContextDisplayed === true) { - pipelineRunsDropdown.classList.add('open'); - } else { - pipelineRunsDropdown.classList.remove('open'); - } - const treeItemsWithChildren = Array.from(document.querySelectorAll('div.tree-item-wrapper')) - .filter(treeItemsWithChildren => treeItemsWithChildren.querySelector('div.tree-item-children') !== null); - - treeItemsWithChildren.forEach(wrapper => { - wrapper.querySelector('div.tree-item-content').addEventListener('click', function(event) { - const checkboxEl = this.querySelector('input[type="checkbox"]'); - const childrenEl = this.parentNode.querySelector('div.tree-item-children'); - const chevronEl = this.querySelector('span.tree-item-icon'); - const prevPageButton = document.getElementById('prevPage'); - const nextPageButton = document.getElementById('nextPage'); - - if (event.target !== checkboxEl && event.target !== nextPageButton && event.target !== prevPageButton) { - event.stopPropagation(); - childrenEl.classList.toggle('open'); - isContextPipelineRunsDisplayed = !isContextPipelineRunsDisplayed; - localStorage.setItem('displayContextPipelineRuns', String(isContextPipelineRunsDisplayed)); - - if (childrenEl.classList.contains('open')) { - chevronEl.innerHTML = ''; - } else { - chevronEl.innerHTML = ''; - } + contextButton.addEventListener('click', event => { + event.stopPropagation(); + optionsDropdown.classList.toggle('hidden'); + isContextDisplayed = !isContextDisplayed; + localStorage.setItem('displayContext', String(isContextDisplayed)); + }); + + document.addEventListener('click', event => { + if (!contextButton.contains(event.target) && !optionsDropdown.contains(event.target)) { + optionsDropdown.classList.add('hidden'); + isContextDisplayed = false; + localStorage.setItem('displayContext', String(isContextDisplayed)); } }); - }); - const textarea = document.getElementById('messageInput'); - const sendButton = document.getElementById('sendMessage'); - const loader = document.getElementById('loader'); - let isInputDisabled = false; - function showLoader() { - loader.classList.add('loader'); - } + const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; + let isContextPipelineRunsDisplayed = + localStorage.getItem('displayContextPipelineRuns') === 'true'; + if (isContextPipelineRunsDisplayed === true && isContextDisplayed === true) { + pipelineRunsDropdown.classList.add('open'); + } else { + pipelineRunsDropdown.classList.remove('open'); + } - function hideLoader() { - loader.classList.remove('loader'); - } + const treeItemsWithChildren = Array.from( + document.querySelectorAll('div.tree-item-wrapper') + ).filter( + treeItemsWithChildren => + treeItemsWithChildren.querySelector('div.tree-item-children') !== null + ); - function disableInput() { - isInputDisabled = true; - sendButton.disabled = true; - } + treeItemsWithChildren.forEach(wrapper => { + wrapper.querySelector('div.tree-item-content').addEventListener('click', function (event) { + const checkboxEl = this.querySelector('input[type="checkbox"]'); + const childrenEl = this.parentNode.querySelector('div.tree-item-children'); + const chevronEl = this.querySelector('span.tree-item-icon'); + const prevPageButton = document.getElementById('prevPage'); + const nextPageButton = document.getElementById('nextPage'); + + if ( + event.target !== checkboxEl && + event.target !== nextPageButton && + event.target !== prevPageButton + ) { + event.stopPropagation(); + childrenEl.classList.toggle('open'); + isContextPipelineRunsDisplayed = !isContextPipelineRunsDisplayed; + localStorage.setItem( + 'displayContextPipelineRuns', + String(isContextPipelineRunsDisplayed) + ); + + if (childrenEl.classList.contains('open')) { + chevronEl.innerHTML = + ''; + } else { + chevronEl.innerHTML = + ''; + } + } + }); + }); - function enableInput() { - isInputDisabled = false; - sendButton.disabled = false; - } - - // Listen for messages from the extension - window.addEventListener('message', event => { - const message = event.data; + const textarea = document.getElementById('messageInput'); + const sendButton = document.getElementById('sendMessage'); + const loader = document.getElementById('loader'); + let isInputDisabled = false; - switch (message.text) { - case 'disableInput': - disableInput(); - break; - case 'enableInput': - enableInput(); - break; - default: - break; + function showLoader() { + loader.classList.add('loader'); } - if (message.command === 'hideLoader') { - hideLoader(); + function hideLoader() { + loader.classList.remove('loader'); } - }); - textarea.addEventListener('keydown', (e) => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - - if (!isInputDisabled) { - const form = document.getElementById('chatForm'); - const event = new SubmitEvent('submit', { - bubbles: true, - cancelable: true, - }); - form.dispatchEvent(event); - showLoader(); - } + function disableInput() { + isInputDisabled = true; + sendButton.disabled = true; } - }); - textarea.addEventListener('keypress', (e) => { - if (e.key === 'Enter' && e.shiftKey) { - // Insert a new line when Shift+Enter is pressed - e.preventDefault(); - textarea.value += '\n'; - textarea.scrollTop = textarea.scrollHeight; + function enableInput() { + isInputDisabled = false; + sendButton.disabled = false; } - }); - // Restore dropdown state - restoreState(); -}); + // Listen for messages from the extension + window.addEventListener('message', event => { + const message = event.data; -// Function to restore the saved state -function restoreState() { - const selectedProvider = localStorage.getItem('selectedProvider'); - const selectedModel = localStorage.getItem('selectedModel'); - const selectedContexts = JSON.parse(localStorage.getItem('selectedContexts')) || []; + switch (message.text) { + case 'disableInput': + disableInput(); + break; + case 'enableInput': + enableInput(); + break; + default: + break; + } - if (selectedProvider) { - document.querySelector('#provider-dropdown').value = selectedProvider; - } - if (selectedModel) { - document.querySelector('#model-dropdown').value = selectedModel; - } + if (message.command === 'hideLoader') { + hideLoader(); + } + }); - const allCheckboxes = document.querySelectorAll('#tree-view input[type="checkbox"]'); + textarea.addEventListener('keydown', e => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); - selectedContexts.forEach(savedValue => { - allCheckboxes.forEach(checkbox => { - if (checkbox.value === savedValue) { - checkbox.checked = true; + if (!isInputDisabled) { + const form = document.getElementById('chatForm'); + const event = new SubmitEvent('submit', { + bubbles: true, + cancelable: true, + }); + form.dispatchEvent(event); + showLoader(); + } } }); + + textarea.addEventListener('keypress', e => { + if (e.key === 'Enter' && e.shiftKey) { + // Insert a new line when Shift+Enter is pressed + e.preventDefault(); + textarea.value += '\n'; + textarea.scrollTop = textarea.scrollHeight; + } + }); + + // Restore dropdown state + restoreState(); }); - const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; - let isContextPipelineRunsDisplayed = localStorage.getItem('displayContextPipelineRuns') === 'true'; + // Function to restore the saved state + function restoreState() { + const selectedProvider = localStorage.getItem('selectedProvider'); + const selectedModel = localStorage.getItem('selectedModel'); + const selectedContexts = JSON.parse(localStorage.getItem('selectedContexts')) || []; - if (isContextPipelineRunsDisplayed === true) { + if (selectedProvider) { + document.querySelector('#provider-dropdown').value = selectedProvider; + } + if (selectedModel) { + document.querySelector('#model-dropdown').value = selectedModel; + } + + const allCheckboxes = document.querySelectorAll('#tree-view input[type="checkbox"]'); + + selectedContexts.forEach(savedValue => { + allCheckboxes.forEach(checkbox => { + if (checkbox.value === savedValue) { + checkbox.checked = true; + } + }); + }); + + const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; + let isContextPipelineRunsDisplayed = + localStorage.getItem('displayContextPipelineRuns') === 'true'; + + if (isContextPipelineRunsDisplayed === true) { pipelineRunsDropdown.classList.add('open'); } else { pipelineRunsDropdown.classList.remove('open'); } } - })(); diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index c16dd920..04532383 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -138,7 +138,8 @@ function convertTreeDataToHtml(treeData: TreeItem[], level = 0): string { title = item.title; } - let checkboxEl = level < 2 ? `` : ''; + let checkboxEl = + level < 2 ? `` : ''; if (item.title === 'pagination') { checkboxEl = ''; diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 4cc7bc2e..bdf18875 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -185,7 +185,7 @@ async function getPipelineRunNodes(type: string, id?: string) { let pipelineData; if (id) { - pipelineData = pipelineRuns.filter((pipelineRun) => pipelineRun.id === id);; + pipelineData = pipelineRuns.filter(pipelineRun => pipelineRun.id === id); } else { pipelineData = pipelineRuns; } @@ -212,4 +212,4 @@ async function getPipelineRunNodes(type: string, id?: string) { }) ); return stepData; -} \ No newline at end of file +} diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index 4496d528..2d644202 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -57,8 +57,13 @@ export function getPaginatedTreeData(): TreeItem[] { let { treeItems } = getPipelineData(); let paginatedTreeItems = []; let pagination = PipelineDataProvider.getInstance().pagination; - let paginatedTreeItem = { title: "pagination", name: `${pagination.currentPage} of ${pagination.totalPages}`, firstPage: false, lastPage: false }; - + let paginatedTreeItem = { + title: 'pagination', + name: `${pagination.currentPage} of ${pagination.totalPages}`, + firstPage: false, + lastPage: false, + }; + for (let i = 0; i < treeItems.length; i++) { paginatedTreeItems.push(treeItems[i]); } @@ -106,4 +111,4 @@ export function getTreeData(): TreeItem[] { }, ]; return treeData; -} \ No newline at end of file +} diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index eea6a9b0..e514dca7 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -107,4 +107,4 @@ export async function* getChatResponse( console.error('Error in getChatResponse:', error); throw new Error(`Error with ${provider} API: ${error.message}`); } -} \ No newline at end of file +} From 66391882efe40586f648344b35ba2f4a2e801a82 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Mon, 23 Sep 2024 13:58:37 -0400 Subject: [PATCH 080/138] fix: change context message role from 'user' to 'system' to ensure context updates selected by user are recognized --- src/views/chatView/utils/TokenUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index e514dca7..66542455 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -84,7 +84,7 @@ export async function* getChatResponse( const fullMessages = [ { role: 'system', content: template }, - { role: 'user', content: await addContext(context) }, + { role: 'system', content: await addContext(context) }, ...messages, ]; try { From 84af4b34ecf433e9a26938c7f9b46049b2afe6fb Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Mon, 23 Sep 2024 13:42:25 -0700 Subject: [PATCH 081/138] refactor: update styling for pipeline context menu --- src/views/chatView/chatRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 04532383..8a92a85d 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -145,8 +145,10 @@ function convertTreeDataToHtml(treeData: TreeItem[], level = 0): string { checkboxEl = ''; if (item.firstPage) { nextPageEl = ``; + prevPageEl = ``; } else if (item.lastPage) { prevPageEl = ``; + nextPageEl = ``; } else { nextPageEl = ``; prevPageEl = ``; From a5c4702a490329f9c4208e9d8010aae3fc7bb2a7 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 13:50:49 -0400 Subject: [PATCH 082/138] refactor(chat): manage disposables in ChatDataProvider - Added tracking of disposables in ChatDataProvider using the _disposables array. - Ensured event listeners and other resources are properly disposed of. - Updated cmds.ts to call ChatDataProvider.dispose() when the webview panel is disposed. - Improved resource management to prevent potential memory leaks. --- requirements.txt | 74 +++++++++++++------------- src/commands/chat/cmds.ts | 2 +- src/test/python_tests/requirements.txt | 6 +-- src/views/chatView/ChatDataProvider.ts | 18 ++++++- 4 files changed, 57 insertions(+), 43 deletions(-) diff --git a/requirements.txt b/requirements.txt index d6b824fb..5d47682f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,9 +10,9 @@ attrs==24.2.0 \ # via # cattrs # lsprotocol -cattrs==24.1.0 \ - --hash=sha256:043bb8af72596432a7df63abcff0055ac0f198a4d2e95af8db5a936a7074a761 \ - --hash=sha256:8274f18b253bf7674a43da851e3096370d67088165d23138b04a1c04c8eaf48e +cattrs==24.1.2 \ + --hash=sha256:67c7495b760168d931a10233f979b28dc04daf853b30752246f4f8471c6d68d0 \ + --hash=sha256:8028cfe1ff5382df59dd36474a86e02d817b06eaf8af84555441bac915d2ef85 # via # lsprotocol # pygls @@ -89,9 +89,9 @@ pyyaml==6.0.2 \ --hash=sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12 \ --hash=sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4 # via -r ./requirements.in -types-pyyaml==6.0.12.20240808 \ - --hash=sha256:b8f76ddbd7f65440a8bda5526a9607e4c7a322dc2f8e1a8c405644f9a6f4b9af \ - --hash=sha256:deda34c5c655265fc517b546c902aa6eed2ef8d3e921e4765fe606fe2afe8d35 +types-pyyaml==6.0.12.20240917 \ + --hash=sha256:392b267f1c0fe6022952462bf5d6523f31e37f6cea49b14cee7ad634b6301570 \ + --hash=sha256:d1405a86f9576682234ef83bcb4e6fff7c9305c8b1fbad5e0bcd4f7dbdc9c587 # via -r ./requirements.in typing-extensions==4.12.2 ; python_version < "3.11" \ --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \ @@ -99,35 +99,35 @@ typing-extensions==4.12.2 ; python_version < "3.11" \ # via # -r ./requirements.in # cattrs -watchdog==5.0.0 \ - --hash=sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658 \ - --hash=sha256:01ab36cddc836a0f202c66267daaef92ba5c17c7d6436deff0587bb61234c5c9 \ - --hash=sha256:0710e9502727f688a7e06d48078545c54485b3d6eb53b171810879d8223c362a \ - --hash=sha256:0834c21efa3e767849b09e667274604c7cdfe30b49eb95d794565c53f4db3c1e \ - --hash=sha256:109daafc5b0f2a98d1fa9475ff9737eb3559d57b18129a36495e20c71de0b44f \ - --hash=sha256:1228cb097e855d1798b550be8f0e9f0cfbac4384f9a3e91f66d250d03e11294e \ - --hash=sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7 \ - --hash=sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c \ - --hash=sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d \ - --hash=sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7 \ - --hash=sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5 \ - --hash=sha256:3c177085c3d210d1c73cb4569442bdaef706ebebc423bd7aed9e90fc12b2e553 \ - --hash=sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4 \ - --hash=sha256:4fe6780915000743074236b21b6c37419aea71112af62237881bc265589fe463 \ - --hash=sha256:663b096368ed7831ac42259919fdb9e0a1f0a8994d972675dfbcca0225e74de1 \ - --hash=sha256:685931412978d00a91a193d9018fc9e394e565e8e7a0c275512a80e59c6e85f8 \ - --hash=sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45 \ - --hash=sha256:6e58eafe9cc5ceebe1562cdb89bacdcd0ef470896e8b0139fe677a5abec243da \ - --hash=sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9 \ - --hash=sha256:7e6b0e9b8a9dc3865d65888b5f5222da4ba9c4e09eab13cff5e305e7b7e7248f \ - --hash=sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e \ - --hash=sha256:b8d747bf6d8fe5ce89cb1a36c3724d1599bd4cde3f90fcba518e6260c7058a52 \ - --hash=sha256:bc16d448a74a929b896ed9578c25756b2125400b19b3258be8d9a681c7ae8e71 \ - --hash=sha256:bf3216ec994eabb2212df9861f19056ca0d4cd3516d56cb95801933876519bfe \ - --hash=sha256:c2b4d90962639ae7cee371ea3a8da506831945d4418eee090c53bc38e6648dc6 \ - --hash=sha256:cb59ad83a1700304fc1ac7bc53ae9e5cbe9d60a52ed9bba8e2e2d782a201bb2b \ - --hash=sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b \ - --hash=sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12 \ - --hash=sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8 \ - --hash=sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9 +watchdog==5.0.2 \ + --hash=sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b \ + --hash=sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc \ + --hash=sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769 \ + --hash=sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7 \ + --hash=sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d \ + --hash=sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8 \ + --hash=sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8 \ + --hash=sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0 \ + --hash=sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941 \ + --hash=sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5 \ + --hash=sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b \ + --hash=sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f \ + --hash=sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab \ + --hash=sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee \ + --hash=sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e \ + --hash=sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee \ + --hash=sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde \ + --hash=sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f \ + --hash=sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889 \ + --hash=sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb \ + --hash=sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e \ + --hash=sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73 \ + --hash=sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b \ + --hash=sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619 \ + --hash=sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877 \ + --hash=sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d \ + --hash=sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76 \ + --hash=sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7 \ + --hash=sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1 \ + --hash=sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b # via -r ./requirements.in diff --git a/src/commands/chat/cmds.ts b/src/commands/chat/cmds.ts index b28e2e17..d83b2476 100644 --- a/src/commands/chat/cmds.ts +++ b/src/commands/chat/cmds.ts @@ -38,7 +38,7 @@ const openChat = (context: vscode.ExtensionContext) => { chatDataProvider.resolveWebviewView(fakeWebviewView, fakeContext, dummyCancellationToken); panel.onDidDispose(() => { - // Clean up resources or perform any necessary actions when the panel is disposed + chatDataProvider.dispose(); }); }; diff --git a/src/test/python_tests/requirements.txt b/src/test/python_tests/requirements.txt index 5bb8718f..513d8fdd 100644 --- a/src/test/python_tests/requirements.txt +++ b/src/test/python_tests/requirements.txt @@ -24,9 +24,9 @@ pyhamcrest==2.1.0 \ --hash=sha256:c6acbec0923d0cb7e72c22af1926f3e7c97b8e8d69fc7498eabacaf7c975bd9c \ --hash=sha256:f6913d2f392e30e0375b3ecbd7aee79e5d1faa25d345c8f4ff597665dcac2587 # via -r ./src/test/python_tests/requirements.in -pytest==8.3.2 \ - --hash=sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5 \ - --hash=sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce +pytest==8.3.3 \ + --hash=sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181 \ + --hash=sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2 # via -r ./src/test/python_tests/requirements.in python-jsonrpc-server==0.4.0 \ --hash=sha256:62c543e541f101ec5b57dc654efc212d2c2e3ea47ff6f54b2e7dcb36ecf20595 \ diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 7c504611..5b22bcdf 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -25,10 +25,14 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private currentProvider: string = 'Gemini'; private currentModel: string = 'gemini-pro'; private eventBus: EventBus = EventBus.getInstance(); + private _disposables: vscode.Disposable[] = []; constructor(private readonly context: vscode.ExtensionContext) { initializeTokenJS(this.context, this.currentProvider); this.eventBus.addListener(LSP_ZENML_STACK_CHANGED, this.refreshWebview.bind(this)); + this._disposables.push( + new vscode.Disposable(() => this.eventBus.removeListener(LSP_ZENML_STACK_CHANGED, this.refreshWebview.bind(this))) + ); } resolveWebviewView( @@ -40,9 +44,19 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.configureWebViewOptions(webviewView.webview); this.loadWebviewContent(); - webviewView.webview.onDidReceiveMessage(async message => { + const messageListener = async (message: any) => { await handleWebviewMessage(message, this); - }); + }; + + webviewView.webview.onDidReceiveMessage(messageListener); + this._disposables.push( + new vscode.Disposable(() => webviewView.webview.onDidReceiveMessage(messageListener)) + ); + } + + dispose(): void { + this._disposables.forEach(disposable => disposable.dispose()); + this._disposables = []; } private configureWebViewOptions(webview: vscode.Webview) { From 7f72f4bd57efb82b9349a4a7cfe738e3432a7285 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 14:08:39 -0400 Subject: [PATCH 083/138] refactor(chatView): improve type safety and function structure in handleWebviewMessage - Moved WebviewMessage and CommandHandler types to ChatTypes.ts for better type management and reusability. - Refactored handleWebviewMessage to use a commandHandlers object for improved readability and maintainability. --- src/types/ChatTypes.ts | 8 +++ src/views/chatView/chatMessageHandler.ts | 73 +++++++++++++++--------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 175e765f..37bb47b9 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -25,3 +25,11 @@ export interface TreeItem { firstPage?: boolean; lastPage?: boolean; } + +export interface WebviewMessage { + command: string; + text?: string; + context?: string[]; + provider?: string; + model?: string; +} \ No newline at end of file diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index 20278228..f9d006dc 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -12,40 +12,57 @@ // permissions and limitations under the License. import { PipelineDataProvider } from '../activityBar'; import { ChatDataProvider } from './ChatDataProvider'; +import { WebviewMessage } from '../../types/ChatTypes'; -export async function handleWebviewMessage(message: any, chatDataProvider: ChatDataProvider) { - if (message.command === 'sendMessage' && message.text) { - await chatDataProvider.addMessage( - message.text, - message.context, - message.provider, - message.model - ); - } +type CommandHandler = (message: WebviewMessage, chatDataProvider: ChatDataProvider) => Promise; - if (message.command === 'clearChat') { +const commandHandlers: Record = { + sendMessage: async (message, chatDataProvider) => { + if (message.text) { + await chatDataProvider.addMessage( + message.text, + message.context, + message.provider, + message.model + ); + } + }, + clearChat: async (_, chatDataProvider) => { await chatDataProvider.clearChatLog(); - } - - if (message.command === 'showInfo') { - chatDataProvider.showInfoMessage(message.text); - } - - if (message.command === 'updateProvider') { - chatDataProvider.updateProvider(message.provider); - } - - if (message.command === 'updateModel') { - chatDataProvider.updateModel(message.model); - } - - if (message.command === 'prevPage') { + }, + showInfo: (message, chatDataProvider) => { + if (message.text) { + chatDataProvider.showInfoMessage(message.text); + } + return Promise.resolve(); + }, + updateProvider: (message, chatDataProvider) => { + if (message.provider) { + chatDataProvider.updateProvider(message.provider); + } + return Promise.resolve(); + }, + updateModel: (message, chatDataProvider) => { + if (message.model) { + chatDataProvider.updateModel(message.model); + } + return Promise.resolve(); + }, + prevPage: async (_, chatDataProvider) => { await PipelineDataProvider.getInstance().goToPreviousPage(); await chatDataProvider.refreshWebview(); - } - - if (message.command === 'nextPage') { + }, + nextPage: async (_, chatDataProvider) => { await PipelineDataProvider.getInstance().goToNextPage(); await chatDataProvider.refreshWebview(); + }, +}; + +export async function handleWebviewMessage(message: WebviewMessage, chatDataProvider: ChatDataProvider) { + const handler = commandHandlers[message.command]; + if (handler) { + await handler(message, chatDataProvider); + } else { + console.warn(`Unknown command: ${message.command}`); } } From 170533733c4c954ea77d2f60d2f29c74524cfa13 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 14:20:33 -0400 Subject: [PATCH 084/138] fix(chatMessageHandler): add error handling and property validation to command handlers --- src/views/chatView/chatMessageHandler.ts | 58 ++++++++++++++++++++---- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index f9d006dc..a03f0dcc 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -18,43 +18,83 @@ type CommandHandler = (message: WebviewMessage, chatDataProvider: ChatDataProvid const commandHandlers: Record = { sendMessage: async (message, chatDataProvider) => { - if (message.text) { + if (!message.text) { + console.error('sendMessage command received without text property'); + return; + } + try { await chatDataProvider.addMessage( message.text, message.context, message.provider, message.model ); + } catch (error) { + console.error('Error adding message:', error); + chatDataProvider.showInfoMessage('Failed to send message. Please try again.'); } }, clearChat: async (_, chatDataProvider) => { - await chatDataProvider.clearChatLog(); + try { + await chatDataProvider.clearChatLog(); + } catch (error) { + console.error('Error clearing chat log:', error); + chatDataProvider.showInfoMessage('Failed to clear chat. Please try again.'); + } }, showInfo: (message, chatDataProvider) => { - if (message.text) { + if (!message.text) { + console.error('showInfo command received without text property'); + return Promise.resolve(); + } + try { chatDataProvider.showInfoMessage(message.text); + } catch (error) { + console.error('Error showing info message:', error); } return Promise.resolve(); }, updateProvider: (message, chatDataProvider) => { - if (message.provider) { + if (!message.provider) { + console.error('updateProvider command received without provider property'); + return Promise.resolve(); + } + try { chatDataProvider.updateProvider(message.provider); + } catch (error) { + console.error('Error updating provider:', error); } return Promise.resolve(); }, updateModel: (message, chatDataProvider) => { - if (message.model) { + if (!message.model) { + console.error('updateModel command received without model property'); + return Promise.resolve(); + } + try { chatDataProvider.updateModel(message.model); + } catch (error) { + console.error('Error updating model:', error); } return Promise.resolve(); }, prevPage: async (_, chatDataProvider) => { - await PipelineDataProvider.getInstance().goToPreviousPage(); - await chatDataProvider.refreshWebview(); + try { + await PipelineDataProvider.getInstance().goToPreviousPage(); + await chatDataProvider.refreshWebview(); + } catch (error) { + console.error('Error going to previous page:', error); + chatDataProvider.showInfoMessage('Failed to go to previous page. Please try again.'); + } }, nextPage: async (_, chatDataProvider) => { - await PipelineDataProvider.getInstance().goToNextPage(); - await chatDataProvider.refreshWebview(); + try { + await PipelineDataProvider.getInstance().goToNextPage(); + await chatDataProvider.refreshWebview(); + } catch (error) { + console.error('Error going to next page:', error); + chatDataProvider.showInfoMessage('Failed to go to next page. Please try again.'); + } }, }; From 186fa185ab58d5bf69d330c6be4d9af5387959ae Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 14:42:48 -0400 Subject: [PATCH 085/138] refactor(secrets): move LLM_PROVIDERS array outside registerLLMAPIKey function --- src/commands/secrets/cmds.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/commands/secrets/cmds.ts b/src/commands/secrets/cmds.ts index 8c4b0289..fbf5f0dd 100644 --- a/src/commands/secrets/cmds.ts +++ b/src/commands/secrets/cmds.ts @@ -14,14 +14,14 @@ import * as vscode from 'vscode'; import type { ExtensionContext } from 'vscode'; -const registerLLMAPIKey = async (context: ExtensionContext) => { - const options: vscode.QuickPickItem[] = [ - { label: 'Anthropic' }, - { label: 'Gemini' }, - { label: 'OpenAI' }, - ]; +const LLM_PROVIDERS: vscode.QuickPickItem[] = [ + { label: 'Anthropic' }, + { label: 'Gemini' }, + { label: 'OpenAI' }, +]; - const selectedOption = await vscode.window.showQuickPick(options, { +const registerLLMAPIKey = async (context: ExtensionContext) => { + const selectedOption = await vscode.window.showQuickPick(LLM_PROVIDERS, { placeHolder: 'Please select a provider.', canPickMany: false, }); From 57ae8fb9f1bbe608c551dcbbafb2ccbdc87ffa32 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 14:52:09 -0400 Subject: [PATCH 086/138] refactor(chat): remove duplicate functions and consolidate event listeners - Removed duplicate disableInput, enableInput, and restoreState functions. - Replaced deprecated keypress event with keydown and consolidated event listeners. - Declared currentAssistantMessage at the beginning of the script to ensure proper scoping. --- resources/chat-view/chat.js | 106 +++++++----------------------------- 1 file changed, 20 insertions(+), 86 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 079a2cd2..f1db5e8d 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -11,6 +11,7 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. (function () { + let currentAssistantMessage = ''; const vscode = acquireVsCodeApi(); // Function to save the current state @@ -201,7 +202,7 @@ break; } case 'showInfo': { - vscode.window.showInformationMesage(message.text); + vscode.window.showInformationMessage(message.text); break; } case 'updateModelList': { @@ -416,96 +417,29 @@ loader.classList.remove('loader'); } - function disableInput() { - isInputDisabled = true; - sendButton.disabled = true; - } - - function enableInput() { - isInputDisabled = false; - sendButton.disabled = false; - } - - // Listen for messages from the extension - window.addEventListener('message', event => { - const message = event.data; - - switch (message.text) { - case 'disableInput': - disableInput(); - break; - case 'enableInput': - enableInput(); - break; - default: - break; - } - - if (message.command === 'hideLoader') { - hideLoader(); - } - }); - textarea.addEventListener('keydown', e => { - if (e.key === 'Enter' && !e.shiftKey) { - e.preventDefault(); - - if (!isInputDisabled) { - const form = document.getElementById('chatForm'); - const event = new SubmitEvent('submit', { - bubbles: true, - cancelable: true, - }); - form.dispatchEvent(event); - showLoader(); + if (e.key === 'Enter') { + if (e.shiftKey) { + // Insert a new line when Shift+Enter is pressed + e.preventDefault(); + textarea.value += '\n'; + textarea.scrollTop = textarea.scrollHeight; + } else { + e.preventDefault(); + if (!isInputDisabled) { + const form = document.getElementById('chatForm'); + const event = new SubmitEvent('submit', { + bubbles: true, + cancelable: true, + }); + form.dispatchEvent(event); + showLoader(); + } } } }); - textarea.addEventListener('keypress', e => { - if (e.key === 'Enter' && e.shiftKey) { - // Insert a new line when Shift+Enter is pressed - e.preventDefault(); - textarea.value += '\n'; - textarea.scrollTop = textarea.scrollHeight; - } - }); - // Restore dropdown state restoreState(); }); - - // Function to restore the saved state - function restoreState() { - const selectedProvider = localStorage.getItem('selectedProvider'); - const selectedModel = localStorage.getItem('selectedModel'); - const selectedContexts = JSON.parse(localStorage.getItem('selectedContexts')) || []; - - if (selectedProvider) { - document.querySelector('#provider-dropdown').value = selectedProvider; - } - if (selectedModel) { - document.querySelector('#model-dropdown').value = selectedModel; - } - - const allCheckboxes = document.querySelectorAll('#tree-view input[type="checkbox"]'); - - selectedContexts.forEach(savedValue => { - allCheckboxes.forEach(checkbox => { - if (checkbox.value === savedValue) { - checkbox.checked = true; - } - }); - }); - - const pipelineRunsDropdown = document.querySelectorAll('div.tree-item-children')[0]; - let isContextPipelineRunsDisplayed = - localStorage.getItem('displayContextPipelineRuns') === 'true'; - - if (isContextPipelineRunsDisplayed === true) { - pipelineRunsDropdown.classList.add('open'); - } else { - pipelineRunsDropdown.classList.remove('open'); - } - } -})(); +})(); \ No newline at end of file From 8538b2cc971694c13713963ea952872a5f567acd Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 15:11:06 -0400 Subject: [PATCH 087/138] refactor(types): improve type safety in TreeItem and WebviewMessage interfaces - Updated TreeItem interface to use a more specific type for the value property. - Used string literal types for command, provider, and model properties in WebviewMessage interface for better type safety and autocomplete support. --- src/types/ChatTypes.ts | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 37bb47b9..f0f27f92 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -18,7 +18,7 @@ export interface ChatMessage { export interface TreeItem { name: string; - value?: string; + value?: string | number; children?: TreeItem[]; title?: string; hidden?: boolean; @@ -27,9 +27,40 @@ export interface TreeItem { } export interface WebviewMessage { - command: string; + command: 'sendMessage' | 'clearChat' | 'showInfo' | 'updateProvider' | 'updateModel' | 'prevPage' | 'nextPage'; text?: string; context?: string[]; - provider?: string; - model?: string; + provider?: 'openai' | 'anthropic' | 'gemini'; + model?: | 'gemini-1.5-pro' + | 'gemini-1.5-flash' + | 'gemini-1.0-pro' + | 'gpt-4o' + | 'gpt-4o-mini' + | 'gpt-4o-2024-05-13' + | 'gpt-4-turbo' + | 'gpt-4-turbo-2024-04-09' + | 'gpt-4-0125-preview' + | 'gpt-4-turbo-preview' + | 'gpt-4-1106-preview' + | 'gpt-4-vision-preview' + | 'gpt-4' + | 'gpt-4-0314' + | 'gpt-4-0613' + | 'gpt-4-32k' + | 'gpt-4-32k-0314' + | 'gpt-4-32k-0613' + | 'gpt-3.5-turbo' + | 'gpt-3.5-turbo-16k' + | 'gpt-3.5-turbo-0301' + | 'gpt-3.5-turbo-0613' + | 'gpt-3.5-turbo-1106' + | 'gpt-3.5-turbo-0125' + | 'gpt-3.5-turbo-16k-0613' + | 'claude-3-5-sonnet-20240620' + | 'claude-3-opus-20240229' + | 'claude-3-sonnet-20240229' + | 'claude-3-haiku-20240307' + | 'claude-2.1' + | 'claude-2.0' + | 'claude-instant-1.2'; } \ No newline at end of file From f0aad975c793e92f5fabf2b543982930aadabe4e Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:00:18 -0400 Subject: [PATCH 088/138] fix: Correct context type for resolveWebviewView in openChat --- src/commands/chat/cmds.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/commands/chat/cmds.ts b/src/commands/chat/cmds.ts index d83b2476..45bee052 100644 --- a/src/commands/chat/cmds.ts +++ b/src/commands/chat/cmds.ts @@ -10,6 +10,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express // or implied.See the License for the specific language governing // permissions and limitations under the License. +// ... existing code ... import * as vscode from 'vscode'; import { ChatDataProvider } from '../../views/chatView/ChatDataProvider'; @@ -21,21 +22,20 @@ const openChat = (context: vscode.ExtensionContext) => { const chatDataProvider = new ChatDataProvider(context); - const fakeContext = {} as vscode.WebviewViewResolveContext; - - const dummyCancellationToken: vscode.CancellationToken = { - isCancellationRequested: false, - onCancellationRequested: callback => { - return new vscode.Disposable(() => {}); - }, + // Create a WebviewViewResolveContext + const webviewViewResolveContext: vscode.WebviewViewResolveContext = { + state: undefined, }; - const fakeWebviewView = { + const webviewView = { webview: panel.webview, onDidDispose: panel.onDidDispose, } as vscode.WebviewView; - chatDataProvider.resolveWebviewView(fakeWebviewView, fakeContext, dummyCancellationToken); + chatDataProvider.resolveWebviewView(webviewView, webviewViewResolveContext, { + isCancellationRequested: false, + onCancellationRequested: callback => new vscode.Disposable(() => {}), + }); panel.onDidDispose(() => { chatDataProvider.dispose(); From 9d9750a3003554e165c7e2d507a6e94e25dd2e65 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:10:43 -0400 Subject: [PATCH 089/138] refactor(docs): Improve WebviewMessage interface and add documentation - Added JSDoc comments to the ChatMessage and TreeItem interfaces. - Moved AI model names to a separate AIModel type for better maintainability. - Improved overall documentation and readability of the code. --- src/types/ChatTypes.ts | 43 +++++++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 7 deletions(-) diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index f0f27f92..21bdb1e1 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -11,28 +11,42 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. +/** + * Represents a chat message. + */ export interface ChatMessage { + /** The role of the message sender (e.g., user, assistant). */ role: string; + /** The content of the message. */ content: string; } +/** + * Represents an item in a tree structure. + */ export interface TreeItem { + /** The name of the tree item. */ name: string; + /** The value associated with the tree item (optional). */ value?: string | number; + /** The children of the tree item (optional). */ children?: TreeItem[]; + /** The title of the tree item (optional). */ title?: string; + /** Whether the tree item is hidden (optional). */ hidden?: boolean; + /** Whether the tree item is the first page (optional). */ firstPage?: boolean; + /** Whether the tree item is the last page (optional). */ lastPage?: boolean; } -export interface WebviewMessage { - command: 'sendMessage' | 'clearChat' | 'showInfo' | 'updateProvider' | 'updateModel' | 'prevPage' | 'nextPage'; - text?: string; - context?: string[]; - provider?: 'openai' | 'anthropic' | 'gemini'; - model?: | 'gemini-1.5-pro' - | 'gemini-1.5-flash' +/** + * Represents the available AI models. + */ +export type AIModel = + | 'gemini-1.5-pro' + | 'gemini-1.5-flash' | 'gemini-1.0-pro' | 'gpt-4o' | 'gpt-4o-mini' @@ -63,4 +77,19 @@ export interface WebviewMessage { | 'claude-2.1' | 'claude-2.0' | 'claude-instant-1.2'; + +/** + * Represents a message sent to the webview. + */ +export interface WebviewMessage { + /** The command to be executed in the webview. */ + command: 'sendMessage' | 'clearChat' | 'showInfo' | 'updateProvider' | 'updateModel' | 'prevPage' | 'nextPage'; + /** The text content of the message (if applicable). */ + text?: string; + /** Additional context for the message (if applicable). */ + context?: string[]; + /** The AI provider to be used (if applicable). */ + provider?: 'openai' | 'anthropic' | 'gemini'; + /** The AI model to be used (if applicable). */ + model?: AIModel; } \ No newline at end of file From c362c38a6b38083c2ae7fbbe26cd882f64565d02 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:19:37 -0400 Subject: [PATCH 090/138] refactor: Enhance error handling in sendMessage handler - Added specific error messages for NetworkError and ValidationError. - Defined custom error classes for NetworkError and ValidationError. - Improved user feedback by providing more informative error messages. --- src/views/chatView/chatMessageHandler.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index a03f0dcc..205b6771 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -16,10 +16,25 @@ import { WebviewMessage } from '../../types/ChatTypes'; type CommandHandler = (message: WebviewMessage, chatDataProvider: ChatDataProvider) => Promise; +class NetworkError extends Error { + constructor(message: string) { + super(message); + this.name = 'NetworkError'; + } +} + +class ValidationError extends Error { + constructor(message: string) { + super(message); + this.name = 'ValidationError'; + } +} + const commandHandlers: Record = { sendMessage: async (message, chatDataProvider) => { if (!message.text) { console.error('sendMessage command received without text property'); + chatDataProvider.showInfoMessage('Message text is required.'); return; } try { @@ -31,7 +46,13 @@ const commandHandlers: Record = { ); } catch (error) { console.error('Error adding message:', error); - chatDataProvider.showInfoMessage('Failed to send message. Please try again.'); + if (error instanceof NetworkError) { + chatDataProvider.showInfoMessage('Network error. Please check your connection and try again.'); + } else if (error instanceof ValidationError) { + chatDataProvider.showInfoMessage('Invalid message format. Please try again.'); + } else { + chatDataProvider.showInfoMessage('An unexpected error occurred. Please try again.'); + } } }, clearChat: async (_, chatDataProvider) => { From 4dfc9d4bedd29f89885be3c582bd2914fb6d9438 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:29:54 -0400 Subject: [PATCH 091/138] refactor: Organize custom error classes and enhance error handling - Created CustomErrors.ts in the chat folder for custom error classes (NetworkError, ValidationError, StorageError). - Enhanced error handling in sendMessage and clearChat handlers with specific error messages. - Improved user feedback by providing more informative error messages. - Improved code organization and maintainability. --- src/views/chatView/chatMessageHandler.ts | 21 +++++---------- src/views/chatView/utils/CustomErrors.ts | 33 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 15 deletions(-) create mode 100644 src/views/chatView/utils/CustomErrors.ts diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index 205b6771..a261ec62 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -13,23 +13,10 @@ import { PipelineDataProvider } from '../activityBar'; import { ChatDataProvider } from './ChatDataProvider'; import { WebviewMessage } from '../../types/ChatTypes'; +import { NetworkError, ValidationError, StorageError } from './utils/CustomErrors'; type CommandHandler = (message: WebviewMessage, chatDataProvider: ChatDataProvider) => Promise; -class NetworkError extends Error { - constructor(message: string) { - super(message); - this.name = 'NetworkError'; - } -} - -class ValidationError extends Error { - constructor(message: string) { - super(message); - this.name = 'ValidationError'; - } -} - const commandHandlers: Record = { sendMessage: async (message, chatDataProvider) => { if (!message.text) { @@ -60,7 +47,11 @@ const commandHandlers: Record = { await chatDataProvider.clearChatLog(); } catch (error) { console.error('Error clearing chat log:', error); - chatDataProvider.showInfoMessage('Failed to clear chat. Please try again.'); + if (error instanceof StorageError) { + chatDataProvider.showInfoMessage('Unable to clear chat history. Storage error occurred.'); + } else { + chatDataProvider.showInfoMessage('Failed to clear chat. Please try again.'); + } } }, showInfo: (message, chatDataProvider) => { diff --git a/src/views/chatView/utils/CustomErrors.ts b/src/views/chatView/utils/CustomErrors.ts new file mode 100644 index 00000000..ae177d3e --- /dev/null +++ b/src/views/chatView/utils/CustomErrors.ts @@ -0,0 +1,33 @@ +// Copyright(c) ZenML GmbH 2024. All Rights Reserved. +// Licensed under the Apache License, Version 2.0(the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +// or implied.See the License for the specific language governing +// permissions and limitations under the License. + +export class NetworkError extends Error { + constructor(message: string) { + super(message); + this.name = 'NetworkError'; + } + } + + export class ValidationError extends Error { + constructor(message: string) { + super(message); + this.name = 'ValidationError'; + } + } + + export class StorageError extends Error { + constructor(message: string) { + super(message); + this.name = 'StorageError'; + } + } \ No newline at end of file From c60248d247554b6f42a741859d30954b58c98199 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:38:25 -0400 Subject: [PATCH 092/138] refactor: Enhance consistency and error handling in chat handlers - Made showInfo, updateProvider, and updateModel handlers async for consistency. - Removed unnecessary Promise.resolve() calls. - Added specific error messages in catch blocks to improve user feedback. --- src/views/chatView/chatMessageHandler.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index a261ec62..5a70d06d 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -54,41 +54,41 @@ const commandHandlers: Record = { } } }, - showInfo: (message, chatDataProvider) => { + showInfo: async (message, chatDataProvider) => { if (!message.text) { console.error('showInfo command received without text property'); - return Promise.resolve(); + return; } try { chatDataProvider.showInfoMessage(message.text); } catch (error) { console.error('Error showing info message:', error); + chatDataProvider.showInfoMessage('Failed to show info message. Please try again.'); } - return Promise.resolve(); }, - updateProvider: (message, chatDataProvider) => { + updateProvider: async (message, chatDataProvider) => { if (!message.provider) { console.error('updateProvider command received without provider property'); - return Promise.resolve(); + return; } try { chatDataProvider.updateProvider(message.provider); } catch (error) { console.error('Error updating provider:', error); + chatDataProvider.showInfoMessage('Failed to update provider. Please try again.'); } - return Promise.resolve(); }, - updateModel: (message, chatDataProvider) => { + updateModel: async (message, chatDataProvider) => { if (!message.model) { console.error('updateModel command received without model property'); - return Promise.resolve(); + return; } try { chatDataProvider.updateModel(message.model); } catch (error) { console.error('Error updating model:', error); + chatDataProvider.showInfoMessage('Failed to update model. Please try again.'); } - return Promise.resolve(); }, prevPage: async (_, chatDataProvider) => { try { From b6a403bea64a1d7e3bfe07e4d8ebb28ba08730df Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 18:44:06 -0400 Subject: [PATCH 093/138] refactor: Use dependency injection for PipelineDataProvider - Updated CommandHandler type to include PipelineDataProvider. - Modified prevPage and nextPage handlers to use the injected pipelineDataProvider. - Adjusted handleWebviewMessage function to pass the PipelineDataProvider instance. - Improved code modularity and testability. --- src/views/chatView/ChatDataProvider.ts | 4 +++- src/views/chatView/chatMessageHandler.ts | 22 +++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 5b22bcdf..bf19d503 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -17,6 +17,7 @@ import { handleWebviewMessage } from './chatMessageHandler'; import { EventBus } from '../../services/EventBus'; import { LSP_ZENML_STACK_CHANGED } from '../../utils/constants'; import { getChatResponse, initializeTokenJS } from './utils/TokenUtils'; +import { PipelineDataProvider } from '../activityBar'; export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; @@ -45,7 +46,8 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.loadWebviewContent(); const messageListener = async (message: any) => { - await handleWebviewMessage(message, this); + const pipelineDataProvider = PipelineDataProvider.getInstance(); + await handleWebviewMessage(message, this, pipelineDataProvider); }; webviewView.webview.onDidReceiveMessage(messageListener); diff --git a/src/views/chatView/chatMessageHandler.ts b/src/views/chatView/chatMessageHandler.ts index 5a70d06d..5b47a332 100644 --- a/src/views/chatView/chatMessageHandler.ts +++ b/src/views/chatView/chatMessageHandler.ts @@ -15,7 +15,11 @@ import { ChatDataProvider } from './ChatDataProvider'; import { WebviewMessage } from '../../types/ChatTypes'; import { NetworkError, ValidationError, StorageError } from './utils/CustomErrors'; -type CommandHandler = (message: WebviewMessage, chatDataProvider: ChatDataProvider) => Promise; +type CommandHandler = ( + message: WebviewMessage, + chatDataProvider: ChatDataProvider, + pipelineDataProvider: PipelineDataProvider +) => Promise; const commandHandlers: Record = { sendMessage: async (message, chatDataProvider) => { @@ -90,18 +94,18 @@ const commandHandlers: Record = { chatDataProvider.showInfoMessage('Failed to update model. Please try again.'); } }, - prevPage: async (_, chatDataProvider) => { + prevPage: async (_, chatDataProvider, pipelineDataProvider) => { try { - await PipelineDataProvider.getInstance().goToPreviousPage(); + await pipelineDataProvider.goToPreviousPage(); await chatDataProvider.refreshWebview(); } catch (error) { console.error('Error going to previous page:', error); chatDataProvider.showInfoMessage('Failed to go to previous page. Please try again.'); } }, - nextPage: async (_, chatDataProvider) => { + nextPage: async (_, chatDataProvider, pipelineDataProvider) => { try { - await PipelineDataProvider.getInstance().goToNextPage(); + await pipelineDataProvider.goToNextPage(); await chatDataProvider.refreshWebview(); } catch (error) { console.error('Error going to next page:', error); @@ -110,10 +114,14 @@ const commandHandlers: Record = { }, }; -export async function handleWebviewMessage(message: WebviewMessage, chatDataProvider: ChatDataProvider) { +export async function handleWebviewMessage( + message: WebviewMessage, + chatDataProvider: ChatDataProvider, + pipelineDataProvider: PipelineDataProvider +) { const handler = commandHandlers[message.command]; if (handler) { - await handler(message, chatDataProvider); + await handler(message, chatDataProvider, pipelineDataProvider); } else { console.warn(`Unknown command: ${message.command}`); } From ace8bc349c2c037f886f5e371d0f03dc9254274c Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 19:02:08 -0400 Subject: [PATCH 094/138] refactor: Optimize and enhance error handling and type safety in PipelineUtils - Added error handling and used map for better performance in getPipelineData. - Used date-fns for consistent date formatting in getPipelineData. - Improved error handling and array operations in getPaginatedTreeData. - Simplified logic for setting firstPage and lastPage in getPaginatedTreeData. - Defined ContextItem interface and extracted context items in getTreeData. - Updated getTreeData to use CONTEXT_ITEMS constant and added specific return type. --- package-lock.json | 11 ++ package.json | 1 + src/types/ChatTypes.ts | 14 ++ src/views/chatView/utils/PipelineUtils.ts | 148 ++++++++++------------ 4 files changed, 94 insertions(+), 80 deletions(-) diff --git a/package-lock.json b/package-lock.json index 336cdc5b..65e6533d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@vscode/python-extension": "^1.0.5", "axios": "^1.7.7", "dagre": "^0.8.5", + "date-fns": "^4.1.0", "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", @@ -3911,6 +3912,16 @@ "lodash": "^4.17.15" } }, + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/package.json b/package.json index 964e99ae..7c4ce014 100644 --- a/package.json +++ b/package.json @@ -530,6 +530,7 @@ "@vscode/python-extension": "^1.0.5", "axios": "^1.7.7", "dagre": "^0.8.5", + "date-fns": "^4.1.0", "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 21bdb1e1..d794617c 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -41,6 +41,20 @@ export interface TreeItem { lastPage?: boolean; } +/** + * Represents a context item in the tree structure. + */ +export interface ContextItem { + /** The name of the context item. */ + name: string; + /** The value associated with the context item. */ + value: string; + /** The title of the context item. */ + title: string; + /** The children of the context item (optional). */ + children?: TreeItem[]; +} + /** * Represents the available AI models. */ diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index 2d644202..e67de8e0 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -10,105 +10,93 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express // or implied.See the License for the specific language governing // permissions and limitations under the License. +import { format } from 'date-fns'; import { PipelineDataProvider } from '../../activityBar'; -import { TreeItem } from '../../../types/ChatTypes'; +import { TreeItem, ContextItem } from '../../../types/ChatTypes'; -export function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { - let pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; - let contextString = ''; - let treeItems: TreeItem[] = []; +const CONTEXT_ITEMS: readonly ContextItem[] = [ + { name: 'Server', value: 'serverContext', title: 'Includes all server metadata with message' }, + { name: 'Environment', value: 'environmentContext', title: 'Includes all server metadata with message' }, + { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, + { name: 'Stack Components', value: 'stackComponentsContext', title: 'Includes all stack component metadata with message' }, +] as const; - pipelineRuns.forEach(run => { - let formattedStartTime = new Date(run.startTime).toLocaleString(); - let formattedEndTime = run.endTime ? new Date(run.endTime).toLocaleString() : 'N/A'; +export function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { + try { + const pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; + let contextString = ''; + const treeItems: TreeItem[] = pipelineRuns.map(run => { + const formattedStartTime = format(new Date(run.startTime), 'Pp'); + const formattedEndTime = run.endTime ? format(new Date(run.endTime), 'Pp') : 'N/A'; - contextString += - `Pipeline Run:\n` + - `Name: ${run.name}\n` + - `Status: ${run.status}\n` + - `Stack Name: ${run.stackName}\n` + - `Start Time: ${formattedStartTime}\n` + - `End Time: ${formattedEndTime}\n` + - `OS: ${run.os} ${run.osVersion}\n` + - `Python Version: ${run.pythonVersion}\n\n`; + contextString += + `Pipeline Run:\n` + + `Name: ${run.name}\n` + + `Status: ${run.status}\n` + + `Stack Name: ${run.stackName}\n` + + `Start Time: ${formattedStartTime}\n` + + `End Time: ${formattedEndTime}\n` + + `OS: ${run.os} ${run.osVersion}\n` + + `Python Version: ${run.pythonVersion}\n\n`; - let stringValue = `Pipeline Run:${JSON.stringify(run)}`; - let treeItem: TreeItem = { - name: run.name, - value: stringValue, - title: 'Includes all code, logs, and metadata for a specific pipeline run with message', - children: [ - { name: `run name: ${run.name}` }, - { name: `status: ${run.status}` }, - { name: `stack: ${run.stackName}` }, - { name: `start time: ${formattedStartTime}` }, - { name: `end time: ${formattedEndTime}` }, - { name: `os: ${run.os} ${run.osVersion}` }, - { name: `python version: ${run.pythonVersion}` }, - ], - }; - treeItems.push(treeItem); - }); + const stringValue = `Pipeline Run:${JSON.stringify(run)}`; + const treeItem: TreeItem = { + name: run.name, + value: stringValue, + title: 'Includes all code, logs, and metadata for a specific pipeline run with message', + children: [ + { name: `run name: ${run.name}` }, + { name: `status: ${run.status}` }, + { name: `stack: ${run.stackName}` }, + { name: `start time: ${formattedStartTime}` }, + { name: `end time: ${formattedEndTime}` }, + { name: `os: ${run.os} ${run.osVersion}` }, + { name: `python version: ${run.pythonVersion}` }, + ], + }; + return treeItem; + }); - return { contextString, treeItems }; + return { contextString, treeItems }; + } catch (error) { + console.error('Error fetching pipeline data:', error); + return { contextString: '', treeItems: [] }; + } } export function getPaginatedTreeData(): TreeItem[] { - let { treeItems } = getPipelineData(); - let paginatedTreeItems = []; - let pagination = PipelineDataProvider.getInstance().pagination; - let paginatedTreeItem = { - title: 'pagination', - name: `${pagination.currentPage} of ${pagination.totalPages}`, - firstPage: false, - lastPage: false, - }; - - for (let i = 0; i < treeItems.length; i++) { - paginatedTreeItems.push(treeItems[i]); - } + try { + const { treeItems } = getPipelineData(); + const paginatedTreeItems = [...treeItems]; + const pagination = PipelineDataProvider.getInstance().pagination; + const paginatedTreeItem = { + title: 'pagination', + name: `${pagination.currentPage} of ${pagination.totalPages}`, + firstPage: pagination.currentPage === 1, + lastPage: pagination.currentPage === pagination.totalPages, + }; - if (pagination.currentPage === 1) { - paginatedTreeItem.firstPage = true; - } else if (pagination.currentPage === pagination.totalPages) { - paginatedTreeItem.lastPage = true; - } else { - paginatedTreeItem.firstPage = false; - paginatedTreeItem.lastPage = false; - } + if (pagination.totalItems > pagination.itemsPerPage) { + paginatedTreeItems.push(paginatedTreeItem); + } - if (pagination.totalItems > pagination.itemsPerPage) { - paginatedTreeItems.push(paginatedTreeItem); + return paginatedTreeItems; + } catch (error) { + console.error('Error fetching paginated tree data:', error); + return []; } - - return paginatedTreeItems; } export function getTreeData(): TreeItem[] { - let treeItems = getPaginatedTreeData(); - let treeData: TreeItem[] = [ - { - name: 'Server', - value: 'serverContext', - title: 'Includes all server metadata with message', - }, - { - name: 'Environment', - value: 'environmentContext', - title: 'Includes all server metadata with message', - }, + const paginatedItems = getPaginatedTreeData(); + + return [ + ...CONTEXT_ITEMS, { name: 'Pipeline Runs', value: 'pipelineContext', title: 'Includes all code, logs, and metadata for pipeline runs with message', - children: treeItems, - }, - { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, - { - name: 'Stack Components', - value: 'stackComponentsContext', - title: 'Includes all stack component metadata with message', + children: paginatedItems, }, ]; - return treeData; } From ae3fabd5b0cfc786580c2d475358b11529784e01 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 19:20:10 -0400 Subject: [PATCH 095/138] chore: Remove chat.css file in favor of inline styling - Deleted chat.css file as styles are now applied using inline styling. - Simplified project structure by removing unused CSS file. --- resources/chat-view/chat.css | 191 ----------------------------- src/views/chatView/chatRenderer.ts | 4 - 2 files changed, 195 deletions(-) delete mode 100644 resources/chat-view/chat.css diff --git a/resources/chat-view/chat.css b/resources/chat-view/chat.css deleted file mode 100644 index 9000fd05..00000000 --- a/resources/chat-view/chat.css +++ /dev/null @@ -1,191 +0,0 @@ -/* Copyright(c) ZenML GmbH 2024. All Rights Reserved. - Licensed under the Apache License, Version 2.0(the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at: - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - or implied.See the License for the specific language governing - permissions and limitations under the License. */ -body { - font-family: var(--vscode-font-family); - margin: 0; - padding: 0; - background-color: var(--vscode-editor-background); - color: var(--vscode-editor-foreground); -} - -#chat-container { - display: flex; - flex-direction: column; - height: 100vh; -} - -#messages { - flex: 1; - overflow-y: auto; - padding: 10px; - background: var(--vscode-editor-background); - border-bottom: 1px solid var(--vscode-panel-border); -} - -#messageInput { - flex: 1; - padding: 10px; - font-size: 14px; - border: 1px solid var(--vscode-input-border); - border-radius: 5px; - background: var(--vscode-input-background); - color: var(--vscode-input-foreground); -} - -.user, .assistant { - background: var(--vscode-editor-background); -} - -#input-container { - display: flex; - padding: 10px; - background: var(--vscode-editor-background); - border-top: 1px solid var(--vscode-panel-border); -} - -#input { - flex: 1; - padding: 5px; - font-size: 14px; -} - -#send { - margin-left: 10px; - padding: 5px 10px; - font-size: 14px; - cursor: pointer; - background-color: var(--vscode-button-background); - color: var(--vscode-button-foreground); - border: none; - border-radius: 4px; -} - -#send:hover { - background-color: var(--vscode-button-hoverBackground); -} - -.message { - padding: 10px; - margin: 5px 0; - background: var(--vscode-editor-background); - border-radius: 10px; - color: var(--vscode-editor-foreground); -} - -#sendMessage { - margin-left: 10px; - padding: 10px; - background: var(--vscode-button-background); - color: var(--vscode-button-foreground); - border: none; - border-radius: 4px; - cursor: pointer; -} - -#sendMessage:hover { - background-color: var(--vscode-button-hoverBackground); -} - -#chatForm { - display: flex; - gap: 10px; -} - -.dropup { - position: relative; - display: inline-block; -} -.dropup-content { - display: none; - position: absolute; - background: var(--vscode-dropdown-background); - min-width: 160px; - box-shadow: 0px -8px 16px 0px var(--vscode-widget-shadow); - bottom: 100%; - left: 0; - z-index: 1; - border-radius: 4px; - margin-bottom: 5px; -} -.dropup-content label { - display: block; - padding: 12px 16px; - color: var(--vscode-dropdown-foreground); -} -.dropup-content label:hover { - background: var(--vscode-list-hoverBackground); -} -.dropup-content input[type="checkbox"] { - margin-right: 8px; - vertical-align: middle; -} -.hide { - display: none; -} -#contextButton { - background: var(--vscode-button-secondaryBackground); - border: 1px solid var(--vscode-button-border); - padding: 5px 10px; - cursor: pointer; - border-radius: 4px; - color: var(--vscode-button-secondaryForeground); -} -#contextButton:hover { - background: var(--vscode-button-secondaryHoverBackground); -} - -.tree-view { - background: var(--vscode-sideBar-background); - color: var(--vscode-sideBar-foreground); - width: 256px; - overflow: auto; -} -.tree-item { - display: flex; - align-items: center; - padding: 2px 8px; - cursor: pointer; -} -.tree-item:hover { - background: var(--vscode-list-hoverBackground); -} -.tree-item-content { - display: flex; - align-items: center; -} -.tree-item-icon { - width: 16px; - height: 16px; - display: inline-flex; - align-items: center; - justify-content: center; -} -.tree-item-checkbox { - margin-right: 8px; -} -.tree-item-name { - font-size: 14px; - color: var(--vscode-sideBarSectionHeader-foreground); - margin-left: 4px; -} -.tree-item-children { - display: none; - width: 100%; -} -.tree-item-children.open { - display: block; -} -.tree-item-wrapper { - display: flex; - flex-direction: column; -} diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 8a92a85d..8e10b90c 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -27,9 +27,6 @@ export function getWebviewContent( const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); - const cssUri = webview.asWebviewUri( - vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.css') - ); const jsUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') ); @@ -39,7 +36,6 @@ export function getWebviewContent( let providerDropdownHtml = getProviderDropdownHtml(currentProvider, availableProviders); let modelDropdownHtml = getModelDropdownHtml(availableModels); - html = html.replace('${cssUri}', cssUri.toString()); html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${treeItemHtml}', treeItemHtml); html = html.replace('${providerDropdownHtml}', providerDropdownHtml); From 03fef9b36c1c6ca1f97a592d27381309ba4761b3 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 19:38:50 -0400 Subject: [PATCH 096/138] fix: Enhance error handling and type safety in getChatResponse - Added nested try-catch for streaming errors. - Improved error logging and re-throwing. - Typed error as unknown and used instanceof for safe handling. --- package.json | 2 -- src/views/chatView/utils/TokenUtils.ts | 19 ++++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 7c4ce014..59b09cbd 100644 --- a/package.json +++ b/package.json @@ -525,7 +525,6 @@ "webpack-cli": "^5.1.4" }, "dependencies": { - "@langchain/google-genai": "^0.0.26", "@svgdotjs/svg.js": "^3.2.4", "@vscode/python-extension": "^1.0.5", "axios": "^1.7.7", @@ -534,7 +533,6 @@ "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", - "langchain": "^0.2.16", "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 66542455..4b1a2a6e 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -98,13 +98,22 @@ export async function* getChatResponse( })), }); - for await (const part of stream) { - if (part.choices[0]?.delta?.content) { - yield part.choices[0].delta.content; + try { + for await (const part of stream) { + if (part.choices[0]?.delta?.content) { + yield part.choices[0].delta.content; + } } + } catch (streamError) { + console.error('Streaming error in getChatResponse:', streamError); + throw new Error(`Streaming error with ${provider} API: ${streamError instanceof Error ? streamError.message : String(streamError)}`); } - } catch (error: any) { + } catch (error: unknown) { console.error('Error in getChatResponse:', error); - throw new Error(`Error with ${provider} API: ${error.message}`); + if (error instanceof Error) { + throw new Error(`Error with ${provider} API: ${error.message}`); + } else { + throw new Error(`Error with ${provider} API: ${String(error)}`); + } } } From ae5636e6371505c096c2fe1da718cfef8246e88c Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 19:45:12 -0400 Subject: [PATCH 097/138] refactor: Ensure consistent naming for button IDs and message commands - Changed button ID to open-chat-button. - Updated message command to openChat. - Renamed handler method to _handleOpenChat. --- .../activityBar/APIView/APIWebviewViewProvider.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/views/activityBar/APIView/APIWebviewViewProvider.ts b/src/views/activityBar/APIView/APIWebviewViewProvider.ts index 0438ec6e..1b678123 100644 --- a/src/views/activityBar/APIView/APIWebviewViewProvider.ts +++ b/src/views/activityBar/APIView/APIWebviewViewProvider.ts @@ -31,19 +31,16 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { webviewView.webview.html = this._getHtmlForWebview(webviewView.webview); - // Add this line to log console messages from the webview webviewView.webview.onDidReceiveMessage(message => { switch (message.command) { - case 'registerLLMAPIKey': - this._handleRegisterApiKey(); + case 'openChat': + this._handleOpenChat(); break; } }); } - private _handleRegisterApiKey(): void { - // vscode.window.showInformationMessage('Registering LLM API Key'); - // vscode.commands.executeCommand('zenml.registerLLMAPIKey'); + private _handleOpenChat(): void { vscode.commands.executeCommand('zenml.openChat'); } @@ -118,13 +115,13 @@ export class APIWebviewViewProvider implements vscode.WebviewViewProvider { - +

    Using the chat feature will incur charges from your model provider as normal.

    From e58f7e09c3cf45a1f386537e8d29afd4738111ab Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Tue, 24 Sep 2024 19:54:36 -0400 Subject: [PATCH 098/138] fix: Properly type the renderer and remove @ts-ignore in renderChatLog - Used Partial to create a partial implementation of the Renderer interface. - Removed @ts-ignore directive and fixed type definitions for the renderer. - Ensured type safety and maintained code quality. --- src/views/chatView/chatRenderer.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 8e10b90c..5e32ef1a 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -12,7 +12,7 @@ // permissions and limitations under the License. import * as vscode from 'vscode'; import * as fs from 'fs'; -import { marked } from 'marked'; +import { marked, Renderer, Tokens } from 'marked'; import { ChatMessage, TreeItem } from '../../types/ChatTypes'; import { getTreeData } from './utils/PipelineUtils'; @@ -76,16 +76,14 @@ export function renderChatLog( messages: ChatMessage[], streamingMessage: ChatMessage | null = null ): string { - const renderer = { - // @ts-ignore - code({ text, lang, escaped, isInline }) { - const code = text.replace(/\n$/, '') + (isInline ? '' : '\n'); - return isInline ? `${code}` : '
    ' + code + '
    \n'; + const renderer: Partial = { + code({ text, lang, escaped }: Tokens.Code) { + const formattedCode = text.replace(/\n$/, '') + (escaped ? '' : '\n'); + return escaped ? `${formattedCode}` : '
    ' + formattedCode + '
    \n'; }, }; - // @ts-ignore - marked.use({ renderer }); + marked.use({ renderer: renderer as Renderer }); const renderedMessages = messages .filter(msg => msg['role'] !== 'system') From 39bcbaba063c69317eb119258a98d6b9544eca23 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Wed, 25 Sep 2024 13:35:41 -0400 Subject: [PATCH 099/138] fix: remove duplicate isInputDisabled, replace SubmitEvent, and send showInfo message to extension host --- resources/chat-view/chat.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index f1db5e8d..e016616d 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -202,7 +202,7 @@ break; } case 'showInfo': { - vscode.window.showInformationMessage(message.text); + vscode.window.showInformationMessage({ command: 'showInfoToExtension', text: message.text }); break; } case 'updateModelList': { @@ -407,7 +407,7 @@ const textarea = document.getElementById('messageInput'); const sendButton = document.getElementById('sendMessage'); const loader = document.getElementById('loader'); - let isInputDisabled = false; + isInputDisabled = false; function showLoader() { loader.classList.add('loader'); @@ -428,7 +428,7 @@ e.preventDefault(); if (!isInputDisabled) { const form = document.getElementById('chatForm'); - const event = new SubmitEvent('submit', { + const event = new Event('submit', { bubbles: true, cancelable: true, }); @@ -442,4 +442,4 @@ // Restore dropdown state restoreState(); }); -})(); \ No newline at end of file +})(); From ed8930e0335ef493cd60cd7e46a8900e74d5dab9 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 14:39:35 -0400 Subject: [PATCH 100/138] refactor: Encapsulate pipelineRuns property and add getter method - Changed `pipelineRuns` property from public to private to better encapsulate internal state. - Added `getPipelineRuns` method to provide controlled access to the `pipelineRuns` data. - Included documentation for the `pipelineRuns` property and `getPipelineRuns` method. --- .../activityBar/pipelineView/PipelineDataProvider.ts | 12 +++++++++++- src/views/chatView/utils/PipelineUtils.ts | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/views/activityBar/pipelineView/PipelineDataProvider.ts b/src/views/activityBar/pipelineView/PipelineDataProvider.ts index 273ca5d5..a5ca9398 100644 --- a/src/views/activityBar/pipelineView/PipelineDataProvider.ts +++ b/src/views/activityBar/pipelineView/PipelineDataProvider.ts @@ -35,7 +35,7 @@ export class PipelineDataProvider extends PaginatedDataProvider { private eventBus = EventBus.getInstance(); private zenmlClientReady = false; private pipelineData: PipelineTreeItem[] = []; - public pipelineRuns: PipelineRun[] = []; + private pipelineRuns: PipelineRun[] = []; constructor() { super(); @@ -44,6 +44,16 @@ export class PipelineDataProvider extends PaginatedDataProvider { this.subscribeToEvents(); } + /** + * Retrieves the pipeline runs data. + * Returns a copy of the pipeline runs array to prevent external modifications. + * + * @returns {PipelineRun[]} A copy of the pipeline runs array. + */ + public getPipelineRuns(): PipelineRun[] { + return [...this.pipelineRuns]; + } + /** * Subscribes to relevant events to trigger a refresh of the tree view. */ diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index e67de8e0..e9baadd7 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -23,7 +23,7 @@ const CONTEXT_ITEMS: readonly ContextItem[] = [ export function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { try { - const pipelineRuns = PipelineDataProvider.getInstance().pipelineRuns; + const pipelineRuns = PipelineDataProvider.getInstance().getPipelineRuns(); let contextString = ''; const treeItems: TreeItem[] = pipelineRuns.map(run => { const formattedStartTime = format(new Date(run.startTime), 'Pp'); From 05f3a634ab4407fed5f957021cd4daf76f065ce8 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 14:58:59 -0400 Subject: [PATCH 101/138] Add error handling for fs.readFileSync and update pagination button logic - Wrapped fs.readFileSync in a try-catch block to handle potential errors and prevent uncaught exceptions. - Added type guard to ensure proper error message display. - Updated pagination button logic to ensure correct assignment of next and previous buttons. --- src/views/chatView/chatRenderer.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 5e32ef1a..53e47767 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -25,7 +25,17 @@ export function getWebviewContent( availableModels: string[] ): string { const htmlPath = vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.html'); - let html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + let html: string; + try { + html = fs.readFileSync(htmlPath.fsPath, 'utf8'); + } catch (err) { + if (err instanceof Error) { + vscode.window.showErrorMessage(`Failed to load HTML template: ${err.message}`); + } else { + vscode.window.showErrorMessage('Failed to load HTML template due to an unkown error.'); + } + return ''; + } const jsUri = webview.asWebviewUri( vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') @@ -138,14 +148,14 @@ function convertTreeDataToHtml(treeData: TreeItem[], level = 0): string { if (item.title === 'pagination') { checkboxEl = ''; if (item.firstPage) { - nextPageEl = ``; - prevPageEl = ``; + nextPageEl = ``; + prevPageEl = ``; } else if (item.lastPage) { - prevPageEl = ``; - nextPageEl = ``; + prevPageEl = ``; + nextPageEl = ``; } else { - nextPageEl = ``; - prevPageEl = ``; + nextPageEl = ``; + prevPageEl = ``; } } From 491de7d76389e0701d15014b4f44c05f04dec467 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 15:21:06 -0400 Subject: [PATCH 102/138] fix: Ensure proper removal of event listener by storing bound function reference - Store the bound function reference for `refreshWebview` in a variable `refreshWebviewBound`. - Update event listener addition and removal to use `refreshWebviewBound`. --- src/views/chatView/ChatDataProvider.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index bf19d503..1481ba79 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -27,12 +27,13 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private currentModel: string = 'gemini-pro'; private eventBus: EventBus = EventBus.getInstance(); private _disposables: vscode.Disposable[] = []; + private refreshWebviewBound = this.refreshWebview.bind(this); constructor(private readonly context: vscode.ExtensionContext) { initializeTokenJS(this.context, this.currentProvider); - this.eventBus.addListener(LSP_ZENML_STACK_CHANGED, this.refreshWebview.bind(this)); + this.eventBus.addListener(LSP_ZENML_STACK_CHANGED, this.refreshWebviewBound); this._disposables.push( - new vscode.Disposable(() => this.eventBus.removeListener(LSP_ZENML_STACK_CHANGED, this.refreshWebview.bind(this))) + new vscode.Disposable(() => this.eventBus.removeListener(LSP_ZENML_STACK_CHANGED, this.refreshWebviewBound)) ); } From 8886671e4653fdd801322f2c40f9d33cfacff144 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 15:25:30 -0400 Subject: [PATCH 103/138] fix: Initialize currentModel with the first available model for the provider - Ensure currentModel is initialized using the first model from getAvailableModels() for the 'Gemini' provider. - This prevents potential issues where currentModel does not match an available model. --- src/views/chatView/ChatDataProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 1481ba79..7a8c3115 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -24,7 +24,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private messages: ChatMessage[] = []; private streamingMessage: ChatMessage | null = null; private currentProvider: string = 'Gemini'; - private currentModel: string = 'gemini-pro'; + private currentModel: string = this.getAvailableModels()[0]; private eventBus: EventBus = EventBus.getInstance(); private _disposables: vscode.Disposable[] = []; private refreshWebviewBound = this.refreshWebview.bind(this); From 5860d10199c90e238d3033e7618ffed35c382e50 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 15:41:43 -0400 Subject: [PATCH 104/138] fix: Dynamic provider name in error message and remove unnecessary delay in response streaming - Updated error message in `addMessage` to dynamically include the current provider name instead of hardcoding 'Gemini'. - Removed the unnecessary 1ms delay in the response streaming loop to improve performance. --- src/views/chatView/ChatDataProvider.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 7a8c3115..5fa8be8f 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -180,7 +180,6 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { for (const letter of partialResponse) { this.streamingMessage.content += letter; this.sendMessageToWebview(letter); - await new Promise(resolve => setTimeout(resolve, 1)); } } @@ -190,7 +189,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.sendMessageToWebview('enableInput'); } catch (error) { console.error('Error in addMessage:', error); - this.sendMessageToWebview('Error: Unable to get response from Gemini'); + this.sendMessageToWebview(`Error: Unable to get response from ${provider || this.currentProvider}`); this.sendMessageToWebview('enableInput'); } } From d1edeb7c97cff42825ce5c9f444be359f6e5ba70 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 16:01:59 -0400 Subject: [PATCH 105/138] fix: Add checks and error handling in ContextUtils - Added a check to ensure `pipelineRunSteps[0]` exists before mapping in `getLogData` to prevent potential undefined access. - Refactored null checks using optional chaining in `getLogData` for cleaner and more concise code. - Added error handling for axios GET request in `getPipelineRunLogs` to handle potential request failures and log errors appropriately. --- src/views/chatView/utils/ContextUtils.ts | 62 ++++++++++++++---------- 1 file changed, 36 insertions(+), 26 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index bdf18875..f0b218c4 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -115,26 +115,31 @@ async function getLogData() { let pipelineRunSteps = await getPipelineRunNodes('step'); - let logs = await Promise.all( - pipelineRunSteps[0].map(async step => { - if (step && step.id) { - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${step.id}`, error); + if (pipelineRunSteps && pipelineRunSteps[0]) { + let logs = await Promise.all( + pipelineRunSteps[0].map(async step => { + if (step?.id) { + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); + } + } else { + console.warn('Encountered a null or invalid step.'); } - } else { - console.warn('Encountered a null or invalid step.'); - } - }) - ); - return logs; + }) + ); + return logs; + } else { + console.warn('No pipeline run steps found.'); + return []; + } } async function getPipelineRunLogs(id: string) { @@ -165,13 +170,18 @@ async function getPipelineRunLogs(id: string) { stepData.map(async step => { if (step && typeof step === 'object' && 'id' in step) { let validStep = step as JsonObject; - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; + try { + let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${validStep.id}`, error); + return null; + } } return null; }) From 824a42e16a7dd62461a0633e35ca0a7100edafdb Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 16:08:03 -0400 Subject: [PATCH 106/138] fix: Add error handling for JSON parsing in addContext - Added a try-catch block around JSON.parse in the default case of addContext to handle potential parsing errors. - Logged errors to the console and added a fallback message to systemMessage to inform the user when pipeline run data cannot be parsed. --- src/views/chatView/utils/ContextUtils.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index f0b218c4..54c8c1b2 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -47,12 +47,18 @@ export async function addContext(requestedContext: string[]): Promise { break; default: if (context.includes('Pipeline Run:')) { - let runData = JSON.parse(context.replace('Pipeline Run:', '')); - let logs = await getPipelineRunLogs(runData.id); - let nodeData = await getPipelineRunNodes('step', runData.id); - systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; - systemMessage += `Logs: ${logs}\n`; - systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; + try { + let runData = JSON.parse(context.replace('Pipeline Run:', '')); + let logs = await getPipelineRunLogs(runData.id); + let nodeData = await getPipelineRunNodes('step', runData.id); + systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; + systemMessage += `Logs: ${logs}\n`; + systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; + } catch (error) { + console.error('Failed to parse pipeline run data from context:', error); + systemMessage += 'Failed to parse pipeline run data from context.\n'; + } + } break; } From 30ffa7acd1593b03f4c66b4c136e286f2eb55db1 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 18:22:05 -0400 Subject: [PATCH 107/138] fix: re-added hideLoader function to stop infinity loading animation. --- resources/chat-view/chat.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index e016616d..17efa7c9 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -209,9 +209,17 @@ updateModelDropdown(message.models); break; } + case 'hideLoader': { + hideLoader(); + break; + } } }); + function hideLoader() { + loader.classList.remove('loader'); + } + function updateModelDropdown(models) { modelDropdown.innerHTML = models .map(model => ``) From 57402d205a345b49223b83972199f8df1f93ec4a Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Thu, 26 Sep 2024 15:30:30 -0700 Subject: [PATCH 108/138] security: prevent XSS attacks --- package-lock.json | 580 ++-------------------------------- package.json | 1 + resources/chat-view/chat.html | 1 + resources/chat-view/chat.js | 13 +- 4 files changed, 43 insertions(+), 552 deletions(-) diff --git a/package-lock.json b/package-lock.json index 65e6533d..6f508638 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,16 +10,15 @@ "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@langchain/google-genai": "^0.0.26", "@svgdotjs/svg.js": "^3.2.4", "@vscode/python-extension": "^1.0.5", "axios": "^1.7.7", "dagre": "^0.8.5", "date-fns": "^4.1.0", + "dompurify": "^3.1.7", "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", - "langchain": "^0.2.16", "marked": "^14.0.0", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", @@ -1434,106 +1433,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@langchain/core": { - "version": "0.2.28", - "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.2.28.tgz", - "integrity": "sha512-xN3+UdfxFaBcm29auMHFHGEYRh+3HwBc/dICHtwfk2wTSmw4HzWmBtZMx3BG+TOgh5Et7+mT6eF6E3omDLfk+A==", - "dependencies": { - "ansi-styles": "^5.0.0", - "camelcase": "6", - "decamelize": "1.2.0", - "js-tiktoken": "^1.0.12", - "langsmith": "^0.1.43", - "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" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@langchain/core/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@langchain/core/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@langchain/core/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@langchain/google-genai": { - "version": "0.0.26", - "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-0.0.26.tgz", - "integrity": "sha512-XGrnnWGifoEi/WOU8rl2a1e22go58ILEeLvkrt09/tSaUN7nJoVSBF7Hug9+8SuKup1DELI3+sLcHDzfMxCLuw==", - "dependencies": { - "@google/generative-ai": "^0.7.0", - "@langchain/core": ">=0.2.21 <0.3.0", - "zod-to-json-schema": "^3.22.4" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@langchain/google-genai/node_modules/@google/generative-ai": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.7.1.tgz", - "integrity": "sha512-WTjMLLYL/xfA5BW6xAycRPiAX7FNHKAxrid/ayqC1QMam0KAK0NbMeS9Lubw80gVg5xFMLE+H7pw4wdNzTOlxw==", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@langchain/openai": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@langchain/openai/-/openai-0.2.7.tgz", - "integrity": "sha512-f2XDXbExJf4SYsy17QSiq0YY/UWJXhJwoiS8uRi/gBa20zBQ8+bBFRnb9vPdLkOkGiaTy+yXZVFro3a9iW2r3w==", - "dependencies": { - "@langchain/core": ">=0.2.26 <0.3.0", - "js-tiktoken": "^1.0.12", - "openai": "^4.55.0", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@langchain/textsplitters": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@langchain/textsplitters/-/textsplitters-0.0.3.tgz", - "integrity": "sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA==", - "dependencies": { - "@langchain/core": ">0.2.0 <0.3.0", - "js-tiktoken": "^1.0.12" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@mistralai/mistralai": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-0.5.0.tgz", @@ -2388,11 +2287,6 @@ "form-data": "^4.0.0" } }, - "node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==" - }, "node_modules/@types/semver": { "version": "7.5.8", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", @@ -2420,11 +2314,6 @@ "integrity": "sha512-ZFwX8cDhbz6jiv3JZdMVYq8SSWHOUchChPmRoMwdIu3lz89aCu/gVK9TdR1eeb0ARQ8+5rtjUKrk1UR8hh0dhQ==", "dev": true }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==" - }, "node_modules/@types/vscode": { "version": "1.92.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.92.0.tgz", @@ -3124,7 +3013,8 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true }, "node_modules/array-union": { "version": "2.1.0", @@ -3189,6 +3079,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "engines": { "node": ">=8" }, @@ -3227,7 +3118,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "devOptional": true + "dev": true }, "node_modules/bowser": { "version": "2.11.0", @@ -3408,6 +3299,7 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, "engines": { "node": ">=10" }, @@ -3465,7 +3357,7 @@ "version": "1.0.0-rc.12", "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", - "devOptional": true, + "dev": true, "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", @@ -3486,7 +3378,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "devOptional": true, + "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", @@ -3879,7 +3771,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "devOptional": true, + "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", @@ -3895,7 +3787,7 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "devOptional": true, + "dev": true, "engines": { "node": ">= 6" }, @@ -4059,7 +3951,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "devOptional": true, + "dev": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -4073,7 +3965,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -4085,7 +3977,7 @@ "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "devOptional": true, + "dev": true, "dependencies": { "domelementtype": "^2.3.0" }, @@ -4096,11 +3988,17 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.7.tgz", + "integrity": "sha512-VaTstWtsneJY8xzy7DekmYWEOZcmzIe3Qb3zPd4STve1OBTa+e+WmS1ITQec1fZYXI3HCsOZZiSMpG6oxoWMWQ==", + "license": "(MPL-2.0 OR Apache-2.0)" + }, "node_modules/domutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "devOptional": true, + "dev": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -4156,7 +4054,7 @@ "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.12" }, @@ -4498,11 +4396,6 @@ "node": ">=6" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" - }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -4947,7 +4840,7 @@ "version": "4.7.8", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "devOptional": true, + "dev": true, "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", @@ -4968,7 +4861,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "devOptional": true, + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -5101,7 +4994,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "devOptional": true, + "dev": true, "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { @@ -5176,7 +5069,7 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "devOptional": true, + "dev": true, "engines": { "node": ">= 4" } @@ -5539,18 +5432,11 @@ "integrity": "sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ==", "license": "BSD-3-Clause" }, - "node_modules/js-tiktoken": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.14.tgz", - "integrity": "sha512-Pk3l3WOgM9joguZY2k52+jH82RtABRgB5RdGFZNUGbOKGMVlNmafcPA3b0ITcCZPu1L9UclP1tne6aw7ZI4Myg==", - "dependencies": { - "base64-js": "^1.5.1" - } - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, "dependencies": { "argparse": "^2.0.1" }, @@ -5599,14 +5485,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", @@ -5666,318 +5544,6 @@ "url": "https://github.com/sindresorhus/ky?sponsor=1" } }, - "node_modules/langchain": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/langchain/-/langchain-0.2.16.tgz", - "integrity": "sha512-NaCl1jdxladaLd63VxGtVcbuypzTq14XSmQI6vBajAISJgz02Q1+wiAIPIC2wMfsKjBRoCSgNCJw3/7nxqUuoQ==", - "dependencies": { - "@langchain/core": ">=0.2.21 <0.3.0", - "@langchain/openai": ">=0.1.0 <0.3.0", - "@langchain/textsplitters": "~0.0.0", - "binary-extensions": "^2.2.0", - "js-tiktoken": "^1.0.12", - "js-yaml": "^4.1.0", - "jsonpointer": "^5.0.1", - "langsmith": "~0.1.40", - "openapi-types": "^12.1.3", - "p-retry": "4", - "uuid": "^10.0.0", - "yaml": "^2.2.1", - "zod": "^3.22.4", - "zod-to-json-schema": "^3.22.3" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@aws-sdk/client-s3": "*", - "@aws-sdk/client-sagemaker-runtime": "*", - "@aws-sdk/client-sfn": "*", - "@aws-sdk/credential-provider-node": "*", - "@azure/storage-blob": "*", - "@browserbasehq/sdk": "*", - "@gomomento/sdk": "*", - "@gomomento/sdk-core": "*", - "@gomomento/sdk-web": "^1.51.1", - "@langchain/anthropic": "*", - "@langchain/aws": "*", - "@langchain/cohere": "*", - "@langchain/community": "*", - "@langchain/google-genai": "*", - "@langchain/google-vertexai": "*", - "@langchain/groq": "*", - "@langchain/mistralai": "*", - "@langchain/ollama": "*", - "@mendable/firecrawl-js": "*", - "@notionhq/client": "*", - "@pinecone-database/pinecone": "*", - "@supabase/supabase-js": "*", - "@vercel/kv": "*", - "@xata.io/client": "*", - "apify-client": "*", - "assemblyai": "*", - "axios": "*", - "cheerio": "*", - "chromadb": "*", - "convex": "*", - "couchbase": "*", - "d3-dsv": "*", - "epub2": "*", - "fast-xml-parser": "*", - "handlebars": "^4.7.8", - "html-to-text": "*", - "ignore": "*", - "ioredis": "*", - "jsdom": "*", - "mammoth": "*", - "mongodb": "*", - "node-llama-cpp": "*", - "notion-to-md": "*", - "officeparser": "*", - "pdf-parse": "*", - "peggy": "^3.0.2", - "playwright": "*", - "puppeteer": "*", - "pyodide": "^0.24.1", - "redis": "*", - "sonix-speech-recognition": "*", - "srt-parser-2": "*", - "typeorm": "*", - "weaviate-ts-client": "*", - "web-auth-library": "*", - "ws": "*", - "youtube-transcript": "*", - "youtubei.js": "*" - }, - "peerDependenciesMeta": { - "@aws-sdk/client-s3": { - "optional": true - }, - "@aws-sdk/client-sagemaker-runtime": { - "optional": true - }, - "@aws-sdk/client-sfn": { - "optional": true - }, - "@aws-sdk/credential-provider-node": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@browserbasehq/sdk": { - "optional": true - }, - "@gomomento/sdk": { - "optional": true - }, - "@gomomento/sdk-core": { - "optional": true - }, - "@gomomento/sdk-web": { - "optional": true - }, - "@langchain/anthropic": { - "optional": true - }, - "@langchain/aws": { - "optional": true - }, - "@langchain/cohere": { - "optional": true - }, - "@langchain/community": { - "optional": true - }, - "@langchain/google-genai": { - "optional": true - }, - "@langchain/google-vertexai": { - "optional": true - }, - "@langchain/groq": { - "optional": true - }, - "@langchain/mistralai": { - "optional": true - }, - "@langchain/ollama": { - "optional": true - }, - "@mendable/firecrawl-js": { - "optional": true - }, - "@notionhq/client": { - "optional": true - }, - "@pinecone-database/pinecone": { - "optional": true - }, - "@supabase/supabase-js": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "@xata.io/client": { - "optional": true - }, - "apify-client": { - "optional": true - }, - "assemblyai": { - "optional": true - }, - "axios": { - "optional": true - }, - "cheerio": { - "optional": true - }, - "chromadb": { - "optional": true - }, - "convex": { - "optional": true - }, - "couchbase": { - "optional": true - }, - "d3-dsv": { - "optional": true - }, - "epub2": { - "optional": true - }, - "faiss-node": { - "optional": true - }, - "fast-xml-parser": { - "optional": true - }, - "handlebars": { - "optional": true - }, - "html-to-text": { - "optional": true - }, - "ignore": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "jsdom": { - "optional": true - }, - "mammoth": { - "optional": true - }, - "mongodb": { - "optional": true - }, - "node-llama-cpp": { - "optional": true - }, - "notion-to-md": { - "optional": true - }, - "officeparser": { - "optional": true - }, - "pdf-parse": { - "optional": true - }, - "peggy": { - "optional": true - }, - "playwright": { - "optional": true - }, - "puppeteer": { - "optional": true - }, - "pyodide": { - "optional": true - }, - "redis": { - "optional": true - }, - "sonix-speech-recognition": { - "optional": true - }, - "srt-parser-2": { - "optional": true - }, - "typeorm": { - "optional": true - }, - "weaviate-ts-client": { - "optional": true - }, - "web-auth-library": { - "optional": true - }, - "ws": { - "optional": true - }, - "youtube-transcript": { - "optional": true - }, - "youtubei.js": { - "optional": true - } - } - }, - "node_modules/langchain/node_modules/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/langsmith": { - "version": "0.1.45", - "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.1.45.tgz", - "integrity": "sha512-ufgPYOTsZDYIYCStHx8gGJX5jLbk1GhFDxEDyycCicPFxlG40et53J9tYdaZ+MTBUCMexs9eKXCmvxBaUt9wrg==", - "dependencies": { - "@types/uuid": "^9.0.1", - "commander": "^10.0.1", - "p-queue": "^6.6.2", - "p-retry": "4", - "semver": "^7.6.3", - "uuid": "^9.0.0" - }, - "peerDependencies": { - "@langchain/core": "*", - "langchain": "*", - "openai": "*" - }, - "peerDependenciesMeta": { - "@langchain/core": { - "optional": true - }, - "langchain": { - "optional": true - }, - "openai": { - "optional": true - } - } - }, - "node_modules/langsmith/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "engines": { - "node": ">=14" - } - }, "node_modules/legacy-swc-helpers": { "name": "@swc/helpers", "version": "0.4.14", @@ -6591,14 +6157,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "node_modules/mustache": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", - "bin": { - "mustache": "bin/mustache" - } - }, "node_modules/mute-stream": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", @@ -6732,7 +6290,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "devOptional": true, + "dev": true, "dependencies": { "boolbase": "^1.0.0" }, @@ -6814,11 +6372,6 @@ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "license": "MIT" }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==" - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -6957,14 +6510,6 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "engines": { - "node": ">=4" - } - }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6995,44 +6540,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-queue": { - "version": "6.6.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", - "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", - "dependencies": { - "eventemitter3": "^4.0.4", - "p-timeout": "^3.2.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dependencies": { - "p-finally": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -7082,7 +6589,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "devOptional": true, + "dev": true, "dependencies": { "entities": "^4.4.0" }, @@ -7094,7 +6601,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", - "devOptional": true, + "dev": true, "dependencies": { "domhandler": "^5.0.2", "parse5": "^7.0.0" @@ -7569,14 +7076,6 @@ "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==" }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -9313,17 +8812,6 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, - "node_modules/yaml": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.0.tgz", - "integrity": "sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -9439,17 +8927,11 @@ "version": "3.23.8", "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "optional": true, + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/zod-to-json-schema": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.23.2.tgz", - "integrity": "sha512-uSt90Gzc/tUfyNqxnjlfBs8W6WSGpNBv0rVsNxP/BVSMHMKGdthPYff4xtCHYloJGM0CFxFsb3NbC0eqPhfImw==", - "peerDependencies": { - "zod": "^3.23.3" - } } } } diff --git a/package.json b/package.json index 59b09cbd..16e0248a 100644 --- a/package.json +++ b/package.json @@ -530,6 +530,7 @@ "axios": "^1.7.7", "dagre": "^0.8.5", "date-fns": "^4.1.0", + "dompurify": "^3.1.7", "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index e39e56aa..394390c3 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -423,5 +423,6 @@

    ZenML Chat

    + diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index e016616d..fbe2c62b 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -148,10 +148,13 @@ currentAssistantMessage += text; + const html = marked.parse(currentAssistantMessage); + const sanitizeHtml = DOMPurify.sanitize(html); + requestAnimationFrame(() => { messageDiv.innerHTML = `

    ZenML Assistant

    - ${marked.parse(currentAssistantMessage)} + ${sanitizeHtml} `; chatMessages.scrollTop = chatMessages.scrollHeight; }); @@ -187,7 +190,8 @@ const message = event.data; switch (message.command) { case 'updateChatLog': - document.getElementById('chatMessages').innerHTML = message.chatLogHtml; + const sanitizeHtml = DOMPurify.sanitize(message.chatLogHtml); + document.getElementById('chatMessages').innerHTML = sanitizeHtml; addCopyButtonsToAssistantMessages(); // For the potential view refresh command break; case 'receiveMessage': { @@ -202,7 +206,10 @@ break; } case 'showInfo': { - vscode.window.showInformationMessage({ command: 'showInfoToExtension', text: message.text }); + vscode.window.showInformationMessage({ + command: 'showInfoToExtension', + text: message.text, + }); break; } case 'updateModelList': { From 13a0ff5e89ec580a055e7a3dbb2502131870bddc Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 20:47:18 -0400 Subject: [PATCH 109/138] feat: Replace CDN dependencies with local files for Marked.js and DOMPurify --- package-lock.json | 578 +++++++++++++++++++++++------ package.json | 3 +- resources/chat-view/chat.html | 23 +- src/views/chatView/chatRenderer.ts | 10 + 4 files changed, 504 insertions(+), 110 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6f508638..6be14deb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,10 @@ "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", - "marked": "^14.0.0", + "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", + "tailwindcss": "^3.4.13", "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" }, @@ -63,6 +64,18 @@ "node": ">=0.10.0" } }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@anthropic-ai/sdk": { "version": "0.24.3", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.24.3.tgz", @@ -1142,7 +1155,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -1305,7 +1318,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1322,7 +1334,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -1334,7 +1345,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1359,7 +1369,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1373,7 +1382,6 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1383,7 +1391,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1392,7 +1399,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "engines": { "node": ">=6.0.0" } @@ -1420,14 +1426,13 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, + "devOptional": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1446,7 +1451,6 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1459,7 +1463,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "engines": { "node": ">= 8" } @@ -1468,7 +1471,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1481,7 +1483,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "optional": true, "engines": { "node": ">=14" @@ -2187,25 +2188,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true + "devOptional": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true + "devOptional": true }, "node_modules/@types/dagre": { "version": "0.7.52", @@ -2875,7 +2876,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "dev": true, + "devOptional": true, "bin": { "acorn": "bin/acorn" }, @@ -2906,7 +2907,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.4.0" } @@ -2974,7 +2975,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2991,11 +2991,16 @@ "node": ">=4" } }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3008,7 +3013,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true + "devOptional": true }, "node_modules/argparse": { "version": "2.0.1", @@ -3079,7 +3084,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "engines": { "node": ">=8" }, @@ -3138,7 +3142,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -3307,6 +3310,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001605", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", @@ -3395,7 +3407,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3751,13 +3762,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true + "devOptional": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3795,6 +3805,18 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/dagre": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", @@ -3914,6 +3936,12 @@ "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==" }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -3935,6 +3963,12 @@ "node": ">=8" } }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -4011,8 +4045,7 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, "node_modules/electron-to-chromium": { "version": "1.4.725", @@ -4023,8 +4056,7 @@ "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, "node_modules/end-of-stream": { "version": "1.4.4", @@ -4423,7 +4455,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4482,7 +4513,6 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -4512,7 +4542,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4610,7 +4639,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -4735,7 +4763,6 @@ "version": "10.3.12", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", - "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.6", @@ -4757,7 +4784,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5173,7 +5199,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -5185,7 +5210,6 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -5197,7 +5221,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5206,7 +5229,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -5215,7 +5237,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -5240,7 +5261,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -5296,8 +5316,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -5374,7 +5393,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5426,6 +5444,15 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/jiti": { + "version": "1.21.6", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", + "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, "node_modules/js-base64": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.2.tgz", @@ -5584,6 +5611,21 @@ "immediate": "~3.0.5" } }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", @@ -5752,7 +5794,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true + "devOptional": true }, "node_modules/markdown-it": { "version": "12.3.2", @@ -5780,9 +5822,9 @@ } }, "node_modules/marked": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz", - "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz", + "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -5807,7 +5849,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "engines": { "node": ">= 8" } @@ -5816,7 +5857,6 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -5883,7 +5923,6 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5906,7 +5945,6 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -6163,6 +6201,17 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, "node_modules/nanoid": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", @@ -6281,7 +6330,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6298,6 +6346,24 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-inspect": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", @@ -6632,7 +6698,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -6640,14 +6705,12 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", - "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6663,7 +6726,6 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, "engines": { "node": "14 || >=16.14" } @@ -6690,16 +6752,15 @@ "dev": true }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -6707,6 +6768,24 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -6771,6 +6850,179 @@ "node": ">=8" } }, + "node_modules/postcss": { + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -6889,7 +7141,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -6952,6 +7203,15 @@ "node": ">=0.8" } }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -6971,7 +7231,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -7004,7 +7263,6 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -7080,7 +7338,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7147,7 +7404,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -7253,7 +7509,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7265,7 +7520,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -7291,7 +7545,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "engines": { "node": ">=14" }, @@ -7412,6 +7665,15 @@ "node": ">= 8" } }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -7512,7 +7774,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -7530,7 +7791,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7543,14 +7803,12 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -7562,7 +7820,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7577,7 +7834,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7590,7 +7846,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7616,6 +7871,37 @@ "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", "license": "MIT" }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/sucrase/node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/supports-color": { "version": "9.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", @@ -7632,7 +7918,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -7659,6 +7944,61 @@ "url": "https://github.com/sponsors/Fuzzyma" } }, + "node_modules/tailwindcss": { + "version": "3.4.13", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", + "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.0", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/tailwindcss/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -7857,6 +8197,27 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -7875,7 +8236,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -7987,6 +8347,12 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, "node_modules/ts-loader": { "version": "9.5.1", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", @@ -8081,7 +8447,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, + "devOptional": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -8124,7 +8490,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.3.1" } @@ -8204,7 +8570,7 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", - "dev": true, + "devOptional": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8322,8 +8688,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/uuid": { "version": "9.0.1", @@ -8342,7 +8707,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true + "devOptional": true }, "node_modules/v8-to-istanbul": { "version": "9.3.0", @@ -8613,7 +8978,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -8646,7 +9010,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -8664,7 +9027,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -8681,7 +9043,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8696,7 +9057,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -8707,20 +9067,17 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -8734,7 +9091,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, "engines": { "node": ">=12" }, @@ -8746,7 +9102,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "engines": { "node": ">=12" }, @@ -8758,7 +9113,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -8812,6 +9166,18 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yaml": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", + "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -8906,7 +9272,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, + "devOptional": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 16e0248a..42d834ef 100644 --- a/package.json +++ b/package.json @@ -534,9 +534,10 @@ "fs-extra": "^11.2.0", "hbs": "^4.2.0", "ky": "^1.7.1", - "marked": "^14.0.0", + "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", + "tailwindcss": "^3.4.13", "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" } diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 394390c3..4210c297 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -15,9 +15,8 @@ - ZenML Chat - + ZenML Chat ZenML Chat
    + + + - + diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 53e47767..c2e64df7 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -41,12 +41,22 @@ export function getWebviewContent( vscode.Uri.joinPath(extensionUri, 'resources', 'chat-view', 'chat.js') ); + const markedUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'node_modules', 'marked', 'marked.min.js') + ); + + const purifyUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'node_modules', 'dompurify', 'dist', 'purify.min.js') + ); + const chatLogHtml = renderChatLog(messages); let treeItemHtml = getTreeHtml(); let providerDropdownHtml = getProviderDropdownHtml(currentProvider, availableProviders); let modelDropdownHtml = getModelDropdownHtml(availableModels); html = html.replace('${jsUri}', jsUri.toString()); + html = html.replace('${markedUri}', markedUri.toString()); + html = html.replace('${purifyUri}', purifyUri.toString()); html = html.replace('${treeItemHtml}', treeItemHtml); html = html.replace('${providerDropdownHtml}', providerDropdownHtml); html = html.replace('${modelDropdownHtml}', modelDropdownHtml); From 32dccd340b730798d7e3cb75db11f9db2b46fa3e Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Thu, 26 Sep 2024 21:34:03 -0400 Subject: [PATCH 110/138] feat: Integrate Tailwind CSS and remove CDN Replaced CDN styles with Tailwind CSS. Configured build process and updated paths to ensure proper loading in the VSCode extension. --- noxfile.py | 9 + package-lock.json | 277 +++++++++++++++++++++++------ package.json | 4 +- postcss.config.js | 6 + resources/chat-view/chat.html | 265 +-------------------------- resources/chat-view/styles.css | 250 ++++++++++++++++++++++++++ src/views/chatView/chatRenderer.ts | 5 + tailwind.config.js | 8 + 8 files changed, 509 insertions(+), 315 deletions(-) create mode 100644 postcss.config.js create mode 100644 resources/chat-view/styles.css create mode 100644 tailwind.config.js diff --git a/noxfile.py b/noxfile.py index ed856c66..3173c7bc 100644 --- a/noxfile.py +++ b/noxfile.py @@ -177,3 +177,12 @@ def update_packages(session: nox.Session) -> None: session.install("wheel", "pip-tools") _update_pip_packages(session) _update_npm_packages(session) + +@nox.session +def build_css(session): + session.install('tailwindcss') + session.run('npx', 'tailwindcss', '-i', './resources/chat-view/styles.css', '-o', './dist/styles.css', '--watch', external=True) + +@nox.session +def dev(session): + session.run('nox', '-s', 'build_css', external=True) \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 6be14deb..f9467a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,6 @@ "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", - "tailwindcss": "^3.4.13", "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" }, @@ -41,10 +40,13 @@ "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^2.24.0", + "autoprefixer": "^10.4.20", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", + "postcss": "^8.4.47", "prettier": "^3.2.5", "sinon": "^17.0.1", + "tailwindcss": "^3.4.13", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "typescript": "^5.3.3", @@ -68,6 +70,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1155,7 +1158,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/trace-mapping": "0.3.9" }, @@ -1318,6 +1321,7 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -1334,6 +1338,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -1345,6 +1350,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -1369,6 +1375,7 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -1382,6 +1389,7 @@ "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -1391,6 +1399,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1399,6 +1408,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -1426,13 +1436,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "devOptional": true, + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" @@ -1451,6 +1462,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -1463,6 +1475,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "engines": { "node": ">= 8" } @@ -1471,6 +1484,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -1483,6 +1497,7 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, "optional": true, "engines": { "node": ">=14" @@ -2188,25 +2203,25 @@ "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node12": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node14": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "devOptional": true + "dev": true }, "node_modules/@tsconfig/node16": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "devOptional": true + "dev": true }, "node_modules/@types/dagre": { "version": "0.7.52", @@ -2876,7 +2891,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", - "devOptional": true, + "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -2907,7 +2922,7 @@ "version": "8.3.2", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.4.0" } @@ -2975,6 +2990,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -2995,12 +3011,14 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3013,7 +3031,7 @@ "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "devOptional": true + "dev": true }, "node_modules/argparse": { "version": "2.0.1", @@ -3035,6 +3053,44 @@ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/axios": { "version": "1.7.7", "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", @@ -3084,6 +3140,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "engines": { "node": ">=8" }, @@ -3142,6 +3199,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "dependencies": { "fill-range": "^7.1.1" }, @@ -3164,9 +3222,9 @@ "dev": true }, "node_modules/browserslist": { - "version": "4.23.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz", + "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==", "dev": true, "funding": [ { @@ -3182,11 +3240,12 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001587", - "electron-to-chromium": "^1.4.668", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.0.13" + "caniuse-lite": "^1.0.30001663", + "electron-to-chromium": "^1.5.28", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, "bin": { "browserslist": "cli.js" @@ -3314,15 +3373,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001605", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001605.tgz", - "integrity": "sha512-nXwGlFWo34uliI9z3n6Qc0wZaf7zaZWA1CPZ169La5mV3I/gem7bst0vr5XQH5TJXZIMfDeZyOrZnSlVzKxxHQ==", + "version": "1.0.30001664", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001664.tgz", + "integrity": "sha512-AmE7k4dXiNKQipgn7a2xg558IRqPN3jMQY/rOsbxDhrd0tyChwbITBfiwtnqz8bi2M5mIWbxAYBvk7W7QBUS2g==", "dev": true, "funding": [ { @@ -3337,7 +3397,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", @@ -3407,6 +3468,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3762,12 +3824,13 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "devOptional": true + "dev": true }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3809,6 +3872,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -3940,6 +4004,7 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/diff": { @@ -3967,6 +4032,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, "license": "MIT" }, "node_modules/doctrine": { @@ -4045,18 +4111,21 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.725", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.725.tgz", - "integrity": "sha512-OGkMXLY7XH6ykHE5ZOVVIMHaGAvvxqw98cswTKB683dntBJre7ufm9wouJ0ExDm0VXhHenU8mREvxIbV5nNoVQ==", - "dev": true + "version": "1.5.29", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.29.tgz", + "integrity": "sha512-PF8n2AlIhCKXQ+gTpiJi0VhcHDb69kYX4MtCiivctc2QD3XuNZ/XIOlbGzt7WAjjEev0TtaH6Cu3arZExm5DOw==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true }, "node_modules/end-of-stream": { "version": "1.4.4", @@ -4455,6 +4524,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -4513,6 +4583,7 @@ "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "dependencies": { "reusify": "^1.0.4" } @@ -4542,6 +4613,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4639,6 +4711,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -4691,6 +4764,20 @@ "node": ">= 14" } }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -4763,6 +4850,7 @@ "version": "10.3.12", "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^2.3.6", @@ -4784,6 +4872,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -5199,6 +5288,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -5210,6 +5300,7 @@ "version": "2.13.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, "dependencies": { "hasown": "^2.0.0" }, @@ -5221,6 +5312,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -5229,6 +5321,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -5237,6 +5330,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -5261,6 +5355,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "engines": { "node": ">=0.12.0" } @@ -5316,7 +5411,8 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true }, "node_modules/isobject": { "version": "3.0.1", @@ -5393,6 +5489,7 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -5448,6 +5545,7 @@ "version": "1.21.6", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "dev": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -5615,6 +5713,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -5624,6 +5723,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/linkify-it": { @@ -5794,7 +5894,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "devOptional": true + "dev": true }, "node_modules/markdown-it": { "version": "12.3.2", @@ -5849,6 +5949,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "engines": { "node": ">= 8" } @@ -5857,6 +5958,7 @@ "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, "dependencies": { "braces": "^3.0.2", "picomatch": "^2.3.1" @@ -5923,6 +6025,7 @@ "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -5945,6 +6048,7 @@ "version": "7.0.4", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -6205,6 +6309,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -6321,15 +6426,27 @@ } }, "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6350,6 +6467,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6359,6 +6477,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -6698,6 +6817,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -6705,12 +6825,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true }, "node_modules/path-scurry": { "version": "1.10.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -6726,6 +6848,7 @@ "version": "10.2.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, "engines": { "node": "14 || >=16.14" } @@ -6755,12 +6878,14 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -6772,6 +6897,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -6781,6 +6907,7 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -6854,6 +6981,7 @@ "version": "8.4.47", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6882,6 +7010,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -6899,6 +7028,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -6918,6 +7048,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6953,6 +7084,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -6965,6 +7097,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6990,6 +7123,7 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -7003,12 +7137,14 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, "license": "MIT" }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, "funding": [ { "type": "github", @@ -7141,6 +7277,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -7207,6 +7344,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -7231,6 +7369,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -7263,6 +7402,7 @@ "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, "dependencies": { "is-core-module": "^2.13.0", "path-parse": "^1.0.7", @@ -7338,6 +7478,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7404,6 +7545,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -7509,6 +7651,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7520,6 +7663,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -7545,6 +7689,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { "node": ">=14" }, @@ -7669,6 +7814,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -7774,6 +7920,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -7791,6 +7938,7 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -7803,12 +7951,14 @@ "node_modules/string-width-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/string-width/node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -7820,6 +7970,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7834,6 +7985,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7846,6 +7998,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -7875,6 +8028,7 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -7897,6 +8051,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -7918,6 +8073,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "engines": { "node": ">= 0.4" }, @@ -7948,6 +8104,7 @@ "version": "3.4.13", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.13.tgz", "integrity": "sha512-KqjHOJKogOUt5Bs752ykCeiwvi0fKVkr5oqsFNt/8px/tA8scFPIlkygsf6jXrfCqGHz7VflA6+yytWuM+XhFw==", + "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -7985,12 +8142,14 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, "license": "MIT" }, "node_modules/tailwindcss/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -8201,6 +8360,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -8210,6 +8370,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -8236,6 +8397,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -8351,6 +8513,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, "license": "Apache-2.0" }, "node_modules/ts-loader": { @@ -8447,7 +8610,7 @@ "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "devOptional": true, + "dev": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -8490,7 +8653,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.3.1" } @@ -8570,7 +8733,7 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz", "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==", - "devOptional": true, + "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8642,9 +8805,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, "funding": [ { @@ -8660,9 +8823,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.1.2", + "picocolors": "^1.0.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -8688,7 +8852,8 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true }, "node_modules/uuid": { "version": "9.0.1", @@ -8707,7 +8872,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "devOptional": true + "dev": true }, "node_modules/v8-to-istanbul": { "version": "9.3.0", @@ -8978,6 +9143,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -9010,6 +9176,7 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -9027,6 +9194,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -9043,6 +9211,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -9057,6 +9226,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -9067,17 +9237,20 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -9091,6 +9264,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, "engines": { "node": ">=12" }, @@ -9102,6 +9276,7 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, "engines": { "node": ">=12" }, @@ -9113,6 +9288,7 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -9170,6 +9346,7 @@ "version": "2.5.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz", "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==", + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -9272,7 +9449,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "devOptional": true, + "dev": true, "engines": { "node": ">=6" } diff --git a/package.json b/package.json index 42d834ef..8aa4001e 100644 --- a/package.json +++ b/package.json @@ -514,10 +514,13 @@ "@vscode/test-cli": "^0.0.10", "@vscode/test-electron": "^2.4.1", "@vscode/vsce": "^2.24.0", + "autoprefixer": "^10.4.20", "eslint": "^8.56.0", "eslint-config-prettier": "^9.1.0", + "postcss": "^8.4.47", "prettier": "^3.2.5", "sinon": "^17.0.1", + "tailwindcss": "^3.4.13", "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "typescript": "^5.3.3", @@ -537,7 +540,6 @@ "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", - "tailwindcss": "^3.4.13", "token.js": "^0.4.3", "vscode-languageclient": "^9.0.1" } diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..304aae66 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + } +}; \ No newline at end of file diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 4210c297..69e3b130 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -15,275 +15,12 @@ - ZenML Chat + - -
    diff --git a/resources/chat-view/styles.css b/resources/chat-view/styles.css new file mode 100644 index 00000000..e7b680df --- /dev/null +++ b/resources/chat-view/styles.css @@ -0,0 +1,250 @@ +/* resources/chat-view/styles.css */ +@tailwind base; +@tailwind components; +@tailwind utilities; + +body { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + } + + ul { + color: black !important; + } + + form { + display: contents; + } + + #chatForm { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 0.375rem 0.375rem 0 0; + } + + #chatMessages { + color: var(--vscode-editor-foreground); + } + + #messageInput { + color: var(--vscode-input-foreground); + background-color: var(--vscode-input-background); + } + + .rounded-lg { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + } + + .rounded-lg:hover { + background-color: var(--vscode-button-hoverBackground); + } + + .hide { + display: none; + } + + .tree-view { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + width: 256px; + max-height: 300px; + overflow: auto; + border: 2px var(--vscode-editor-foreground) solid; + border-radius: 0.375rem; + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + } + + .tree-item { + display: flex; + align-items: center; + cursor: pointer; + } + + .tree-item:hover { + background-color: var(--vscode-list-background); + } + + .tree-item-content { + display: flex; + align-items: center; + flex-grow: 1; + padding: 2px 8px; + } + + .tree-item-icon { + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; + } + + .tree-item-name { + font-size: 14px; + color: var(--vscode-editor-foreground); + margin-left: 4px; + flex-grow: 1; + } + + .tree-item-children { + display: none; + width: 100%; + } + + .tree-item-children.open { + display: block; + } + + .tree-item-wrapper { + display: flex; + flex-direction: column; + width: 100%; + } + + .tree-item-checkbox { + width: 16px; + height: 16px; + margin-left: 8px; + } + + .chatbar-options { + display: flex; + justify-content: flex-start; + align-items: center; + padding: 0.5rem; + background-color: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-top: none; + border-radius: 0 0 0.375rem 0.375rem; + } + + .model-dropdown { + font-size: 0.75rem; + padding: 0.25rem; + background-color: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 0.25rem; + margin-right: 0.5rem; + } + + .context-button { + font-size: 0.75rem; + padding: 0.25rem; + background-color: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); + border: none; + border-radius: 0.25rem; + cursor: pointer; + margin-right: 0.5rem; + } + + .context-button:hover { + background-color: var(--vscode-button-hoverBackground); + } + + .context-dropdown { + position: absolute; + left: 0; + right: 0; + bottom: 100%; + margin-bottom: 0.25rem; + z-index: 10; + } + + #chatMessages h1, + #chatMessages h2, + #chatMessages h3, + #chatMessages h4, + #chatMessages h5, + #chatMessages h6 { + color: var(--vscode-editor-foreground); + font-weight: bold; + } + + #chatMessages p { + color: var(--vscode-editor-foreground); + } + + #chatMessages ul, + #chatMessages ol { + color: var(--vscode-editor-foreground); + } + + #chatMessages li { + color: var(--vscode-editor-foreground); + } + + #chatMessages pre { + padding: 1em; + border-radius: 5px; + } + + #chatMessages code { + padding: 0.2em 0.4em; + border-radius: 3px; + } + + #chatMessages pre code { + display: block; + background-color: var(--vscode-textCodeBlock-background); + color: var(--vscode-editor-foreground); + padding: 1em; + border-radius: 5px; + } + + #chatMessages a { + color: var(--vscode-textLink-foreground); + text-decoration: none; + } + + #chatMessages strong { + font-weight: bold; + } + + #chatMessages em { + font-style: italic; + } + + .copy-button { + position: absolute; + bottom: 8px; + right: 8px; + background-color: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-radius: 4px; + padding: 4px 8px; + font-size: 12px; + cursor: pointer; + } + + .copy-button:hover { + background-color: var(--vscode-button-hoverBackground); + } + + .assistant { + background-color: var(--vscode-input-background); + position: relative; + } + + .expand span { + color: var(--vscode-textLink-foreground); + } + + .loader { + width: 50px; + aspect-ratio: 1; + border-radius: 50%; + border: 8px solid; + border-color: var(--vscode-input-background) var(--vscode-input-foreground); + animation: l1 1s infinite; + } + @keyframes l1 { + to { + transform: rotate(0.5turn); + } + } \ No newline at end of file diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index c2e64df7..45b5f56c 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -49,6 +49,10 @@ export function getWebviewContent( vscode.Uri.joinPath(extensionUri, 'node_modules', 'dompurify', 'dist', 'purify.min.js') ); + const stylesUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'dist', 'styles.css') + ); + const chatLogHtml = renderChatLog(messages); let treeItemHtml = getTreeHtml(); let providerDropdownHtml = getProviderDropdownHtml(currentProvider, availableProviders); @@ -57,6 +61,7 @@ export function getWebviewContent( html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${markedUri}', markedUri.toString()); html = html.replace('${purifyUri}', purifyUri.toString()); + html = html.replace('${stylesUri}', stylesUri.toString()); html = html.replace('${treeItemHtml}', treeItemHtml); html = html.replace('${providerDropdownHtml}', providerDropdownHtml); html = html.replace('${modelDropdownHtml}', modelDropdownHtml); diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..c8e41dd3 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./resources/chat-view/**/*.{html,js}"], + theme: { + extend: {}, + }, + plugins: [], +}; From 4acb5e429f667e954492270d6b940bfbb7758109 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 14:37:52 -0400 Subject: [PATCH 111/138] fix: Handle dynamic pipeline run context in addContext function - Added a check to handle context strings starting with 'Pipeline Run:'. - Expanded ContextType union to include string for dynamic context handling. - Retained switch case for other context types and added a default case to log unknown context types. This ensures that pipeline run contexts are correctly parsed and processed, preventing unknown context type warnings in the console. --- src/views/chatView/utils/ContextUtils.ts | 77 ++++++++++++------------ 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 54c8c1b2..0ff68215 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -22,45 +22,48 @@ import { getPipelineData } from './PipelineUtils'; import { ComponentDataProvider } from '../../activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../../activityBar/environmentView/EnvironmentDataProvider'; -export async function addContext(requestedContext: string[]): Promise { +type ContextType = 'serverContext' | 'environmentContext' | 'pipelineContext' | 'stackContext' | 'stackComponentsContext' | 'logsContext' | 'pipelineRunContext' | string; + +export async function addContext(requestedContext: ContextType[]): Promise { let systemMessage = 'Context:\n'; for (let context of requestedContext) { - switch (context) { - case 'serverContext': - systemMessage += getServerData(); - break; - case 'environmentContext': - systemMessage += await getEnvironmentData(); - break; - case 'pipelineContext': - systemMessage += getPipelineData().contextString; - systemMessage += '\n A pipeline is a series of steps in a machine learning workflow.'; - break; - case 'stackContext': - systemMessage += getStackData(); - break; - case 'stackComponentsContext': - systemMessage += getStackComponentData(); - break; - case 'logsContext': - systemMessage += await getLogData(); - break; - default: - if (context.includes('Pipeline Run:')) { - try { - let runData = JSON.parse(context.replace('Pipeline Run:', '')); - let logs = await getPipelineRunLogs(runData.id); - let nodeData = await getPipelineRunNodes('step', runData.id); - systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; - systemMessage += `Logs: ${logs}\n`; - systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; - } catch (error) { - console.error('Failed to parse pipeline run data from context:', error); - systemMessage += 'Failed to parse pipeline run data from context.\n'; - } - - } - break; + if (context.startsWith('Pipeline Run:')) { + try { + let runData = JSON.parse(context.replace('Pipeline Run:', '')); + let logs = await getPipelineRunLogs(runData.id); + let nodeData = await getPipelineRunNodes('step', runData.id); + systemMessage += `Pipeline Run: ${JSON.stringify(runData)}\n`; + systemMessage += `Logs: ${logs}\n`; + systemMessage += `Step Data: ${JSON.stringify(nodeData)}\n`; + } catch (error) { + console.error('Failed to parse pipeline run data from context:', error); + systemMessage += 'Failed to parse pipeline run data from context.\n'; + } + } else { + switch (context) { + case 'serverContext': + systemMessage += getServerData(); + break; + case 'environmentContext': + systemMessage += await getEnvironmentData(); + break; + case 'pipelineContext': + systemMessage += getPipelineData().contextString; + systemMessage += '\n A pipeline is a series of steps in a machine learning workflow.'; + break; + case 'stackContext': + systemMessage += getStackData(); + break; + case 'stackComponentsContext': + systemMessage += getStackComponentData(); + break; + case 'logsContext': + systemMessage += await getLogData(); + break; + default: + console.warn(`Unknown context type: ${context}`); + break; + } } } return systemMessage; From b0a065bd3f8d6943207138b081615898ea16b0d5 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 15:00:24 -0400 Subject: [PATCH 112/138] refactor: Enhance error handling and type safety in utility functions - Updated addContext function to handle dynamic pipeline run contexts and log unknown context types. - Added error handling to getServerData function to ensure it always returns a string, even on failure. - Enhanced getStackComponentData function with error handling and improved type safety using type guards. - Added error handling to getEnvironmentData function to handle potential promise rejections. - Improved getStackData function with error handling and safer property access using type guards. These changes improve the robustness and maintainability of the utility functions by ensuring they handle errors gracefully and enforce type safety. --- src/views/chatView/utils/ContextUtils.ts | 86 +++++++++++++++--------- 1 file changed, 56 insertions(+), 30 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 0ff68215..28d8cb4c 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -70,45 +70,71 @@ export async function addContext(requestedContext: ContextType[]): Promise { - if (item instanceof StackComponentTreeItem) { - let { name, type, flavor, id } = item.component; - let stackId = item.stackId; - let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; - let componentId = ` - Component ID: ${id}`; - return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; - } else { - return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; - } - }) - .join('\n'); - return `Stack Component Data:\n${componentData}\n`; + try { + let components = ComponentDataProvider.getInstance().items; + let componentData = components + .map((item: vscode.TreeItem) => { + if (item instanceof StackComponentTreeItem) { + let { name, type, flavor, id } = item.component; + let stackId = item.stackId; + let idInfo = stackId ? ` - Stack ID: ${stackId}` : ''; + let componentId = ` - Component ID: ${id}`; + return `Name: ${name}, Type: ${type}, Flavor: ${flavor}${componentId}${idInfo}`; + } else if (item.label) { + return `Label: ${item.label}, Description: ${item.description || 'N/A'}`; + } else { + return 'Unknown item type'; + } + }) + .join('\n'); + return `Stack Component Data:\n${componentData}\n`; + } catch (error) { + console.error('Failed to get stack component data:', error); + return 'Stack Component Data: Unable to retrieve\n'; + } } async function getEnvironmentData(): Promise { - let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); - let contextString = environmentData - .map(item => `${item.label}: ${item.description || ''}`) - .join('\n'); - return `Environment Data:\n${contextString}\n`; + try { + let environmentData = await EnvironmentDataProvider.getInstance().getChildren(); + let contextString = environmentData + .map(item => `${item.label}: ${item.description || ''}`) + .join('\n'); + return `Environment Data:\n${contextString}\n`; + } catch (error) { + console.error('Failed to get environment data:', error); + return 'Environment Data: Unable to retrieve\n'; + } } function getStackData(): string { - let stackData = StackDataProvider.getInstance().items; - let contextString = stackData - .map(item => { - let stackItem = item as vscode.TreeItem & { isActive: boolean; id: string }; - return `Name: ${item.label}\n` + `ID: ${stackItem.id}\n` + `Active: ${stackItem.isActive}`; - }) - .join('\n'); - return `Stack Data:\n${contextString}\n`; + try { + let stackData = StackDataProvider.getInstance().items; + let contextString = stackData + .map(item => { + if ('isActive' in item && 'id' in item) { + return `Name: ${item.label || 'N/A'}\n` + + `ID: ${item.id}\n` + + `Active: ${item.isActive}`; + } + return `Name: ${item.label || 'N/A'}`; + }) + .join('\n'); + return `Stack Data:\n${contextString}\n`; + } catch (error) { + console.error('Failed to get stack data:', error); + return 'Stack Data: Unable to retrieve\n'; + } } async function getLogData() { From bdad3ee447977a27b577731d742330009fe95603 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 15:22:33 -0400 Subject: [PATCH 113/138] refactor: Enhance error handling, type safety, and readability - Refactored getLogData: - Extracted fetchLogs function. - Improved error handling and logging. - Refactored getPipelineRunLogs: - Extracted common logic into fetchLogs. - Enhanced type annotations and error handling. - Refactored getPipelineRunNodes: - Added NodeType type. - Improved error handling and variable naming. - Extracted node filtering logic. These changes improve robustness, maintainability, and readability. --- src/views/chatView/utils/ContextUtils.ts | 202 ++++++++++++----------- 1 file changed, 107 insertions(+), 95 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 28d8cb4c..3395f814 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -62,6 +62,7 @@ export async function addContext(requestedContext: ContextType[]): Promise { + try { + const lsClient = LSClient.getInstance(); + const globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + const apiToken = globalConfig.store.api_token; + const dashboardUrl = globalConfig.store.url; - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } + const pipelineRunSteps = await getPipelineRunNodes('step'); - let pipelineRunSteps = await getPipelineRunNodes('step'); + if (!pipelineRunSteps?.[0]) { + console.warn('No pipeline run steps found.'); + return []; + } - if (pipelineRunSteps && pipelineRunSteps[0]) { - let logs = await Promise.all( - pipelineRunSteps[0].map(async step => { - if (step?.id) { - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${step.id}`, error); - } - } else { - console.warn('Encountered a null or invalid step.'); - } - }) - ); - return logs; - } else { - console.warn('No pipeline run steps found.'); + const fetchStepLogs = async (step: any) => { + if (!step?.id) { + console.warn('Encountered a null or invalid step.'); + return null; + } + try { + const response = await axios.get(`${dashboardUrl}/api/v1/steps/${step.id}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${step.id}`, error); + return null; + } + }; + + const logs = await Promise.all(pipelineRunSteps[0].map(fetchStepLogs)); + return logs.filter(log => log !== null); + } catch (error) { + console.error('Failed to retrieve log data:', error); return []; } } -async function getPipelineRunLogs(id: string) { - let lsClient = LSClient.getInstance(); +type Step = { + id: string; + [key: string]: any; +}; - let dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); +async function fetchLogs(stepId: string, apiToken: string, dashboardUrl: string): Promise { + try { + const response = await axios.get(`${dashboardUrl}/api/v1/steps/${stepId}/logs`, { + headers: { + Authorization: `Bearer ${apiToken}`, + accept: 'application/json', + }, + }); + return response.data; + } catch (error) { + console.error(`Failed to get logs for step with id ${stepId}`, error); + return null; + } +} - let stepData = await Promise.all( - dagData.nodes.map(async (node: DagArtifact | DagStep) => { - if (node.type === 'step') { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - }) - ); +async function getPipelineRunLogs(id: string): Promise { + try { + const lsClient = LSClient.getInstance(); + const dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); - stepData = stepData.filter(value => value !== null); + const stepData = (await Promise.all( + dagData.nodes.map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + )).filter((step): step is Step => step !== null); - let globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - let apiToken = globalConfig.store.api_token; - let dashboardUrl = globalConfig.store.url; + const globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + const apiToken = globalConfig.store.api_token; + const dashboardUrl = globalConfig.store.url; - if (!apiToken) { - throw new Error('API Token is not available in global configuration'); - } + if (!apiToken) { + throw new Error('API Token is not available in global configuration'); + } - let logs = await Promise.all( - stepData.map(async step => { - if (step && typeof step === 'object' && 'id' in step) { - let validStep = step as JsonObject; - try { - let response = await axios.get(`${dashboardUrl}/api/v1/steps/${validStep.id}/logs`, { - headers: { - Authorization: `Bearer ${apiToken}`, - accept: 'application/json', - }, - }); - return response.data; - } catch (error) { - console.error(`Failed to get logs for step with id ${validStep.id}`, error); - return null; - } - } - return null; - }) - ); - logs = logs.filter(log => log !== null); - return logs; + const logs = await Promise.all( + stepData.map(step => fetchLogs(step.id, apiToken, dashboardUrl)) + ); + + return logs.filter(log => log !== null); + } catch (error) { + console.error('Failed to retrieve pipeline run logs:', error); + return []; + } } -async function getPipelineRunNodes(type: string, id?: string) { - let pipelineRuns = PipelineDataProvider.getInstance().getPipelineData(); - let pipelineData; +type NodeType = 'all' | 'step' | 'artifact'; - if (id) { - pipelineData = pipelineRuns.filter(pipelineRun => pipelineRun.id === id); - } else { - pipelineData = pipelineRuns; - } +async function getPipelineRunNodes(nodeType: NodeType, pipelineRunId?: string): Promise { + try { + const pipelineDataProvider = PipelineDataProvider.getInstance(); + const allPipelineRuns = pipelineDataProvider.getPipelineData(); + const targetPipelineRuns = pipelineRunId + ? allPipelineRuns.filter(run => run.id === pipelineRunId) + : allPipelineRuns; - let lsClient = LSClient.getInstance(); - let dagData = await Promise.all( - pipelineData.map(async node => { - let dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [node.id]); - return dag; - }) - ); + const lsClient = LSClient.getInstance(); - let stepData = await Promise.all( - dagData.map(async (dag: PipelineRunDag) => { - let filteredNodes = await Promise.all( + const fetchAndFilterNodes = async (pipelineRun: { id: string }): Promise => { + const dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [pipelineRun.id]); + + const filteredNodes = await Promise.all( dag.nodes.map(async (node: DagArtifact | DagStep) => { - if (type === 'all' || node.type === type) { + if (nodeType === 'all' || node.type === nodeType) { return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); } return null; }) ); - return filteredNodes.filter(value => value !== null); - }) - ); - return stepData; + + return filteredNodes.filter((node): node is JsonObject => node !== null); + }; + + const pipelineNodesData = await Promise.all(targetPipelineRuns.map(fetchAndFilterNodes)); + return pipelineNodesData; + } catch (error) { + console.error('Failed to retrieve pipeline run nodes:', error); + return []; + } } From e32fe11ac09f30b8cbb4da24bf2838479ef48e89 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 15:57:12 -0400 Subject: [PATCH 114/138] fix: Improve compatibility and prevent scope leakage in chat.js - Replaced Event constructor with CustomEvent for better compatibility in VSCode webview environment. - Wrapped case body in braces to prevent variable hoisting issues in switch-case statements. --- resources/chat-view/chat.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 2070bc64..2edc9cae 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -107,9 +107,10 @@ window.addEventListener('message', event => { const message = event.data; switch (message.command) { - case 'updateChatLog': + case 'updateChatLog': { document.getElementById('chatMessages').innerHTML = message.chatLogHtml; break; + } } }); @@ -189,11 +190,12 @@ window.addEventListener('message', event => { const message = event.data; switch (message.command) { - case 'updateChatLog': + case 'updateChatLog': { const sanitizeHtml = DOMPurify.sanitize(message.chatLogHtml); document.getElementById('chatMessages').innerHTML = sanitizeHtml; addCopyButtonsToAssistantMessages(); // For the potential view refresh command break; + } case 'receiveMessage': { if (message.text === 'disableInput') { disableInput(); @@ -294,10 +296,11 @@ let message; switch (buttonValue) { - case 'aboutChat': + case 'aboutChat': { message = 'What can this chat do?'; break; - case 'summarizeStats': + } + case 'summarizeStats': { message = 'Generate a summary of my stats.'; context.push('serverContext'); context.push('environmentContext'); @@ -305,12 +308,15 @@ context.push('stackContext'); context.push('stackComponentsContext'); break; - case 'summarizeLogs': + } + case 'summarizeLogs': { message = 'Generate a summary of my logs.'; context.push('logsContext'); break; - default: + } + default: { break; + } } if (message) { @@ -443,7 +449,7 @@ e.preventDefault(); if (!isInputDisabled) { const form = document.getElementById('chatForm'); - const event = new Event('submit', { + const event = new CustomEvent('submit', { bubbles: true, cancelable: true, }); From dd89c85b8b2ef214e48b186011d48235cff804ec Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 16:06:48 -0400 Subject: [PATCH 115/138] chore: Refactor postcss.config.js for style adherence and maintainability --- postcss.config.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/postcss.config.js b/postcss.config.js index 304aae66..44fc2b97 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,8 @@ module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - } -}; \ No newline at end of file + plugins: [ + // Process Tailwind CSS classes + require('tailwindcss'), + // Add vendor prefixes to CSS rules + require('autoprefixer'), + ] + }; \ No newline at end of file From 9ecc7ba621fb1f600dceedb76a83383839feccab Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 16:12:38 -0400 Subject: [PATCH 116/138] chore: Enhance build_css function and remove redundant dev function - Added error handling, logging, and optional watch flag to build_css. - Removed the redundant dev function. --- noxfile.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/noxfile.py b/noxfile.py index 3173c7bc..b8e10aa3 100644 --- a/noxfile.py +++ b/noxfile.py @@ -179,10 +179,22 @@ def update_packages(session: nox.Session) -> None: _update_npm_packages(session) @nox.session -def build_css(session): - session.install('tailwindcss') - session.run('npx', 'tailwindcss', '-i', './resources/chat-view/styles.css', '-o', './dist/styles.css', '--watch', external=True) - -@nox.session -def dev(session): - session.run('nox', '-s', 'build_css', external=True) \ No newline at end of file +def build_css(session, watch=False): + try: + session.log("Installing tailwindcss...") + session.install('tailwindcss') + + session.log("Processing CSS...") + cmd = [ + 'npx', 'tailwindcss', + '-i', './resources/chat-view/styles.css', + '-o', './dist/styles.css' + ] + if watch: + cmd.append('--watch') + + session.run(*cmd, external=True) + session.log("CSS processing completed successfully.") + except Exception as e: + session.log(f"An error occurred during CSS processing: {str(e)}") + raise \ No newline at end of file From 43e6f1e6fa97bfb7d98f4e265c86893c6e410929 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Fri, 27 Sep 2024 16:25:01 -0400 Subject: [PATCH 117/138] refactor: Improve accessibility and maintainability of CSS - Replaced `display: contents;` with `display: flex;` for the `
    ` element to maintain accessibility. - Changed hardcoded `color: black !important;` to `color: var(--vscode-editor-foreground);` for `ul` elements to ensure theme consistency. - Removed redundant color properties from `#chatMessages` child elements to allow inheritance and simplify the CSS. --- resources/chat-view/styles.css | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/resources/chat-view/styles.css b/resources/chat-view/styles.css index e7b680df..7c4f99dd 100644 --- a/resources/chat-view/styles.css +++ b/resources/chat-view/styles.css @@ -9,11 +9,11 @@ body { } ul { - color: black !important; + color: var(--vscode-editor-foreground); } form { - display: contents; + display: flex; } #chatForm { @@ -161,23 +161,9 @@ body { #chatMessages h4, #chatMessages h5, #chatMessages h6 { - color: var(--vscode-editor-foreground); font-weight: bold; } - #chatMessages p { - color: var(--vscode-editor-foreground); - } - - #chatMessages ul, - #chatMessages ol { - color: var(--vscode-editor-foreground); - } - - #chatMessages li { - color: var(--vscode-editor-foreground); - } - #chatMessages pre { padding: 1em; border-radius: 5px; From b546d3f9774fc836e2c59b115acf378f9ea92ff6 Mon Sep 17 00:00:00 2001 From: Will Yennie Date: Mon, 30 Sep 2024 18:39:18 -0400 Subject: [PATCH 118/138] chore(noxfile): add TailwindCSS build step to setup session --- noxfile.py | 2 ++ requirements.txt | 62 ++++++++++++++++++++++++------------------------ 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/noxfile.py b/noxfile.py index b8e10aa3..90e0a2b4 100644 --- a/noxfile.py +++ b/noxfile.py @@ -121,6 +121,8 @@ def setup(session: nox.Session) -> None: _setup_template_environment(session) print(f"DEBUG – Virtual Environment Interpreter: {session.bin}/python") + build_css(session) + @nox.session() def tests(session: nox.Session) -> None: diff --git a/requirements.txt b/requirements.txt index 5d47682f..7e819441 100644 --- a/requirements.txt +++ b/requirements.txt @@ -99,35 +99,35 @@ typing-extensions==4.12.2 ; python_version < "3.11" \ # via # -r ./requirements.in # cattrs -watchdog==5.0.2 \ - --hash=sha256:14dd4ed023d79d1f670aa659f449bcd2733c33a35c8ffd88689d9d243885198b \ - --hash=sha256:29e4a2607bd407d9552c502d38b45a05ec26a8e40cc7e94db9bb48f861fa5abc \ - --hash=sha256:3960136b2b619510569b90f0cd96408591d6c251a75c97690f4553ca88889769 \ - --hash=sha256:3e8d5ff39f0a9968952cce548e8e08f849141a4fcc1290b1c17c032ba697b9d7 \ - --hash=sha256:53ed1bf71fcb8475dd0ef4912ab139c294c87b903724b6f4a8bd98e026862e6d \ - --hash=sha256:5597c051587f8757798216f2485e85eac583c3b343e9aa09127a3a6f82c65ee8 \ - --hash=sha256:638bcca3d5b1885c6ec47be67bf712b00a9ab3d4b22ec0881f4889ad870bc7e8 \ - --hash=sha256:6bec703ad90b35a848e05e1b40bf0050da7ca28ead7ac4be724ae5ac2653a1a0 \ - --hash=sha256:726eef8f8c634ac6584f86c9c53353a010d9f311f6c15a034f3800a7a891d941 \ - --hash=sha256:72990192cb63872c47d5e5fefe230a401b87fd59d257ee577d61c9e5564c62e5 \ - --hash=sha256:7d1aa7e4bb0f0c65a1a91ba37c10e19dabf7eaaa282c5787e51371f090748f4b \ - --hash=sha256:8c47150aa12f775e22efff1eee9f0f6beee542a7aa1a985c271b1997d340184f \ - --hash=sha256:901ee48c23f70193d1a7bc2d9ee297df66081dd5f46f0ca011be4f70dec80dab \ - --hash=sha256:963f7c4c91e3f51c998eeff1b3fb24a52a8a34da4f956e470f4b068bb47b78ee \ - --hash=sha256:9814adb768c23727a27792c77812cf4e2fd9853cd280eafa2bcfa62a99e8bd6e \ - --hash=sha256:aa9cd6e24126d4afb3752a3e70fce39f92d0e1a58a236ddf6ee823ff7dba28ee \ - --hash=sha256:b6dc8f1d770a8280997e4beae7b9a75a33b268c59e033e72c8a10990097e5fde \ - --hash=sha256:b84bff0391ad4abe25c2740c7aec0e3de316fdf7764007f41e248422a7760a7f \ - --hash=sha256:ba32efcccfe2c58f4d01115440d1672b4eb26cdd6fc5b5818f1fb41f7c3e1889 \ - --hash=sha256:bda40c57115684d0216556671875e008279dea2dc00fcd3dde126ac8e0d7a2fb \ - --hash=sha256:c4a440f725f3b99133de610bfec93d570b13826f89616377715b9cd60424db6e \ - --hash=sha256:d010be060c996db725fbce7e3ef14687cdcc76f4ca0e4339a68cc4532c382a73 \ - --hash=sha256:d2ab34adc9bf1489452965cdb16a924e97d4452fcf88a50b21859068b50b5c3b \ - --hash=sha256:d7594a6d32cda2b49df3fd9abf9b37c8d2f3eab5df45c24056b4a671ac661619 \ - --hash=sha256:d961f4123bb3c447d9fcdcb67e1530c366f10ab3a0c7d1c0c9943050936d4877 \ - --hash=sha256:dae7a1879918f6544201d33666909b040a46421054a50e0f773e0d870ed7438d \ - --hash=sha256:dcebf7e475001d2cdeb020be630dc5b687e9acdd60d16fea6bb4508e7b94cf76 \ - --hash=sha256:f627c5bf5759fdd90195b0c0431f99cff4867d212a67b384442c51136a098ed7 \ - --hash=sha256:f8b2918c19e0d48f5f20df458c84692e2a054f02d9df25e6c3c930063eca64c1 \ - --hash=sha256:fb223456db6e5f7bd9bbd5cd969f05aae82ae21acc00643b60d81c770abd402b +watchdog==5.0.3 \ + --hash=sha256:0f9332243355643d567697c3e3fa07330a1d1abf981611654a1f2bf2175612b7 \ + --hash=sha256:1021223c08ba8d2d38d71ec1704496471ffd7be42cfb26b87cd5059323a389a1 \ + --hash=sha256:108f42a7f0345042a854d4d0ad0834b741d421330d5f575b81cb27b883500176 \ + --hash=sha256:1e9679245e3ea6498494b3028b90c7b25dbb2abe65c7d07423ecfc2d6218ff7c \ + --hash=sha256:223160bb359281bb8e31c8f1068bf71a6b16a8ad3d9524ca6f523ac666bb6a1e \ + --hash=sha256:26dd201857d702bdf9d78c273cafcab5871dd29343748524695cecffa44a8d97 \ + --hash=sha256:294b7a598974b8e2c6123d19ef15de9abcd282b0fbbdbc4d23dfa812959a9e05 \ + --hash=sha256:349c9488e1d85d0a58e8cb14222d2c51cbc801ce11ac3936ab4c3af986536926 \ + --hash=sha256:49f4d36cb315c25ea0d946e018c01bb028048023b9e103d3d3943f58e109dd45 \ + --hash=sha256:53a3f10b62c2d569e260f96e8d966463dec1a50fa4f1b22aec69e3f91025060e \ + --hash=sha256:53adf73dcdc0ef04f7735066b4a57a4cd3e49ef135daae41d77395f0b5b692cb \ + --hash=sha256:560135542c91eaa74247a2e8430cf83c4342b29e8ad4f520ae14f0c8a19cfb5b \ + --hash=sha256:720ef9d3a4f9ca575a780af283c8fd3a0674b307651c1976714745090da5a9e8 \ + --hash=sha256:752fb40efc7cc8d88ebc332b8f4bcbe2b5cc7e881bccfeb8e25054c00c994ee3 \ + --hash=sha256:78864cc8f23dbee55be34cc1494632a7ba30263951b5b2e8fc8286b95845f82c \ + --hash=sha256:85527b882f3facda0579bce9d743ff7f10c3e1e0db0a0d0e28170a7d0e5ce2ea \ + --hash=sha256:90a67d7857adb1d985aca232cc9905dd5bc4803ed85cfcdcfcf707e52049eda7 \ + --hash=sha256:91b522adc25614cdeaf91f7897800b82c13b4b8ac68a42ca959f992f6990c490 \ + --hash=sha256:9413384f26b5d050b6978e6fcd0c1e7f0539be7a4f1a885061473c5deaa57221 \ + --hash=sha256:94d11b07c64f63f49876e0ab8042ae034674c8653bfcdaa8c4b32e71cfff87e8 \ + --hash=sha256:950f531ec6e03696a2414b6308f5c6ff9dab7821a768c9d5788b1314e9a46ca7 \ + --hash=sha256:a2e8f3f955d68471fa37b0e3add18500790d129cc7efe89971b8a4cc6fdeb0b2 \ + --hash=sha256:ae6deb336cba5d71476caa029ceb6e88047fc1dc74b62b7c4012639c0b563906 \ + --hash=sha256:b8ca4d854adcf480bdfd80f46fdd6fb49f91dd020ae11c89b3a79e19454ec627 \ + --hash=sha256:c66f80ee5b602a9c7ab66e3c9f36026590a0902db3aea414d59a2f55188c1f49 \ + --hash=sha256:d52db5beb5e476e6853da2e2d24dbbbed6797b449c8bf7ea118a4ee0d2c9040e \ + --hash=sha256:dd021efa85970bd4824acacbb922066159d0f9e546389a4743d56919b6758b91 \ + --hash=sha256:e25adddab85f674acac303cf1f5835951345a56c5f7f582987d266679979c75b \ + --hash=sha256:f00b4cf737f568be9665563347a910f8bdc76f88c2970121c86243c8cfdf90e9 \ + --hash=sha256:f01f4a3565a387080dc49bdd1fefe4ecc77f894991b88ef927edbfa45eb10818 # via -r ./requirements.in From 0a895b968290d5179b121912292959931491fb23 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 1 Nov 2024 03:40:42 -0700 Subject: [PATCH 119/138] update supported models and display error to user --- package-lock.json | 9 ++--- package.json | 2 +- src/views/chatView/ChatDataProvider.ts | 46 +++++--------------------- src/views/chatView/utils/TokenUtils.ts | 10 +++--- 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9467a04..aacfd5d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,7 +22,7 @@ "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", - "token.js": "^0.4.3", + "token.js": "^0.4.5", "vscode-languageclient": "^9.0.1" }, "devDependencies": { @@ -8406,9 +8406,10 @@ } }, "node_modules/token.js": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/token.js/-/token.js-0.4.3.tgz", - "integrity": "sha512-3TXC0op+OqVrwqLWCPoFrL99Q+qLa9iwLRTXA7c5WuwRZgrff4pIxltVvpSwzhukVrAzGopRXBfGdZxS930+5A==", + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/token.js/-/token.js-0.4.5.tgz", + "integrity": "sha512-5fDAlvEq7yy1N7cjCRKU7FTRQGT1vKyQdnBo9KXTRD1qzU10lY6SJ0XgIqFFfx+JwvIyanV9T5txo/Kz9VClbw==", + "license": "MIT", "dependencies": { "@anthropic-ai/sdk": "^0.24.3", "@aws-sdk/client-bedrock-runtime": "^3.609.0", diff --git a/package.json b/package.json index 8aa4001e..7204d789 100644 --- a/package.json +++ b/package.json @@ -540,7 +540,7 @@ "marked": "^14.1.2", "svg-pan-zoom": "github:bumbu/svg-pan-zoom", "svgdom": "^0.1.19", - "token.js": "^0.4.3", + "token.js": "^0.4.5", "vscode-languageclient": "^9.0.1" } } diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 5fa8be8f..0088c9f0 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -33,7 +33,9 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { initializeTokenJS(this.context, this.currentProvider); this.eventBus.addListener(LSP_ZENML_STACK_CHANGED, this.refreshWebviewBound); this._disposables.push( - new vscode.Disposable(() => this.eventBus.removeListener(LSP_ZENML_STACK_CHANGED, this.refreshWebviewBound)) + new vscode.Disposable(() => + this.eventBus.removeListener(LSP_ZENML_STACK_CHANGED, this.refreshWebviewBound) + ) ); } @@ -88,42 +90,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getAvailableModels(): string[] { switch (this.currentProvider) { case 'Gemini': - return ['gemini-1.5-pro', 'gemini-1.5-flash', 'gemini-1.0-pro']; + return ['gemini-1.5-pro', 'gemini-1.5-flash']; case 'OpenAI': - return [ - 'gpt-4o', - 'gpt-4o-mini', - 'gpt-4o-2024-05-13', - 'gpt-4-turbo', - 'gpt-4-turbo-2024-04-09', - 'gpt-4-0125-preview', - 'gpt-4-turbo-preview', - 'gpt-4-1106-preview', - 'gpt-4-vision-preview', - 'gpt-4', - 'gpt-4-0314', - 'gpt-4-0613', - 'gpt-4-32k', - 'gpt-4-32k-0314', - 'gpt-4-32k-0613', - 'gpt-3.5-turbo', - 'gpt-3.5-turbo-16k', - 'gpt-3.5-turbo-0301', - 'gpt-3.5-turbo-0613', - 'gpt-3.5-turbo-1106', - 'gpt-3.5-turbo-0125', - 'gpt-3.5-turbo-16k-0613', - ]; + return ['gpt-4o-mini', 'gpt-3.5-turbo']; case 'Anthropic': - return [ - 'claude-3-5-sonnet-20240620', - 'claude-3-opus-20240229', - 'claude-3-sonnet-20240229', - 'claude-3-haiku-20240307', - 'claude-2.1', - 'claude-2.0', - 'claude-instant-1.2', - ]; + return ['claude-3-5-sonnet-20240620', 'claude-3-opus-20240229']; default: return []; } @@ -187,10 +158,11 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.streamingMessage = null; this.updateWebviewContent(); this.sendMessageToWebview('enableInput'); - } catch (error) { + } catch (error: any) { console.error('Error in addMessage:', error); - this.sendMessageToWebview(`Error: Unable to get response from ${provider || this.currentProvider}`); + this.sendMessageToWebview(`${error}`); this.sendMessageToWebview('enableInput'); + this._view?.webview.postMessage({ command: 'hideLoader' }); } } diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 4b1a2a6e..3945d4fd 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -106,14 +106,16 @@ export async function* getChatResponse( } } catch (streamError) { console.error('Streaming error in getChatResponse:', streamError); - throw new Error(`Streaming error with ${provider} API: ${streamError instanceof Error ? streamError.message : String(streamError)}`); + throw new Error( + `Streaming error with ${provider} API: ${streamError instanceof Error ? streamError.message : String(streamError)}` + ); } - } catch (error: unknown) { + } catch (error: any) { console.error('Error in getChatResponse:', error); if (error instanceof Error) { - throw new Error(`Error with ${provider} API: ${error.message}`); + throw new Error(`${error.message}`); } else { - throw new Error(`Error with ${provider} API: ${String(error)}`); + throw new Error(`${provider} API: ${String(error)}`); } } } From 84ca5002156b6b53a49776c9d6113a5e35bd0bb7 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 1 Nov 2024 06:56:32 -0700 Subject: [PATCH 120/138] change default llm to open ai and made frontend and backend state more consistent --- resources/chat-view/chat.js | 10 +++++++--- src/views/chatView/ChatDataProvider.ts | 13 ++++++++++--- src/views/chatView/chatRenderer.ts | 26 +++++++++++++++----------- 3 files changed, 32 insertions(+), 17 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 2edc9cae..4d61dbe7 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -77,6 +77,8 @@ } else { pipelineRunsDropdown.classList.remove('open'); } + + vscode.postMessage({ command: 'updateProvider', provider: selectedProvider }); } function sendMessage(event) { @@ -218,6 +220,11 @@ updateModelDropdown(message.models); break; } + case 'updateModel': { + localStorage.setItem('selectedModel', message.text); + restoreState(); + break; + } case 'hideLoader': { hideLoader(); break; @@ -285,9 +292,6 @@ checkbox.addEventListener('change', saveState); }); - // Restore state when page loads - document.addEventListener('DOMContentLoaded', restoreState); - function sendSampleMessage() { const provider = document.querySelector('#provider-dropdown').value; const model = document.querySelector('#model-dropdown').value; diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 0088c9f0..b20d9f5b 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -23,7 +23,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private _view?: vscode.WebviewView; private messages: ChatMessage[] = []; private streamingMessage: ChatMessage | null = null; - private currentProvider: string = 'Gemini'; + private currentProvider: string = 'OpenAI'; private currentModel: string = this.getAvailableModels()[0]; private eventBus: EventBus = EventBus.getInstance(); private _disposables: vscode.Disposable[] = []; @@ -46,7 +46,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { ) { this._view = webviewView; this.configureWebViewOptions(webviewView.webview); - this.loadWebviewContent(); + this.refreshWebview(); const messageListener = async (message: any) => { const pipelineDataProvider = PipelineDataProvider.getInstance(); @@ -77,6 +77,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.context.extensionUri, this.messages, this.currentProvider, + this.currentModel, this.getAvailableProviders(), this.getAvailableModels() ); @@ -110,12 +111,18 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } async updateProvider(provider: string) { + // Doesn't update if there's no change to avoid infinite loops with the frontend + if (this.currentProvider === provider) { + return; + } + this.currentProvider = provider; this.currentModel = this.getAvailableModels()[0]; + this._view?.webview.postMessage({ command: 'updateModel', text: this.currentModel }); try { await initializeTokenJS(this.context, provider); - this.loadWebviewContent(); + this.refreshWebview(); } catch (error: any) { console.error('Error initializing TokenJS:', error); vscode.window.showErrorMessage(`Failed to initialize ${provider}: ${error.message}`); diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index 45b5f56c..d84c9c6c 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -21,6 +21,7 @@ export function getWebviewContent( extensionUri: vscode.Uri, messages: ChatMessage[], currentProvider: string, + currentModel: string, availableProviders: string[], availableModels: string[] ): string { @@ -28,11 +29,11 @@ export function getWebviewContent( let html: string; try { html = fs.readFileSync(htmlPath.fsPath, 'utf8'); - } catch (err) { - if (err instanceof Error) { - vscode.window.showErrorMessage(`Failed to load HTML template: ${err.message}`); + } catch (error) { + if (error instanceof Error) { + vscode.window.showErrorMessage(`Failed to load HTML template: ${error.message}`); } else { - vscode.window.showErrorMessage('Failed to load HTML template due to an unkown error.'); + vscode.window.showErrorMessage('Failed to load HTML template due to an unknown error.'); } return ''; } @@ -49,14 +50,12 @@ export function getWebviewContent( vscode.Uri.joinPath(extensionUri, 'node_modules', 'dompurify', 'dist', 'purify.min.js') ); - const stylesUri = webview.asWebviewUri( - vscode.Uri.joinPath(extensionUri, 'dist', 'styles.css') - ); + const stylesUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'dist', 'styles.css')); const chatLogHtml = renderChatLog(messages); let treeItemHtml = getTreeHtml(); let providerDropdownHtml = getProviderDropdownHtml(currentProvider, availableProviders); - let modelDropdownHtml = getModelDropdownHtml(availableModels); + let modelDropdownHtml = getModelDropdownHtml(currentModel, availableModels); html = html.replace('${jsUri}', jsUri.toString()); html = html.replace('${markedUri}', markedUri.toString()); @@ -85,9 +84,12 @@ function getProviderDropdownHtml(currentProvider: string, availableProviders: st `; } -function getModelDropdownHtml(availableModels: string[]): string { +function getModelDropdownHtml(currentModel: string, availableModels: string[]): string { const options = availableModels - .map(model => ``) + .map( + model => + `` + ) .join(''); return ` @@ -104,7 +106,9 @@ export function renderChatLog( const renderer: Partial = { code({ text, lang, escaped }: Tokens.Code) { const formattedCode = text.replace(/\n$/, '') + (escaped ? '' : '\n'); - return escaped ? `${formattedCode}` : '
    ' + formattedCode + '
    \n'; + return escaped + ? `${formattedCode}` + : '
    ' + formattedCode + '
    \n'; }, }; From 091eefaa87f6fcd340e8f2002191cb8a3632efc6 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Fri, 1 Nov 2024 06:59:28 -0700 Subject: [PATCH 121/138] update files to follow lint style --- postcss.config.js | 14 +++---- resources/chat-view/chat.html | 2 +- src/types/ChatTypes.ts | 15 +++++-- src/views/chatView/chatMessageHandler.ts | 4 +- src/views/chatView/utils/ContextUtils.ts | 49 +++++++++++++++-------- src/views/chatView/utils/CustomErrors.ts | 30 +++++++------- src/views/chatView/utils/PipelineUtils.ts | 14 +++++-- tailwind.config.js | 2 +- 8 files changed, 81 insertions(+), 49 deletions(-) diff --git a/postcss.config.js b/postcss.config.js index 44fc2b97..c75e4951 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,8 +1,8 @@ module.exports = { - plugins: [ - // Process Tailwind CSS classes - require('tailwindcss'), - // Add vendor prefixes to CSS rules - require('autoprefixer'), - ] - }; \ No newline at end of file + plugins: [ + // Process Tailwind CSS classes + require('tailwindcss'), + // Add vendor prefixes to CSS rules + require('autoprefixer'), + ], +}; diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 69e3b130..6fd35a41 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -16,7 +16,7 @@ ZenML Chat - + = { } catch (error) { console.error('Error adding message:', error); if (error instanceof NetworkError) { - chatDataProvider.showInfoMessage('Network error. Please check your connection and try again.'); + chatDataProvider.showInfoMessage( + 'Network error. Please check your connection and try again.' + ); } else if (error instanceof ValidationError) { chatDataProvider.showInfoMessage('Invalid message format. Please try again.'); } else { diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 3395f814..10d0762f 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -22,7 +22,15 @@ import { getPipelineData } from './PipelineUtils'; import { ComponentDataProvider } from '../../activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../../activityBar/environmentView/EnvironmentDataProvider'; -type ContextType = 'serverContext' | 'environmentContext' | 'pipelineContext' | 'stackContext' | 'stackComponentsContext' | 'logsContext' | 'pipelineRunContext' | string; +type ContextType = + | 'serverContext' + | 'environmentContext' + | 'pipelineContext' + | 'stackContext' + | 'stackComponentsContext' + | 'logsContext' + | 'pipelineRunContext' + | string; export async function addContext(requestedContext: ContextType[]): Promise { let systemMessage = 'Context:\n'; @@ -124,9 +132,7 @@ function getStackData(): string { let contextString = stackData .map(item => { if ('isActive' in item && 'id' in item) { - return `Name: ${item.label || 'N/A'}\n` + - `ID: ${item.id}\n` + - `Active: ${item.isActive}`; + return `Name: ${item.label || 'N/A'}\n` + `ID: ${item.id}\n` + `Active: ${item.isActive}`; } return `Name: ${item.label || 'N/A'}`; }) @@ -141,7 +147,8 @@ function getStackData(): string { async function getLogData(): Promise { try { const lsClient = LSClient.getInstance(); - const globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + const globalConfig = + await lsClient.sendLsClientRequest('getGlobalConfig'); const apiToken = globalConfig.store.api_token; const dashboardUrl = globalConfig.store.url; @@ -208,16 +215,19 @@ async function getPipelineRunLogs(id: string): Promise { const lsClient = LSClient.getInstance(); const dagData = await lsClient.sendLsClientRequest('getPipelineRunDag', [id]); - const stepData = (await Promise.all( - dagData.nodes.map(async (node: DagArtifact | DagStep) => { - if (node.type === 'step') { - return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); - } - return null; - }) - )).filter((step): step is Step => step !== null); + const stepData = ( + await Promise.all( + dagData.nodes.map(async (node: DagArtifact | DagStep) => { + if (node.type === 'step') { + return await lsClient.sendLsClientRequest('getPipelineRunStep', [node.id]); + } + return null; + }) + ) + ).filter((step): step is Step => step !== null); - const globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); + const globalConfig = + await lsClient.sendLsClientRequest('getGlobalConfig'); const apiToken = globalConfig.store.api_token; const dashboardUrl = globalConfig.store.url; @@ -238,7 +248,10 @@ async function getPipelineRunLogs(id: string): Promise { type NodeType = 'all' | 'step' | 'artifact'; -async function getPipelineRunNodes(nodeType: NodeType, pipelineRunId?: string): Promise { +async function getPipelineRunNodes( + nodeType: NodeType, + pipelineRunId?: string +): Promise { try { const pipelineDataProvider = PipelineDataProvider.getInstance(); const allPipelineRuns = pipelineDataProvider.getPipelineData(); @@ -249,8 +262,10 @@ async function getPipelineRunNodes(nodeType: NodeType, pipelineRunId?: string): const lsClient = LSClient.getInstance(); const fetchAndFilterNodes = async (pipelineRun: { id: string }): Promise => { - const dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [pipelineRun.id]); - + const dag = await lsClient.sendLsClientRequest('getPipelineRunDag', [ + pipelineRun.id, + ]); + const filteredNodes = await Promise.all( dag.nodes.map(async (node: DagArtifact | DagStep) => { if (nodeType === 'all' || node.type === nodeType) { diff --git a/src/views/chatView/utils/CustomErrors.ts b/src/views/chatView/utils/CustomErrors.ts index ae177d3e..21d1c086 100644 --- a/src/views/chatView/utils/CustomErrors.ts +++ b/src/views/chatView/utils/CustomErrors.ts @@ -12,22 +12,22 @@ // permissions and limitations under the License. export class NetworkError extends Error { - constructor(message: string) { - super(message); - this.name = 'NetworkError'; - } + constructor(message: string) { + super(message); + this.name = 'NetworkError'; } +} - export class ValidationError extends Error { - constructor(message: string) { - super(message); - this.name = 'ValidationError'; - } +export class ValidationError extends Error { + constructor(message: string) { + super(message); + this.name = 'ValidationError'; } +} - export class StorageError extends Error { - constructor(message: string) { - super(message); - this.name = 'StorageError'; - } - } \ No newline at end of file +export class StorageError extends Error { + constructor(message: string) { + super(message); + this.name = 'StorageError'; + } +} diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index e9baadd7..3a8c46ad 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -16,9 +16,17 @@ import { TreeItem, ContextItem } from '../../../types/ChatTypes'; const CONTEXT_ITEMS: readonly ContextItem[] = [ { name: 'Server', value: 'serverContext', title: 'Includes all server metadata with message' }, - { name: 'Environment', value: 'environmentContext', title: 'Includes all server metadata with message' }, + { + name: 'Environment', + value: 'environmentContext', + title: 'Includes all server metadata with message', + }, { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, - { name: 'Stack Components', value: 'stackComponentsContext', title: 'Includes all stack component metadata with message' }, + { + name: 'Stack Components', + value: 'stackComponentsContext', + title: 'Includes all stack component metadata with message', + }, ] as const; export function getPipelineData(): { contextString: string; treeItems: TreeItem[] } { @@ -89,7 +97,7 @@ export function getPaginatedTreeData(): TreeItem[] { export function getTreeData(): TreeItem[] { const paginatedItems = getPaginatedTreeData(); - + return [ ...CONTEXT_ITEMS, { diff --git a/tailwind.config.js b/tailwind.config.js index c8e41dd3..d9d0be6d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,6 +1,6 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ["./resources/chat-view/**/*.{html,js}"], + content: ['./resources/chat-view/**/*.{html,js}'], theme: { extend: {}, }, From 6fff64976781d31d345932e87c83785cb02877d9 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 03:18:08 -0800 Subject: [PATCH 122/138] docs: added documentation for custom errors related to the chatbot and have the errors preserve the stack trace --- src/views/chatView/utils/CustomErrors.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/views/chatView/utils/CustomErrors.ts b/src/views/chatView/utils/CustomErrors.ts index 21d1c086..81184b82 100644 --- a/src/views/chatView/utils/CustomErrors.ts +++ b/src/views/chatView/utils/CustomErrors.ts @@ -11,23 +11,38 @@ // or implied.See the License for the specific language governing // permissions and limitations under the License. +/** + * Custom error for network issues + * @param {string} message - The error message + */ export class NetworkError extends Error { constructor(message: string) { super(message); this.name = 'NetworkError'; + Object.setPrototypeOf(this, NetworkError.prototype); } } +/** + * Custom error for validation issues + * @param {string} message - The error message + */ export class ValidationError extends Error { constructor(message: string) { super(message); this.name = 'ValidationError'; + Object.setPrototypeOf(this, ValidationError); } } +/** + * Custom error for storage issues relating to the chat history + * @param {string} message - The error message + */ export class StorageError extends Error { constructor(message: string) { super(message); this.name = 'StorageError'; + Object.setPrototypeOf(this, StorageError); } } From f03198481bea926355fca957acb6c4de01f93388 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 03:30:47 -0800 Subject: [PATCH 123/138] chore: refactor custom chattype aimodel --- src/types/ChatTypes.ts | 65 +++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 71c99848..0f4febf2 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -56,41 +56,40 @@ export interface ContextItem { } /** - * Represents the available AI models. + * Represents the available AI models grouped by provider. + */ +const OPENAI_MODELS = { + GPT4: { + DEFAULT: 'gpt-4o-mini', + }, + GPT35: { + DEFAULT: 'gpt-3.5-turbo', + }, +} as const; + +const ANTHROPIC_MODELS = { + CLAUDE_SONNET: { + DEFAULT: 'claude-3-5-sonnet-20240620', + }, + CLAUDE_OPUS: { + DEFAULT: 'claude-3-opus-20240229', + }, +} as const; + +const GEMINI_MODELS = { + GEMINI: { + DEFAULT: 'gemini-1.5-pro', + FLASH: 'gemini-1.5-flash', + }, +} as const; + +/** + * AIModel is a union type of the nested values from the constants. */ export type AIModel = - | 'gemini-1.5-pro' - | 'gemini-1.5-flash' - | 'gemini-1.0-pro' - | 'gpt-4o' - | 'gpt-4o-mini' - | 'gpt-4o-2024-05-13' - | 'gpt-4-turbo' - | 'gpt-4-turbo-2024-04-09' - | 'gpt-4-0125-preview' - | 'gpt-4-turbo-preview' - | 'gpt-4-1106-preview' - | 'gpt-4-vision-preview' - | 'gpt-4' - | 'gpt-4-0314' - | 'gpt-4-0613' - | 'gpt-4-32k' - | 'gpt-4-32k-0314' - | 'gpt-4-32k-0613' - | 'gpt-3.5-turbo' - | 'gpt-3.5-turbo-16k' - | 'gpt-3.5-turbo-0301' - | 'gpt-3.5-turbo-0613' - | 'gpt-3.5-turbo-1106' - | 'gpt-3.5-turbo-0125' - | 'gpt-3.5-turbo-16k-0613' - | 'claude-3-5-sonnet-20240620' - | 'claude-3-opus-20240229' - | 'claude-3-sonnet-20240229' - | 'claude-3-haiku-20240307' - | 'claude-2.1' - | 'claude-2.0' - | 'claude-instant-1.2'; + | (typeof OPENAI_MODELS)[keyof typeof OPENAI_MODELS][keyof (typeof OPENAI_MODELS)[keyof typeof OPENAI_MODELS]] + | (typeof ANTHROPIC_MODELS)[keyof typeof ANTHROPIC_MODELS][keyof (typeof ANTHROPIC_MODELS)[keyof typeof ANTHROPIC_MODELS]] + | (typeof GEMINI_MODELS)[keyof typeof GEMINI_MODELS][keyof (typeof GEMINI_MODELS)[keyof typeof GEMINI_MODELS]]; /** * Represents a message sent to the webview. From df35edf57ede2851c99c2f8a4abd8b1ed0107dad Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 03:33:52 -0800 Subject: [PATCH 124/138] refactor: changed the type of role in chatmessage to the only three possible types --- src/types/ChatTypes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/ChatTypes.ts b/src/types/ChatTypes.ts index 0f4febf2..f9a1a904 100644 --- a/src/types/ChatTypes.ts +++ b/src/types/ChatTypes.ts @@ -16,7 +16,7 @@ */ export interface ChatMessage { /** The role of the message sender (e.g., user, assistant). */ - role: string; + role: 'user' | 'assistant' | 'system'; /** The content of the message. */ content: string; } From 012ce96ea0177ac1efa7d0de50e1be9e3ea0b9b8 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 03:42:06 -0800 Subject: [PATCH 125/138] refactor: layout uses flexible css rather than fixed height --- resources/chat-view/chat.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index 6fd35a41..ab8bd0f3 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -29,7 +29,7 @@

    ZenML Chat

    ${chatLogHtml}
    From 21e7c8fc3f553398e99bcc6c61dc0e72d9e0bd23 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:27:58 -0800 Subject: [PATCH 126/138] refactor: refactor error handling in tokenutils --- src/views/chatView/utils/TokenUtils.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 3945d4fd..6fea250a 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -112,10 +112,9 @@ export async function* getChatResponse( } } catch (error: any) { console.error('Error in getChatResponse:', error); - if (error instanceof Error) { - throw new Error(`${error.message}`); - } else { - throw new Error(`${provider} API: ${String(error)}`); - } + throw new Error( + `Error in getChatResponse: ${error instanceof Error ? error.message : `${provider} API: ${String(error)}`}`, + { cause: error } + ); } } From bbac8d58b8dcc78b020000f9efc39e615b00dc01 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:29:31 -0800 Subject: [PATCH 127/138] refactor: make prompting more consistent --- src/views/chatView/utils/TokenUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 6fea250a..4ea394ab 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -63,7 +63,7 @@ export async function* getChatResponse( Key 2-2 value 2-2 - To bold words, use tags. Do not ever use asterisks for formatting. + To bold words, use tags. Do not ever use asterisks for formatting. To write code blocks, use tags. if there's an explanation at the end, add it like: From f8926613c351997dc20e73092637fabb50802e81 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:35:48 -0800 Subject: [PATCH 128/138] refactor: avoids side effects inside getPipelineData --- src/views/chatView/utils/PipelineUtils.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index 3a8c46ad..f65d825f 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -33,7 +33,9 @@ export function getPipelineData(): { contextString: string; treeItems: TreeItem[ try { const pipelineRuns = PipelineDataProvider.getInstance().getPipelineRuns(); let contextString = ''; - const treeItems: TreeItem[] = pipelineRuns.map(run => { + const treeItems: TreeItem[] = []; + + pipelineRuns.forEach(run => { const formattedStartTime = format(new Date(run.startTime), 'Pp'); const formattedEndTime = run.endTime ? format(new Date(run.endTime), 'Pp') : 'N/A'; @@ -62,7 +64,7 @@ export function getPipelineData(): { contextString: string; treeItems: TreeItem[ { name: `python version: ${run.pythonVersion}` }, ], }; - return treeItem; + treeItems.push(treeItem); }); return { contextString, treeItems }; From e954b898e28b6c421197e4fa502346b4df2d8072 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:37:35 -0800 Subject: [PATCH 129/138] refactor: add error handling for getTreeData --- src/views/chatView/utils/PipelineUtils.ts | 26 +++++++++++++---------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index f65d825f..c1b989da 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -98,15 +98,19 @@ export function getPaginatedTreeData(): TreeItem[] { } export function getTreeData(): TreeItem[] { - const paginatedItems = getPaginatedTreeData(); - - return [ - ...CONTEXT_ITEMS, - { - name: 'Pipeline Runs', - value: 'pipelineContext', - title: 'Includes all code, logs, and metadata for pipeline runs with message', - children: paginatedItems, - }, - ]; + try { + const paginatedItems = getPaginatedTreeData(); + return [ + ...CONTEXT_ITEMS, + { + name: 'Pipeline Runs', + value: 'pipelineContext', + title: 'Includes all code, logs, and metadata for pipeline runs with message', + children: paginatedItems, + }, + ]; + } catch (error) { + console.error('Error fetching tree data: ', error); + return [...CONTEXT_ITEMS]; + } } From da4ef3566bc7d92a900ad513cb786f862aecaca2 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:42:20 -0800 Subject: [PATCH 130/138] refactor: correct typo in context items in pipelineutils --- src/views/chatView/utils/PipelineUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/views/chatView/utils/PipelineUtils.ts b/src/views/chatView/utils/PipelineUtils.ts index c1b989da..91a03d72 100644 --- a/src/views/chatView/utils/PipelineUtils.ts +++ b/src/views/chatView/utils/PipelineUtils.ts @@ -19,7 +19,7 @@ const CONTEXT_ITEMS: readonly ContextItem[] = [ { name: 'Environment', value: 'environmentContext', - title: 'Includes all server metadata with message', + title: 'Includes all environment metadata with message', }, { name: 'Stack', value: 'stackContext', title: 'Includes all stack metadata with message' }, { From 391fae13cdde8e71ee5732b6b2649353250e7044 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:44:59 -0800 Subject: [PATCH 131/138] refactor: better error handling in chatdataprovider --- src/views/chatView/ChatDataProvider.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index b20d9f5b..3141e711 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -167,7 +167,9 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { this.sendMessageToWebview('enableInput'); } catch (error: any) { console.error('Error in addMessage:', error); - this.sendMessageToWebview(`${error}`); + const errorMessage = error instanceof Error ? error.message : 'An unexpected error occured.'; + this.sendMessageToWebview(`${errorMessage}`); + this.sendMessageToWebview('enableInput'); this._view?.webview.postMessage({ command: 'hideLoader' }); } From c7bf7a0369ebad9a92a7bd590faf6f5b2fe23b72 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 04:56:43 -0800 Subject: [PATCH 132/138] refactor: return default html when error reading html --- src/views/chatView/chatRenderer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/views/chatView/chatRenderer.ts b/src/views/chatView/chatRenderer.ts index d84c9c6c..36e746dc 100644 --- a/src/views/chatView/chatRenderer.ts +++ b/src/views/chatView/chatRenderer.ts @@ -32,10 +32,11 @@ export function getWebviewContent( } catch (error) { if (error instanceof Error) { vscode.window.showErrorMessage(`Failed to load HTML template: ${error.message}`); + return '

    Failed to load chat interface

    '; } else { vscode.window.showErrorMessage('Failed to load HTML template due to an unknown error.'); + return '

    Failed to load chat interface

    '; } - return ''; } const jsUri = webview.asWebviewUri( From 205a6483064dd89b33dd231062192cdbd00356d5 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 05:06:25 -0800 Subject: [PATCH 133/138] refactor: add error handling for lsclient --- src/views/chatView/utils/ContextUtils.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 10d0762f..00689daf 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -21,6 +21,7 @@ import * as vscode from 'vscode'; import { getPipelineData } from './PipelineUtils'; import { ComponentDataProvider } from '../../activityBar/componentView/ComponentDataProvider'; import { EnvironmentDataProvider } from '../../activityBar/environmentView/EnvironmentDataProvider'; +import { error } from 'console'; type ContextType = | 'serverContext' @@ -147,15 +148,23 @@ function getStackData(): string { async function getLogData(): Promise { try { const lsClient = LSClient.getInstance(); + const globalConfig = await lsClient.sendLsClientRequest('getGlobalConfig'); - const apiToken = globalConfig.store.api_token; - const dashboardUrl = globalConfig.store.url; + if (!globalConfig.store) { + throw new Error('Global configuration store is undefined'); + } + const apiToken = globalConfig.store.api_token; if (!apiToken) { throw new Error('API Token is not available in global configuration'); } + const dashboardUrl = globalConfig.store.url; + if (!dashboardUrl) { + throw new Error('Dashboard URL is not available in global configuration'); + } + const pipelineRunSteps = await getPipelineRunNodes('step'); if (!pipelineRunSteps?.[0]) { From 7d91ad35f71367c91aa73bc86ea3ce14ed571316 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Tue, 5 Nov 2024 14:27:18 -0800 Subject: [PATCH 134/138] refactor: better prompting when there is no selected context --- src/views/chatView/utils/ContextUtils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/views/chatView/utils/ContextUtils.ts b/src/views/chatView/utils/ContextUtils.ts index 00689daf..9499da0e 100644 --- a/src/views/chatView/utils/ContextUtils.ts +++ b/src/views/chatView/utils/ContextUtils.ts @@ -16,7 +16,7 @@ import { ZenmlGlobalConfigResp } from '../../../types/LSClientResponseTypes'; import { PipelineRunDag, DagStep, DagArtifact } from '../../../types/PipelineTypes'; import { JsonObject } from '../../panel/panelView/PanelTreeItem'; import { StackComponentTreeItem } from '../../activityBar'; -import axios from 'axios'; +import axios, { request } from 'axios'; import * as vscode from 'vscode'; import { getPipelineData } from './PipelineUtils'; import { ComponentDataProvider } from '../../activityBar/componentView/ComponentDataProvider'; @@ -34,6 +34,10 @@ type ContextType = | string; export async function addContext(requestedContext: ContextType[]): Promise { + if (!requestedContext?.length) { + return 'Context:\nNo context given'; + } + let systemMessage = 'Context:\n'; for (let context of requestedContext) { if (context.startsWith('Pipeline Run:')) { From af93a3fc1a8b793bcd855262d41f11327befd14e Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Wed, 6 Nov 2024 04:20:03 -0800 Subject: [PATCH 135/138] feat: added checkbox behavior for children checkboxes --- resources/chat-view/chat.html | 3 +- resources/chat-view/chat.js | 39 ++- resources/chat-view/styles.css | 461 +++++++++++++++++---------------- 3 files changed, 266 insertions(+), 237 deletions(-) diff --git a/resources/chat-view/chat.html b/resources/chat-view/chat.html index ab8bd0f3..d9abcced 100644 --- a/resources/chat-view/chat.html +++ b/resources/chat-view/chat.html @@ -28,8 +28,7 @@

    ZenML Chat

    ${chatLogHtml}
    diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index 4d61dbe7..d0cc4fbe 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -81,6 +81,39 @@ vscode.postMessage({ command: 'updateProvider', provider: selectedProvider }); } + // Checkbox behavior for children checkboxes under pipeline runs + const pipelineRunsBox = document.querySelector('input[type="checkbox"][value="pipelineContext"]'); + pipelineRunsBox.addEventListener('click', toggleTreeItemBoxes); + + function toggleTreeItemBoxes() { + const pipelineRunsBox = document.querySelector( + 'input[type="checkbox"][value="pipelineContext"]' + ); + + const pipelineRunBoxes = document.querySelectorAll( + 'input[type="checkbox"][value*="Pipeline Run:"]' + ); + + // Condition: If the main checkbox is clicked, all children checkboxes should be checked. + if (pipelineRunsBox.checked) { + pipelineRunBoxes.forEach(checkbox => (checkbox.checked = true)); + console.log('checked boxes: ', pipelineRunBoxes); + } else { + pipelineRunBoxes.forEach(checkbox => (checkbox.checked = false)); + } + + // Condition: If any of the children checkboxes are unchecked, then the main checkbox should be checked. + function updateMainCheckbox() { + const anyUnchecked = Array.from(pipelineRunBoxes).some(checkbox => !checkbox.checked); + pipelineRunsBox.checked = !anyUnchecked; + } + + pipelineRunBoxes.forEach(checkbox => { + checkbox.addEventListener('change', updateMainCheckbox); + }); + } + + // Sends a message to the LLM function sendMessage(event) { event.preventDefault(); if (isInputDisabled) { @@ -128,6 +161,7 @@ document.getElementById('clearChat').addEventListener('click', clearChatLog); + // Adds the message to the UI function appendToChat(text, role) { const chatMessages = document.getElementById('chatMessages'); let messageDiv; @@ -430,7 +464,6 @@ }); const textarea = document.getElementById('messageInput'); - const sendButton = document.getElementById('sendMessage'); const loader = document.getElementById('loader'); isInputDisabled = false; @@ -438,10 +471,6 @@ loader.classList.add('loader'); } - function hideLoader() { - loader.classList.remove('loader'); - } - textarea.addEventListener('keydown', e => { if (e.key === 'Enter') { if (e.shiftKey) { diff --git a/resources/chat-view/styles.css b/resources/chat-view/styles.css index 7c4f99dd..70f467dc 100644 --- a/resources/chat-view/styles.css +++ b/resources/chat-view/styles.css @@ -4,233 +4,234 @@ @tailwind utilities; body { - background-color: var(--vscode-editor-background); - color: var(--vscode-editor-foreground); - } - - ul { - color: var(--vscode-editor-foreground); - } - - form { - display: flex; - } - - #chatForm { - background: var(--vscode-input-background); - border: 1px solid var(--vscode-input-border); - border-radius: 0.375rem 0.375rem 0 0; - } - - #chatMessages { - color: var(--vscode-editor-foreground); - } - - #messageInput { - color: var(--vscode-input-foreground); - background-color: var(--vscode-input-background); - } - - .rounded-lg { - background-color: var(--vscode-editor-background); - color: var(--vscode-editor-foreground); - } - - .rounded-lg:hover { - background-color: var(--vscode-button-hoverBackground); - } - - .hide { - display: none; - } - - .tree-view { - background-color: var(--vscode-editor-background); - color: var(--vscode-editor-foreground); - width: 256px; - max-height: 300px; - overflow: auto; - border: 2px var(--vscode-editor-foreground) solid; - border-radius: 0.375rem; - box-shadow: - 0 4px 6px -1px rgba(0, 0, 0, 0.1), - 0 2px 4px -1px rgba(0, 0, 0, 0.06); - } - - .tree-item { - display: flex; - align-items: center; - cursor: pointer; - } - - .tree-item:hover { - background-color: var(--vscode-list-background); - } - - .tree-item-content { - display: flex; - align-items: center; - flex-grow: 1; - padding: 2px 8px; - } - - .tree-item-icon { - width: 16px; - height: 16px; - display: inline-flex; - align-items: center; - justify-content: center; - } - - .tree-item-name { - font-size: 14px; - color: var(--vscode-editor-foreground); - margin-left: 4px; - flex-grow: 1; - } - - .tree-item-children { - display: none; - width: 100%; - } - - .tree-item-children.open { - display: block; - } - - .tree-item-wrapper { - display: flex; - flex-direction: column; - width: 100%; - } - - .tree-item-checkbox { - width: 16px; - height: 16px; - margin-left: 8px; - } - - .chatbar-options { - display: flex; - justify-content: flex-start; - align-items: center; - padding: 0.5rem; - background-color: var(--vscode-input-background); - border: 1px solid var(--vscode-input-border); - border-top: none; - border-radius: 0 0 0.375rem 0.375rem; - } - - .model-dropdown { - font-size: 0.75rem; - padding: 0.25rem; - background-color: var(--vscode-dropdown-background); - color: var(--vscode-dropdown-foreground); - border: 1px solid var(--vscode-dropdown-border); - border-radius: 0.25rem; - margin-right: 0.5rem; - } - - .context-button { - font-size: 0.75rem; - padding: 0.25rem; - background-color: var(--vscode-dropdown-background); - color: var(--vscode-dropdown-foreground); - border: none; - border-radius: 0.25rem; - cursor: pointer; - margin-right: 0.5rem; - } - - .context-button:hover { - background-color: var(--vscode-button-hoverBackground); - } - - .context-dropdown { - position: absolute; - left: 0; - right: 0; - bottom: 100%; - margin-bottom: 0.25rem; - z-index: 10; - } - - #chatMessages h1, - #chatMessages h2, - #chatMessages h3, - #chatMessages h4, - #chatMessages h5, - #chatMessages h6 { - font-weight: bold; - } - - #chatMessages pre { - padding: 1em; - border-radius: 5px; - } - - #chatMessages code { - padding: 0.2em 0.4em; - border-radius: 3px; - } - - #chatMessages pre code { - display: block; - background-color: var(--vscode-textCodeBlock-background); - color: var(--vscode-editor-foreground); - padding: 1em; - border-radius: 5px; - } - - #chatMessages a { - color: var(--vscode-textLink-foreground); - text-decoration: none; - } - - #chatMessages strong { - font-weight: bold; - } - - #chatMessages em { - font-style: italic; - } - - .copy-button { - position: absolute; - bottom: 8px; - right: 8px; - background-color: var(--vscode-button-background); - color: var(--vscode-button-foreground); - border: none; - border-radius: 4px; - padding: 4px 8px; - font-size: 12px; - cursor: pointer; - } - - .copy-button:hover { - background-color: var(--vscode-button-hoverBackground); - } - - .assistant { - background-color: var(--vscode-input-background); - position: relative; - } - - .expand span { - color: var(--vscode-textLink-foreground); - } - - .loader { - width: 50px; - aspect-ratio: 1; - border-radius: 50%; - border: 8px solid; - border-color: var(--vscode-input-background) var(--vscode-input-foreground); - animation: l1 1s infinite; - } - @keyframes l1 { - to { - transform: rotate(0.5turn); - } - } \ No newline at end of file + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); +} + +ul { + color: var(--vscode-editor-foreground); +} + +form { + display: flex; +} + +#chatForm { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 0.375rem 0.375rem 0 0; +} + +#chatMessages { + color: var(--vscode-editor-foreground); +} + +#messageInput { + color: var(--vscode-input-foreground); + background-color: var(--vscode-input-background); +} + +.rounded-lg { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); +} + +.rounded-lg:hover { + background-color: var(--vscode-button-hoverBackground); +} + +.hide { + display: none; +} + +.tree-view { + background-color: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + width: 256px; + max-height: 300px; + overflow: auto; + border: 2px var(--vscode-editor-foreground) solid; + border-radius: 0.375rem; + box-shadow: + 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); +} + +.tree-item { + display: flex; + align-items: center; + cursor: pointer; +} + +.tree-item:hover { + background-color: var(--vscode-list-background); +} + +.tree-item-content { + display: flex; + align-items: center; + flex-grow: 1; + padding: 2px 8px; +} + +.tree-item-icon { + width: 16px; + height: 16px; + display: inline-flex; + align-items: center; + justify-content: center; +} + +.tree-item-name { + font-size: 14px; + color: var(--vscode-editor-foreground); + margin-left: 4px; + flex-grow: 1; +} + +.tree-item-children { + display: none; + width: 100%; +} + +.tree-item-children.open { + display: block; +} + +.tree-item-wrapper { + display: flex; + flex-direction: column; + width: 100%; +} + +.tree-item-checkbox { + width: 16px; + height: 16px; + margin-left: 8px; +} + +.chatbar-options { + display: flex; + justify-content: flex-start; + align-items: center; + padding: 0.5rem; + background-color: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-top: none; + border-radius: 0 0 0.375rem 0.375rem; +} + +.model-dropdown { + font-size: 0.75rem; + padding: 0.25rem; + background-color: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 0.25rem; + margin-right: 0.5rem; +} + +.context-button { + font-size: 0.75rem; + padding: 0.25rem; + background-color: var(--vscode-dropdown-background); + color: var(--vscode-dropdown-foreground); + border: none; + border-radius: 0.25rem; + cursor: pointer; + margin-right: 0.5rem; +} + +.context-button:hover { + background-color: var(--vscode-button-hoverBackground); +} + +.context-dropdown { + position: absolute; + left: 0; + right: 0; + bottom: 100%; + margin-bottom: 0.25rem; + z-index: 10; +} + +#chatMessages h1, +#chatMessages h2, +#chatMessages h3, +#chatMessages h4, +#chatMessages h5, +#chatMessages h6 { + font-weight: bold; +} + +#chatMessages pre { + padding: 1em; + border-radius: 5px; +} + +#chatMessages code { + padding: 0.2em 0.4em; + border-radius: 3px; +} + +#chatMessages pre code { + display: block; + background-color: var(--vscode-textCodeBlock-background); + color: var(--vscode-editor-foreground); + padding: 1em; + border-radius: 5px; +} + +#chatMessages a { + color: var(--vscode-textLink-foreground); + text-decoration: none; +} + +#chatMessages strong { + font-weight: bold; +} + +#chatMessages em { + font-style: italic; +} + +.copy-button { + position: absolute; + bottom: 8px; + right: 8px; + background-color: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-radius: 4px; + padding: 4px 8px; + font-size: 12px; + cursor: pointer; +} + +.copy-button:hover { + background-color: var(--vscode-button-hoverBackground); +} + +.assistant { + background-color: var(--vscode-input-background); + position: relative; +} + +.expand span { + color: var(--vscode-textLink-foreground); +} + +.loader { + width: 50px; + aspect-ratio: 1; + border-radius: 50%; + border: 8px solid; + border-color: var(--vscode-input-background) var(--vscode-input-foreground); + animation: l1 1s infinite; +} + +@keyframes l1 { + to { + transform: rotate(0.5turn); + } +} From 236e93cd74b722c9a31a040314bdee6434d38c57 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Wed, 6 Nov 2024 18:55:18 -0800 Subject: [PATCH 136/138] refactor: cleanup event handlers and make checkbox behavior persist through pagination --- resources/chat-view/chat.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/resources/chat-view/chat.js b/resources/chat-view/chat.js index d0cc4fbe..2d155da7 100644 --- a/resources/chat-view/chat.js +++ b/resources/chat-view/chat.js @@ -79,6 +79,9 @@ } vscode.postMessage({ command: 'updateProvider', provider: selectedProvider }); + + // Needs to preserve state for children checkboxes under pipeline runs when switching pages + toggleTreeItemBoxes(); } // Checkbox behavior for children checkboxes under pipeline runs @@ -96,10 +99,14 @@ // Condition: If the main checkbox is clicked, all children checkboxes should be checked. if (pipelineRunsBox.checked) { - pipelineRunBoxes.forEach(checkbox => (checkbox.checked = true)); + pipelineRunBoxes.forEach(checkbox => { + checkbox.checked = true; + }); console.log('checked boxes: ', pipelineRunBoxes); } else { - pipelineRunBoxes.forEach(checkbox => (checkbox.checked = false)); + pipelineRunBoxes.forEach(checkbox => { + checkbox.checked = false; + }); } // Condition: If any of the children checkboxes are unchecked, then the main checkbox should be checked. @@ -108,6 +115,11 @@ pipelineRunsBox.checked = !anyUnchecked; } + // Cleans up event listeners before adding new ones + pipelineRunBoxes.forEach(checkbox => { + checkbox.removeEventListener('change', updateMainCheckbox); + }); + pipelineRunBoxes.forEach(checkbox => { checkbox.addEventListener('change', updateMainCheckbox); }); From 15c11ae42e4a180acb04bbac5fdc59adeca1bdee Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Thu, 14 Nov 2024 17:04:26 -0800 Subject: [PATCH 137/138] feat: render chat stream more continuously --- src/views/chatView/utils/TokenUtils.ts | 57 +++++++++++++++----------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 4ea394ab..07959e0b 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -13,6 +13,7 @@ import * as vscode from 'vscode'; import { ChatMessage } from '../../../types/ChatTypes'; import { addContext } from './ContextUtils'; +import { ms } from 'date-fns/locale'; let tokenjs: any; @@ -87,34 +88,44 @@ export async function* getChatResponse( { role: 'system', content: await addContext(context) }, ...messages, ]; + + const stream = await tokenjs.chat.completions.create({ + stream: true, + provider: provider.toLowerCase(), + model: model, + messages: fullMessages.map(msg => ({ + role: msg.role as 'system' | 'user' | 'assistant', + content: msg.content, + })), + }); + + let buffer = ''; + const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); + try { - const stream = await tokenjs.chat.completions.create({ - stream: true, - provider: provider.toLowerCase(), - model: model, - messages: fullMessages.map(msg => ({ - role: msg.role as 'system' | 'user' | 'assistant', - content: msg.content, - })), - }); + for await (const part of stream) { + if (part.choices[0]?.delta?.content) { + buffer += part.choices[0].delta.content; + } - try { - for await (const part of stream) { - if (part.choices[0]?.delta?.content) { - yield part.choices[0].delta.content; - } + // Stream from the buffer continuously + while (buffer.length > 0) { + yield buffer[0]; + buffer = buffer.slice(1); + await delay(5); } - } catch (streamError) { - console.error('Streaming error in getChatResponse:', streamError); - throw new Error( - `Streaming error with ${provider} API: ${streamError instanceof Error ? streamError.message : String(streamError)}` - ); } - } catch (error: any) { - console.error('Error in getChatResponse:', error); + + // Continue emptying the buffer after the stream ends + while (buffer.length > 0) { + yield buffer[0]; + buffer = buffer.slice(1); + await delay(5); + } + } catch (streamError) { + console.error('Streaming error in getChatResponse:', streamError); throw new Error( - `Error in getChatResponse: ${error instanceof Error ? error.message : `${provider} API: ${String(error)}`}`, - { cause: error } + `Streaming error with ${provider} API: ${streamError instanceof Error ? streamError.message : String(streamError)}` ); } } From d19c19907a76958c2b720d77ec5f9347bcf5b244 Mon Sep 17 00:00:00 2001 From: Alan Cho Date: Thu, 14 Nov 2024 17:48:08 -0800 Subject: [PATCH 138/138] chore: handled edge cases in rendering chat --- src/views/chatView/ChatDataProvider.ts | 6 ++-- src/views/chatView/utils/TokenUtils.ts | 40 +++----------------------- 2 files changed, 8 insertions(+), 38 deletions(-) diff --git a/src/views/chatView/ChatDataProvider.ts b/src/views/chatView/ChatDataProvider.ts index 3141e711..7355fa48 100644 --- a/src/views/chatView/ChatDataProvider.ts +++ b/src/views/chatView/ChatDataProvider.ts @@ -91,7 +91,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { private getAvailableModels(): string[] { switch (this.currentProvider) { case 'Gemini': - return ['gemini-1.5-pro', 'gemini-1.5-flash']; + return ['gemini-1.5-pro', 'gemini-1.5-flash', 'error']; case 'OpenAI': return ['gpt-4o-mini', 'gpt-3.5-turbo']; case 'Anthropic': @@ -135,6 +135,7 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { async addMessage(message: string, context?: string[], provider?: string, model?: string) { this.messages.push({ role: 'user', content: message }); + this.streamingMessage = null; this.updateWebviewContent(); try { @@ -168,8 +169,9 @@ export class ChatDataProvider implements vscode.WebviewViewProvider { } catch (error: any) { console.error('Error in addMessage:', error); const errorMessage = error instanceof Error ? error.message : 'An unexpected error occured.'; + // Save the error message so it'll still be rendered in subsequent messages + this.messages.push({ role: 'assistant', content: errorMessage }); this.sendMessageToWebview(`${errorMessage}`); - this.sendMessageToWebview('enableInput'); this._view?.webview.postMessage({ command: 'hideLoader' }); } diff --git a/src/views/chatView/utils/TokenUtils.ts b/src/views/chatView/utils/TokenUtils.ts index 07959e0b..6ca0b7a5 100644 --- a/src/views/chatView/utils/TokenUtils.ts +++ b/src/views/chatView/utils/TokenUtils.ts @@ -13,7 +13,6 @@ import * as vscode from 'vscode'; import { ChatMessage } from '../../../types/ChatTypes'; import { addContext } from './ContextUtils'; -import { ms } from 'date-fns/locale'; let tokenjs: any; @@ -41,41 +40,10 @@ export async function* getChatResponse( ): AsyncGenerator { const template = ` You are a ZenML assistant that summarizes users' ZenML information, problem solves users' ZenML problems, or optimizes users' code in their ZenML pipeline runs. - - Every time you get a user message, check the message for Context. - - Structure (with markdown) the output like this: - -
    -

    Category 1

    -
    - Key 1-1 - value 1-1 -
    - Key 1-2 - value 1-2 -

    -
    -

    Category 2

    -
    - Key 2-1 - value 2-1 -
    - Key 2-2 - value 2-2 - - To bold words, use tags. Do not ever use asterisks for formatting. - To write code blocks, use tags. - if there's an explanation at the end, add it like: - -

    -
    -

    Summary

    -
    -
    - Explanation -
  • point 1
  • -
  • point 2
  • + Example Response Format: +

    Section Title

    + Key: Value
    + Use for inline code and for bolding. `; if (!tokenjs) { throw new Error('TokenJS not initialized');