From 15cf921616bed2b0f711a39d2c4b9dcc1b8f59d3 Mon Sep 17 00:00:00 2001 From: Timothy Carambat Date: Thu, 16 May 2024 10:38:21 -0700 Subject: [PATCH] Support SQL Agent skill (#1411) * Support SQL Agent skill * add MSSQL agent connector * Add frontend to agent skills remove FAKE_DB mock reset skills to pickup child-skill dynamically * add prompt examples for tools on untooled * add better logging on SQL agents * Wipe toolruns on each chat relay so tools can be used within the same session * update comments --- .../SQLConnectorSelection/DBConnection.jsx | 47 ++ .../NewConnectionModal.jsx | 271 +++++++++++ .../SQLConnectorSelection/icons/mssql.png | Bin 0 -> 38612 bytes .../SQLConnectorSelection/icons/mysql.png | Bin 0 -> 12971 bytes .../icons/postgresql.png | Bin 0 -> 47152 bytes .../SQLConnectorSelection/index.jsx | 109 +++++ .../WorkspaceSettings/AgentConfig/index.jsx | 7 + server/endpoints/admin.js | 2 + server/models/systemSettings.js | 112 ++++- server/package.json | 4 + server/utils/agents/aibitat/index.js | 13 +- server/utils/agents/aibitat/plugins/index.js | 3 + .../plugins/sql-agent/SQLConnectors/MSSQL.js | 89 ++++ .../plugins/sql-agent/SQLConnectors/MySQL.js | 59 +++ .../sql-agent/SQLConnectors/Postgresql.js | 52 +++ .../plugins/sql-agent/SQLConnectors/index.js | 60 +++ .../plugins/sql-agent/get-table-schema.js | 98 ++++ .../agents/aibitat/plugins/sql-agent/index.js | 21 + .../plugins/sql-agent/list-database.js | 49 ++ .../aibitat/plugins/sql-agent/list-table.js | 85 ++++ .../agents/aibitat/plugins/sql-agent/query.js | 101 ++++ .../utils/agents/aibitat/plugins/websocket.js | 2 +- .../agents/aibitat/providers/anthropic.js | 2 +- .../utils/agents/aibitat/providers/azure.js | 7 +- .../agents/aibitat/providers/genericOpenAi.js | 6 +- server/utils/agents/aibitat/providers/groq.js | 2 +- .../agents/aibitat/providers/koboldcpp.js | 6 +- .../agents/aibitat/providers/lmstudio.js | 6 +- .../utils/agents/aibitat/providers/localai.js | 6 +- .../utils/agents/aibitat/providers/mistral.js | 6 +- .../utils/agents/aibitat/providers/ollama.js | 6 +- .../utils/agents/aibitat/providers/openai.js | 2 +- .../agents/aibitat/providers/openrouter.js | 6 +- .../agents/aibitat/providers/perplexity.js | 6 +- .../agents/aibitat/providers/textgenwebui.js | 6 +- .../agents/aibitat/providers/togetherai.js | 6 +- server/utils/agents/aibitat/utils/dedupe.js | 19 + server/utils/agents/defaults.js | 14 + server/utils/agents/index.js | 72 ++- server/yarn.lock | 442 +++++++++++++++++- 40 files changed, 1745 insertions(+), 59 deletions(-) create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/DBConnection.jsx create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/NewConnectionModal.jsx create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mssql.png create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mysql.png create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/postgresql.png create mode 100644 frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/index.jsx create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MySQL.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/Postgresql.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/index.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/get-table-schema.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/index.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/list-database.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/list-table.js create mode 100644 server/utils/agents/aibitat/plugins/sql-agent/query.js diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/DBConnection.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/DBConnection.jsx new file mode 100644 index 00000000..7a58da45 --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/DBConnection.jsx @@ -0,0 +1,47 @@ +import PostgreSQLLogo from "./icons/postgresql.png"; +import MySQLLogo from "./icons/mysql.png"; +import MSSQLLogo from "./icons/mssql.png"; +import { X } from "@phosphor-icons/react"; + +export const DB_LOGOS = { + postgresql: PostgreSQLLogo, + mysql: MySQLLogo, + "sql-server": MSSQLLogo, +}; + +export default function DBConnection({ connection, onRemove }) { + const { database_id, engine } = connection; + function removeConfirmation() { + if ( + !window.confirm( + `Delete ${database_id} from the list of available SQL connections? This cannot be undone.` + ) + ) { + return false; + } + onRemove(database_id); + } + + return ( +
+ {`${engine} +
+
+
{database_id}
+
{engine}
+
+ +
+
+ ); +} diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/NewConnectionModal.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/NewConnectionModal.jsx new file mode 100644 index 00000000..e76b27cc --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/NewConnectionModal.jsx @@ -0,0 +1,271 @@ +import { useState } from "react"; +import { createPortal } from "react-dom"; +import ModalWrapper from "@/components/ModalWrapper"; +import { WarningOctagon, X } from "@phosphor-icons/react"; +import { DB_LOGOS } from "./DBConnection"; + +function assembleConnectionString({ + engine, + username = "", + password = "", + host = "", + port = "", + database = "", +}) { + if ([username, password, host, database].every((i) => !!i) === false) + return `Please fill out all the fields above.`; + switch (engine) { + case "postgresql": + return `postgres://${username}:${password}@${host}:${port}/${database}`; + case "mysql": + return `mysql://${username}:${password}@${host}:${port}/${database}`; + case "sql-server": + return `mssql://${username}:${password}@${host}:${port}/${database}`; + default: + return null; + } +} + +const DEFAULT_ENGINE = "postgresql"; +const DEFAULT_CONFIG = { + username: null, + password: null, + host: null, + port: null, + database: null, +}; + +export default function NewSQLConnection({ isOpen, closeModal, onSubmit }) { + const [engine, setEngine] = useState(DEFAULT_ENGINE); + const [config, setConfig] = useState(DEFAULT_CONFIG); + if (!isOpen) return null; + + function handleClose() { + setEngine(DEFAULT_ENGINE); + setConfig(DEFAULT_CONFIG); + closeModal(); + } + + function onFormChange() { + const form = new FormData(document.getElementById("sql-connection-form")); + setConfig({ + username: form.get("username").trim(), + password: form.get("password"), + host: form.get("host").trim(), + port: form.get("port").trim(), + database: form.get("database").trim(), + }); + } + + async function handleUpdate(e) { + e.preventDefault(); + e.stopPropagation(); + const form = new FormData(e.target); + onSubmit({ + engine, + database_id: form.get("name"), + connectionString: assembleConnectionString({ engine, ...config }), + }); + handleClose(); + return false; + } + + // Cannot do nested forms, it will cause all sorts of issues, so we portal this out + // to the parent container form so we don't have nested forms. + return createPortal( + +
+
+
+

+ New SQL Connection +

+ +
+ +
+
+

+ Add the connection information for your database below and it + will be available for future SQL agent calls. +

+
+
+ +

+ WARNING: The SQL agent has been instructed to + only perform non-modifying queries. This does not{" "} + prevent a hallucination from still deleting data. Only + connect with a user who has READ_ONLY permissions. +

+
+ + +
+ setEngine("postgresql")} + /> + setEngine("mysql")} + /> + setEngine("sql-server")} + /> +
+
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+

+ {assembleConnectionString({ engine, ...config })} +

+
+
+ + +
+
+
+
+
, + document.getElementById("workspace-agent-settings-container") + ); +} + +function DBEngine({ provider, active, onClick }) { + return ( + + ); +} diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mssql.png b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mssql.png new file mode 100644 index 0000000000000000000000000000000000000000..7bd58a9a3eac3327f9f9703a081b79274fc8ee62 GIT binary patch literal 38612 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 literal 0 HcmV?d00001 diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mysql.png b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/mysql.png new file mode 100644 index 0000000000000000000000000000000000000000..5a440811548774bb7554a38ccfd82a300d1037fd GIT binary patch literal 12971 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 K;$@=x{{Id8SL%WQ literal 0 HcmV?d00001 diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/postgresql.png b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/icons/postgresql.png new file mode 100644 index 0000000000000000000000000000000000000000..67918df7a77905a065d286802efe3055969ec266 GIT binary patch literal 47152 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

xK0k(o)B2 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 E11Pf9Pyhe` literal 0 HcmV?d00001 diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/index.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/index.jsx new file mode 100644 index 00000000..9feb4b8b --- /dev/null +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/SQLConnectorSelection/index.jsx @@ -0,0 +1,109 @@ +import React, { useState } from "react"; +import DBConnection from "./DBConnection"; +import { Plus } from "@phosphor-icons/react"; +import NewSQLConnection from "./NewConnectionModal"; +import { useModal } from "@/hooks/useModal"; + +export default function AgentSQLConnectorSelection({ + skill, + settings, + toggleSkill, + enabled = false, +}) { + const { isOpen, openModal, closeModal } = useModal(); + const [connections, setConnections] = useState( + settings?.preferences?.agent_sql_connections || [] + ); + + return ( + <> +

+
+
+ + +
+

+ Enable your agent to be able to leverage SQL to answer you questions + by connecting to various SQL database providers. +

+
+ {enabled && ( + <> + + conn.action !== "remove") + )} + /> +
+

+ Your database connections +

+
+ {connections + .filter((connection) => connection.action !== "remove") + .map((connection) => ( + { + setConnections((prev) => + prev.map((conn) => { + if (conn.database_id === databaseId) + return { ...conn, action: "remove" }; + return conn; + }) + ); + }} + /> + ))} + +
+
+ + )} +
+ + setConnections((prev) => [...prev, { action: "add", ...newDb }]) + } + /> + + ); +} diff --git a/frontend/src/pages/WorkspaceSettings/AgentConfig/index.jsx b/frontend/src/pages/WorkspaceSettings/AgentConfig/index.jsx index c96cc124..02065251 100644 --- a/frontend/src/pages/WorkspaceSettings/AgentConfig/index.jsx +++ b/frontend/src/pages/WorkspaceSettings/AgentConfig/index.jsx @@ -5,6 +5,7 @@ import { castToType } from "@/utils/types"; import { useEffect, useRef, useState } from "react"; import AgentLLMSelection from "./AgentLLMSelection"; import AgentWebSearchSelection from "./WebSearchSelection"; +import AgentSQLConnectorSelection from "./SQLConnectorSelection"; import GenericSkill from "./GenericSkill"; import Admin from "@/models/admin"; import * as Skeleton from "react-loading-skeleton"; @@ -205,6 +206,12 @@ function AvailableAgentSkills({ skills, settings, toggleAgentSkill }) { toggleSkill={toggleAgentSkill} enabled={skills.includes("web-browsing")} /> + ); diff --git a/server/endpoints/admin.js b/server/endpoints/admin.js index 2ef611f6..959e023f 100644 --- a/server/endpoints/admin.js +++ b/server/endpoints/admin.js @@ -350,6 +350,8 @@ function adminEndpoints(app) { agent_search_provider: (await SystemSettings.get({ label: "agent_search_provider" })) ?.value || null, + agent_sql_connections: + await SystemSettings.brief.agent_sql_connections(), default_agent_skills: safeJsonParse( (await SystemSettings.get({ label: "default_agent_skills" })) diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js index 248ca8cd..7b4f21ee 100644 --- a/server/models/systemSettings.js +++ b/server/models/systemSettings.js @@ -2,8 +2,10 @@ process.env.NODE_ENV === "development" ? require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` }) : require("dotenv").config(); -const { isValidUrl } = require("../utils/http"); +const { default: slugify } = require("slugify"); +const { isValidUrl, safeJsonParse } = require("../utils/http"); const prisma = require("../utils/prisma"); +const { v4 } = require("uuid"); function isNullOrNaN(value) { if (value === null) return true; @@ -24,6 +26,7 @@ const SystemSettings = { "text_splitter_chunk_overlap", "agent_search_provider", "default_agent_skills", + "agent_sql_connections", ], validations: { footer_data: (updates) => { @@ -65,6 +68,7 @@ const SystemSettings = { }, agent_search_provider: (update) => { try { + if (update === "none") return null; if (!["google-search-engine", "serper-dot-dev"].includes(update)) throw new Error("Invalid SERP provider."); return String(update); @@ -85,6 +89,22 @@ const SystemSettings = { return JSON.stringify([]); } }, + agent_sql_connections: async (updates) => { + const existingConnections = safeJsonParse( + (await SystemSettings.get({ label: "agent_sql_connections" }))?.value, + [] + ); + try { + const updatedConnections = mergeConnections( + existingConnections, + safeJsonParse(updates, []) + ); + return JSON.stringify(updatedConnections); + } catch (e) { + console.error(`Failed to merge connections`); + return JSON.stringify(existingConnections ?? []); + } + }, }, currentSettings: async function () { const { hasVectorCachedFiles } = require("../utils/files"); @@ -204,22 +224,30 @@ const SystemSettings = { // that takes no user input for the keys being modified. _updateSettings: async function (updates = {}) { try { - const updatePromises = Object.keys(updates).map((key) => { - const validatedValue = this.validations.hasOwnProperty(key) - ? this.validations[key](updates[key]) - : updates[key]; + const updatePromises = []; + for (const key of Object.keys(updates)) { + let validatedValue = updates[key]; + if (this.validations.hasOwnProperty(key)) { + if (this.validations[key].constructor.name === "AsyncFunction") { + validatedValue = await this.validations[key](updates[key]); + } else { + validatedValue = this.validations[key](updates[key]); + } + } - return prisma.system_settings.upsert({ - where: { label: key }, - update: { - value: validatedValue === null ? null : String(validatedValue), - }, - create: { - label: key, - value: validatedValue === null ? null : String(validatedValue), - }, - }); - }); + updatePromises.push( + prisma.system_settings.upsert({ + where: { label: key }, + update: { + value: validatedValue === null ? null : String(validatedValue), + }, + create: { + label: key, + value: validatedValue === null ? null : String(validatedValue), + }, + }) + ); + } await Promise.all(updatePromises); return { success: true, error: null }; @@ -392,6 +420,58 @@ const SystemSettings = { CohereModelPref: process.env.COHERE_MODEL_PREF, }; }, + + // For special retrieval of a key setting that does not expose any credential information + brief: { + agent_sql_connections: async function () { + const setting = await SystemSettings.get({ + label: "agent_sql_connections", + }); + if (!setting) return []; + return safeJsonParse(setting.value, []).map((dbConfig) => { + const { connectionString, ...rest } = dbConfig; + return rest; + }); + }, + }, }; +function mergeConnections(existingConnections = [], updates = []) { + let updatedConnections = [...existingConnections]; + const existingDbIds = existingConnections.map((conn) => conn.database_id); + + // First remove all 'action:remove' candidates from existing connections. + const toRemove = updates + .filter((conn) => conn.action === "remove") + .map((conn) => conn.database_id); + updatedConnections = updatedConnections.filter( + (conn) => !toRemove.includes(conn.database_id) + ); + + // Next add all 'action:add' candidates into the updatedConnections; We DO NOT validate the connection strings. + // but we do validate their database_id is unique. + updates + .filter((conn) => conn.action === "add") + .forEach((update) => { + if (!update.connectionString) return; // invalid connection string + + // Remap name to be unique to entire set. + if (existingDbIds.includes(update.database_id)) { + update.database_id = slugify( + `${update.database_id}-${v4().slice(0, 4)}` + ); + } else { + update.database_id = slugify(update.database_id); + } + + updatedConnections.push({ + engine: update.engine, + database_id: update.database_id, + connectionString: update.connectionString, + }); + }); + + return updatedConnections; +} + module.exports.SystemSettings = SystemSettings; diff --git a/server/package.json b/server/package.json index 73b947c4..4f995470 100644 --- a/server/package.json +++ b/server/package.json @@ -58,11 +58,14 @@ "langchain": "0.1.36", "mime": "^3.0.0", "moment": "^2.29.4", + "mssql": "^10.0.2", "multer": "^1.4.5-lts.1", + "mysql2": "^3.9.7", "node-html-markdown": "^1.3.0", "node-llama-cpp": "^2.8.0", "ollama": "^0.5.0", "openai": "4.38.5", + "pg": "^8.11.5", "pinecone-client": "^1.1.0", "pluralize": "^8.0.0", "posthog-node": "^3.1.1", @@ -72,6 +75,7 @@ "sqlite3": "^5.1.6", "swagger-autogen": "^2.23.5", "swagger-ui-express": "^5.0.0", + "url-pattern": "^1.0.3", "uuid": "^9.0.0", "uuid-apikey": "^1.5.3", "vectordb": "0.4.11", diff --git a/server/utils/agents/aibitat/index.js b/server/utils/agents/aibitat/index.js index f21c4aa4..fa490edb 100644 --- a/server/utils/agents/aibitat/index.js +++ b/server/utils/agents/aibitat/index.js @@ -498,6 +498,17 @@ Only return the role. return availableNodes[Math.floor(Math.random() * availableNodes.length)]; } + /** + * + * @param {string} pluginName this name of the plugin being called + * @returns string of the plugin to be called compensating for children denoted by # in the string. + * eg: sql-agent:list-database-connections + */ + #parseFunctionName(pluginName = "") { + if (!pluginName.includes("#")) return pluginName; + return pluginName.split("#")[1]; + } + /** * Check if the chat has reached the maximum number of rounds. */ @@ -550,7 +561,7 @@ ${this.getHistory({ to: route.to }) // get the functions that the node can call const functions = fromConfig.functions - ?.map((name) => this.functions.get(name)) + ?.map((name) => this.functions.get(this.#parseFunctionName(name))) .filter((a) => !!a); const provider = this.getProviderForConfig({ diff --git a/server/utils/agents/aibitat/plugins/index.js b/server/utils/agents/aibitat/plugins/index.js index d9ff544e..9a7ee7a0 100644 --- a/server/utils/agents/aibitat/plugins/index.js +++ b/server/utils/agents/aibitat/plugins/index.js @@ -6,6 +6,7 @@ const { saveFileInBrowser } = require("./save-file-browser.js"); const { chatHistory } = require("./chat-history.js"); const { memory } = require("./memory.js"); const { rechart } = require("./rechart.js"); +const { sqlAgent } = require("./sql-agent/index.js"); module.exports = { webScraping, @@ -16,6 +17,7 @@ module.exports = { chatHistory, memory, rechart, + sqlAgent, // Plugin name aliases so they can be pulled by slug as well. [webScraping.name]: webScraping, @@ -26,4 +28,5 @@ module.exports = { [chatHistory.name]: chatHistory, [memory.name]: memory, [rechart.name]: rechart, + [sqlAgent.name]: sqlAgent, }; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js new file mode 100644 index 00000000..ed75aa7a --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MSSQL.js @@ -0,0 +1,89 @@ +const mssql = require("mssql"); +const UrlPattern = require("url-pattern"); + +class MSSQLConnector { + #connected = false; + database_id = ""; + connectionConfig = { + user: null, + password: null, + database: null, + server: null, + port: null, + pool: { + max: 10, + min: 0, + idleTimeoutMillis: 30000, + }, + options: { + encrypt: false, + trustServerCertificate: true, + }, + }; + + constructor( + config = { + // we will force into RFC-3986 from DB + // eg: mssql://user:password@server:port/database?{...opts} + connectionString: null, // we will force into RFC-3986 + } + ) { + this.connectionString = config.connectionString; + this._client = null; + this.#parseDatabase(); + } + + #parseDatabase() { + const connectionPattern = new UrlPattern( + "mssql\\://:username\\::password@*\\::port/:database*" + ); + const match = connectionPattern.match(this.connectionString); + this.database_id = match?.database; + this.connectionConfig = { + ...this.connectionConfig, + user: match?.username, + password: match?.password, + database: match?.database, + server: match?._[0], + port: match?.port ? Number(match.port) : null, + }; + } + + async connect() { + this._client = await mssql.connect(this.connectionConfig); + this.#connected = true; + return this._client; + } + + /** + * + * @param {string} queryString the SQL query to be run + * @returns {import(".").QueryResult} + */ + async runQuery(queryString = "") { + const result = { rows: [], count: 0, error: null }; + try { + if (!this.#connected) await this.connect(); + + const query = await this._client.query(queryString); + result.rows = query.recordset; + result.count = query.rowsAffected.reduce((sum, a) => sum + a, 0); + } catch (err) { + console.log(this.constructor.name, err); + result.error = err.message; + } finally { + await this._client.close(); + this.#connected = false; + } + return result; + } + + getTablesSql() { + return `SELECT name FROM sysobjects WHERE xtype='U';`; + } + getTableSchemaSql(table_name) { + return `SELECT COLUMN_NAME,COLUMN_DEFAULT,IS_NULLABLE,DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='${table_name}'`; + } +} + +module.exports.MSSQLConnector = MSSQLConnector; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MySQL.js b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MySQL.js new file mode 100644 index 00000000..d9982ab3 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/MySQL.js @@ -0,0 +1,59 @@ +const mysql = require("mysql2/promise"); +const UrlPattern = require("url-pattern"); + +class MySQLConnector { + #connected = false; + database_id = ""; + constructor( + config = { + connectionString: null, + } + ) { + this.connectionString = config.connectionString; + this._client = null; + this.database_id = this.#parseDatabase(); + } + + #parseDatabase() { + const connectionPattern = new UrlPattern("mysql\\://*@*/:database*"); + const match = connectionPattern.match(this.connectionString); + return match?.database; + } + + async connect() { + this._client = await mysql.createConnection({ uri: this.connectionString }); + this.#connected = true; + return this._client; + } + + /** + * + * @param {string} queryString the SQL query to be run + * @returns {import(".").QueryResult} + */ + async runQuery(queryString = "") { + const result = { rows: [], count: 0, error: null }; + try { + if (!this.#connected) await this.connect(); + const [query] = await this._client.query(queryString); + result.rows = query; + result.count = query?.length; + } catch (err) { + console.log(this.constructor.name, err); + result.error = err.message; + } finally { + await this._client.end(); + this.#connected = false; + } + return result; + } + + getTablesSql() { + return `SELECT table_name FROM information_schema.tables WHERE table_schema = '${this.database_id}'`; + } + getTableSchemaSql(table_name) { + return `SHOW COLUMNS FROM ${this.database_id}.${table_name};`; + } +} + +module.exports.MySQLConnector = MySQLConnector; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/Postgresql.js b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/Postgresql.js new file mode 100644 index 00000000..463fea51 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/Postgresql.js @@ -0,0 +1,52 @@ +const pgSql = require("pg"); + +class PostgresSQLConnector { + #connected = false; + constructor( + config = { + connectionString: null, + } + ) { + this.connectionString = config.connectionString; + this._client = new pgSql.Client({ + connectionString: this.connectionString, + }); + } + + async connect() { + await this._client.connect(); + this.#connected = true; + return this._client; + } + + /** + * + * @param {string} queryString the SQL query to be run + * @returns {import(".").QueryResult} + */ + async runQuery(queryString = "") { + const result = { rows: [], count: 0, error: null }; + try { + if (!this.#connected) await this.connect(); + const query = await this._client.query(queryString); + result.rows = query.rows; + result.count = query.rowCount; + } catch (err) { + console.log(this.constructor.name, err); + result.error = err.message; + } finally { + await this._client.end(); + this.#connected = false; + } + return result; + } + + getTablesSql() { + return `SELECT * FROM pg_catalog.pg_tables WHERE schemaname = 'public'`; + } + getTableSchemaSql(table_name) { + return ` select column_name, data_type, character_maximum_length, column_default, is_nullable from INFORMATION_SCHEMA.COLUMNS where table_name = '${table_name}'`; + } +} + +module.exports.PostgresSQLConnector = PostgresSQLConnector; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/index.js b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/index.js new file mode 100644 index 00000000..9cf1e1ff --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/SQLConnectors/index.js @@ -0,0 +1,60 @@ +const { SystemSettings } = require("../../../../../../models/systemSettings"); +const { safeJsonParse } = require("../../../../../http"); + +/** + * @typedef {('postgresql'|'mysql'|'sql-server')} SQLEngine + */ + +/** + * @typedef {Object} QueryResult + * @property {[number]} rows - The query result rows + * @property {number} count - Number of rows the query returned/changed + * @property {string|null} error - Error string if there was an issue + */ + +/** + * A valid database SQL connection object + * @typedef {Object} SQLConnection + * @property {string} database_id - Unique identifier of the database connection + * @property {SQLEngine} engine - Engine used by connection + * @property {string} connectionString - RFC connection string for db + */ + +/** + * @param {SQLEngine} identifier + * @param {object} connectionConfig + * @returns Database Connection Engine Class for SQLAgent or throws error + */ +function getDBClient(identifier = "", connectionConfig = {}) { + switch (identifier) { + case "mysql": + const { MySQLConnector } = require("./MySQL"); + return new MySQLConnector(connectionConfig); + case "postgresql": + const { PostgresSQLConnector } = require("./Postgresql"); + return new PostgresSQLConnector(connectionConfig); + case "sql-server": + const { MSSQLConnector } = require("./MSSQL"); + return new MSSQLConnector(connectionConfig); + default: + throw new Error( + `There is no supported database connector for ${identifier}` + ); + } +} + +/** + * Lists all of the known database connection that can be used by the agent. + * @returns {Promise<[SQLConnection]>} + */ +async function listSQLConnections() { + return safeJsonParse( + (await SystemSettings.get({ label: "agent_sql_connections" }))?.value, + [] + ); +} + +module.exports = { + getDBClient, + listSQLConnections, +}; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/get-table-schema.js b/server/utils/agents/aibitat/plugins/sql-agent/get-table-schema.js new file mode 100644 index 00000000..2f66d119 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/get-table-schema.js @@ -0,0 +1,98 @@ +module.exports.SqlAgentGetTableSchema = { + name: "sql-get-table-schema", + plugin: function () { + const { + listSQLConnections, + getDBClient, + } = require("./SQLConnectors/index.js"); + + return { + name: "sql-get-table-schema", + setup(aibitat) { + aibitat.function({ + super: aibitat, + name: this.name, + description: + "Gets the table schema in SQL for a given `table` and `database_id`", + examples: [ + { + prompt: "What does the customers table in access-logs look like?", + call: JSON.stringify({ + database_id: "access-logs", + table_name: "customers", + }), + }, + { + prompt: + "Get me the full name of a company in records-main, the table should be call comps", + call: JSON.stringify({ + database_id: "records-main", + table_name: "comps", + }), + }, + ], + parameters: { + $schema: "http://json-schema.org/draft-07/schema#", + type: "object", + properties: { + database_id: { + type: "string", + description: + "The database identifier for which we will connect to to query the table schema. This is a required field.", + }, + table_name: { + type: "string", + description: + "The database identifier for the table name we want the schema for. This is a required field.", + }, + }, + additionalProperties: false, + }, + required: ["database_id", "table_name"], + handler: async function ({ database_id = "", table_name = "" }) { + this.super.handlerProps.log(`Using the sql-get-table-schema tool.`); + try { + const databaseConfig = (await listSQLConnections()).find( + (db) => db.database_id === database_id + ); + if (!databaseConfig) { + this.super.handlerProps.log( + `sql-get-table-schema to find config!.`, + database_id + ); + return `No database connection for ${database_id} was found!`; + } + + const db = getDBClient(databaseConfig.engine, databaseConfig); + this.super.introspect( + `${this.caller}: Querying the table schema for ${table_name} in the ${databaseConfig.database_id} database.` + ); + this.super.introspect( + `Running SQL: ${db.getTableSchemaSql(table_name)}` + ); + const result = await db.runQuery( + db.getTableSchemaSql(table_name) + ); + + if (result.error) { + this.super.handlerProps.log( + `sql-get-table-schema tool reported error`, + result.error + ); + this.super.introspect(`Error: ${result.error}`); + return `There was an error running the query: ${result.error}`; + } + + return JSON.stringify(result); + } catch (e) { + this.super.handlerProps.log( + `sql-get-table-schema raised an error. ${e.message}` + ); + return e.message; + } + }, + }); + }, + }; + }, +}; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/index.js b/server/utils/agents/aibitat/plugins/sql-agent/index.js new file mode 100644 index 00000000..b7c1ed7d --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/index.js @@ -0,0 +1,21 @@ +const { SqlAgentGetTableSchema } = require("./get-table-schema"); +const { SqlAgentListDatabase } = require("./list-database"); +const { SqlAgentListTables } = require("./list-table"); +const { SqlAgentQuery } = require("./query"); + +const sqlAgent = { + name: "sql-agent", + startupConfig: { + params: {}, + }, + plugin: [ + SqlAgentListDatabase, + SqlAgentListTables, + SqlAgentGetTableSchema, + SqlAgentQuery, + ], +}; + +module.exports = { + sqlAgent, +}; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/list-database.js b/server/utils/agents/aibitat/plugins/sql-agent/list-database.js new file mode 100644 index 00000000..20e67c28 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/list-database.js @@ -0,0 +1,49 @@ +module.exports.SqlAgentListDatabase = { + name: "sql-list-databases", + plugin: function () { + const { listSQLConnections } = require("./SQLConnectors"); + return { + name: "sql-list-databases", + setup(aibitat) { + aibitat.function({ + super: aibitat, + name: this.name, + description: + "List all available databases via `list_databases` you currently have access to. Returns a unique string identifier `database_id` that can be used for future calls.", + examples: [ + { + prompt: "What databases can you access?", + call: JSON.stringify({}), + }, + { + prompt: "What databases can you tell me about?", + call: JSON.stringify({}), + }, + { + prompt: "Is there a database named erp-logs you can access?", + call: JSON.stringify({}), + }, + ], + parameters: { + $schema: "http://json-schema.org/draft-07/schema#", + type: "object", + properties: {}, + additionalProperties: false, + }, + handler: async function () { + this.super.handlerProps.log(`Using the sql-list-databases tool.`); + this.super.introspect( + `${this.caller}: Checking what are the available databases.` + ); + + const connections = (await listSQLConnections()).map((conn) => { + const { connectionString, ...rest } = conn; + return rest; + }); + return JSON.stringify(connections); + }, + }); + }, + }; + }, +}; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/list-table.js b/server/utils/agents/aibitat/plugins/sql-agent/list-table.js new file mode 100644 index 00000000..d186bf01 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/list-table.js @@ -0,0 +1,85 @@ +module.exports.SqlAgentListTables = { + name: "sql-list-tables", + plugin: function () { + const { + listSQLConnections, + getDBClient, + } = require("./SQLConnectors/index.js"); + + return { + name: "sql-list-tables", + setup(aibitat) { + aibitat.function({ + super: aibitat, + name: this.name, + description: + "List all available tables in a database via its `database_id`.", + examples: [ + { + prompt: "What tables are there in the `access-logs` database?", + call: JSON.stringify({ database_id: "access-logs" }), + }, + { + prompt: + "What information can you access in the customer_accts postgres db?", + call: JSON.stringify({ database_id: "customer_accts" }), + }, + { + prompt: "Can you tell me what is in the primary-logs db?", + call: JSON.stringify({ database_id: "primary-logs" }), + }, + ], + parameters: { + $schema: "http://json-schema.org/draft-07/schema#", + type: "object", + properties: { + database_id: { + type: "string", + description: + "The database identifier for which we will list all tables for. This is a required parameter", + }, + }, + additionalProperties: false, + }, + required: ["database_id"], + handler: async function ({ database_id = "" }) { + try { + this.super.handlerProps.log(`Using the sql-list-tables tool.`); + const databaseConfig = (await listSQLConnections()).find( + (db) => db.database_id === database_id + ); + if (!databaseConfig) { + this.super.handlerProps.log( + `sql-list-tables failed to find config!.`, + database_id + ); + return `No database connection for ${database_id} was found!`; + } + + const db = getDBClient(databaseConfig.engine, databaseConfig); + this.super.introspect( + `${this.caller}: Checking what are the available tables in the ${databaseConfig.database_id} database.` + ); + + this.super.introspect(`Running SQL: ${db.getTablesSql()}`); + const result = await db.runQuery(db.getTablesSql(database_id)); + if (result.error) { + this.super.handlerProps.log( + `sql-list-tables tool reported error`, + result.error + ); + this.super.introspect(`Error: ${result.error}`); + return `There was an error running the query: ${result.error}`; + } + + return JSON.stringify(result); + } catch (e) { + console.error(e); + return e.message; + } + }, + }); + }, + }; + }, +}; diff --git a/server/utils/agents/aibitat/plugins/sql-agent/query.js b/server/utils/agents/aibitat/plugins/sql-agent/query.js new file mode 100644 index 00000000..fca1aeb0 --- /dev/null +++ b/server/utils/agents/aibitat/plugins/sql-agent/query.js @@ -0,0 +1,101 @@ +module.exports.SqlAgentQuery = { + name: "sql-query", + plugin: function () { + const { + getDBClient, + listSQLConnections, + } = require("./SQLConnectors/index.js"); + + return { + name: "sql-query", + setup(aibitat) { + aibitat.function({ + super: aibitat, + name: this.name, + description: + "Run a read-only SQL query on a `database_id` which will return up rows of data related to the query. The query must only be SELECT statements which do not modify the table data. There should be a reasonable LIMIT on the return quantity to prevent long-running or queries which crash the db.", + examples: [ + { + prompt: "How many customers are in dvd-rentals?", + call: JSON.stringify({ + database_id: "dvd-rentals", + sql_query: "SELECT * FROM customers", + }), + }, + { + prompt: "Can you tell me the total volume of sales last month?", + call: JSON.stringify({ + database_id: "sales-db", + sql_query: + "SELECT SUM(sale_amount) AS total_sales FROM sales WHERE sale_date >= DATEADD(month, -1, DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1)) AND sale_date < DATEFROMPARTS(YEAR(GETDATE()), MONTH(GETDATE()), 1)", + }), + }, + { + prompt: + "Do we have anyone in the staff table for our production db named 'sam'? ", + call: JSON.stringify({ + database_id: "production", + sql_query: + "SElECT * FROM staff WHERE first_name='sam%' OR last_name='sam%'", + }), + }, + ], + parameters: { + $schema: "http://json-schema.org/draft-07/schema#", + type: "object", + properties: { + database_id: { + type: "string", + description: + "The database identifier for which we will connect to to query the table schema. This is required to run the SQL query.", + }, + sql_query: { + type: "string", + description: + "The raw SQL query to run. Should be a query which does not modify the table and will return results.", + }, + }, + additionalProperties: false, + }, + required: ["database_id", "table_name"], + handler: async function ({ database_id = "", sql_query = "" }) { + this.super.handlerProps.log(`Using the sql-query tool.`); + try { + const databaseConfig = (await listSQLConnections()).find( + (db) => db.database_id === database_id + ); + if (!databaseConfig) { + this.super.handlerProps.log( + `sql-query failed to find config!.`, + database_id + ); + return `No database connection for ${database_id} was found!`; + } + + this.super.introspect( + `${this.caller}: Im going to run a query on the ${database_id} to get an answer.` + ); + const db = getDBClient(databaseConfig.engine, databaseConfig); + + this.super.introspect(`Running SQL: ${sql_query}`); + const result = await db.runQuery(sql_query); + if (result.error) { + this.super.handlerProps.log( + `sql-query tool reported error`, + result.error + ); + this.super.introspect(`Error: ${result.error}`); + return `There was an error running the query: ${result.error}`; + } + + return JSON.stringify(result); + } catch (e) { + console.error(e); + return e.message; + } + }, + }); + }, + }; + }, +}; diff --git a/server/utils/agents/aibitat/plugins/websocket.js b/server/utils/agents/aibitat/plugins/websocket.js index b6154984..f5c73b33 100644 --- a/server/utils/agents/aibitat/plugins/websocket.js +++ b/server/utils/agents/aibitat/plugins/websocket.js @@ -49,7 +49,7 @@ const websocket = { setup(aibitat) { aibitat.onError(async (error) => { if (!!error?.message) { - console.error(chalk.red(` error: ${error.message}`)); + console.error(chalk.red(` error: ${error.message}`), error); aibitat.introspect( `Error encountered while running: ${error.message}` ); diff --git a/server/utils/agents/aibitat/providers/anthropic.js b/server/utils/agents/aibitat/providers/anthropic.js index 307731ba..b69b14d6 100644 --- a/server/utils/agents/aibitat/providers/anthropic.js +++ b/server/utils/agents/aibitat/providers/anthropic.js @@ -3,7 +3,7 @@ const { RetryError } = require("../error.js"); const Provider = require("./ai-provider.js"); /** - * The provider for the Anthropic API. + * The agent provider for the Anthropic API. * By default, the model is set to 'claude-2'. */ class AnthropicProvider extends Provider { diff --git a/server/utils/agents/aibitat/providers/azure.js b/server/utils/agents/aibitat/providers/azure.js index cdcf7618..0ecc398f 100644 --- a/server/utils/agents/aibitat/providers/azure.js +++ b/server/utils/agents/aibitat/providers/azure.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the Azure OpenAI API. + * The agent provider for the Azure OpenAI API. */ class AzureOpenAiProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -84,6 +84,11 @@ class AzureOpenAiProvider extends InheritMultiple([Provider, UnTooled]) { ); completion = response.choices[0].message; } + + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0 }; } catch (error) { throw error; diff --git a/server/utils/agents/aibitat/providers/genericOpenAi.js b/server/utils/agents/aibitat/providers/genericOpenAi.js index e41476d2..a1b2db3e 100644 --- a/server/utils/agents/aibitat/providers/genericOpenAi.js +++ b/server/utils/agents/aibitat/providers/genericOpenAi.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the Generic OpenAI provider. + * The agent provider for the Generic OpenAI provider. * Since we cannot promise the generic provider even supports tool calling * which is nearly 100% likely it does not, we can just wrap it in untooled * which often is far better anyway. @@ -94,6 +94,10 @@ class GenericOpenAiProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/groq.js b/server/utils/agents/aibitat/providers/groq.js index 720d8743..01f69f7c 100644 --- a/server/utils/agents/aibitat/providers/groq.js +++ b/server/utils/agents/aibitat/providers/groq.js @@ -3,7 +3,7 @@ const Provider = require("./ai-provider.js"); const { RetryError } = require("../error.js"); /** - * The provider for the Groq provider. + * The agent provider for the Groq provider. * Using OpenAI tool calling with groq really sucks right now * its just fast and bad. We should probably migrate this to Untooled to improve * coherence. diff --git a/server/utils/agents/aibitat/providers/koboldcpp.js b/server/utils/agents/aibitat/providers/koboldcpp.js index 77088263..2dd12784 100644 --- a/server/utils/agents/aibitat/providers/koboldcpp.js +++ b/server/utils/agents/aibitat/providers/koboldcpp.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the KoboldCPP provider. + * The agent provider for the KoboldCPP provider. */ class KoboldCPPProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -89,6 +89,10 @@ class KoboldCPPProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/lmstudio.js b/server/utils/agents/aibitat/providers/lmstudio.js index f5c4a2e8..258f2e29 100644 --- a/server/utils/agents/aibitat/providers/lmstudio.js +++ b/server/utils/agents/aibitat/providers/lmstudio.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the LMStudio provider. + * The agent provider for the LMStudio. */ class LMStudioProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -89,6 +89,10 @@ class LMStudioProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/localai.js b/server/utils/agents/aibitat/providers/localai.js index 161172c2..bd0c3b52 100644 --- a/server/utils/agents/aibitat/providers/localai.js +++ b/server/utils/agents/aibitat/providers/localai.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the LocalAI provider. + * The agent provider for the LocalAI provider. */ class LocalAiProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -93,6 +93,10 @@ class LocalAiProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0 }; } catch (error) { throw error; diff --git a/server/utils/agents/aibitat/providers/mistral.js b/server/utils/agents/aibitat/providers/mistral.js index cdc2a5e7..aa2c1c6e 100644 --- a/server/utils/agents/aibitat/providers/mistral.js +++ b/server/utils/agents/aibitat/providers/mistral.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the Mistral provider. + * The agent provider for the Mistral provider. * Mistral limits what models can call tools and even when using those * the model names change and dont match docs. When you do have the right model * it still fails and is not truly OpenAI compatible so its easier to just wrap @@ -93,6 +93,10 @@ class MistralProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/ollama.js b/server/utils/agents/aibitat/providers/ollama.js index d52d80ca..5cb529e5 100644 --- a/server/utils/agents/aibitat/providers/ollama.js +++ b/server/utils/agents/aibitat/providers/ollama.js @@ -4,7 +4,7 @@ const UnTooled = require("./helpers/untooled.js"); const { Ollama } = require("ollama"); /** - * The provider for the Ollama provider. + * The agent provider for the Ollama provider. */ class OllamaProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -83,6 +83,10 @@ class OllamaProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/openai.js b/server/utils/agents/aibitat/providers/openai.js index 3fcc46ca..2866a759 100644 --- a/server/utils/agents/aibitat/providers/openai.js +++ b/server/utils/agents/aibitat/providers/openai.js @@ -3,7 +3,7 @@ const Provider = require("./ai-provider.js"); const { RetryError } = require("../error.js"); /** - * The provider for the OpenAI API. + * The agent provider for the OpenAI API. * By default, the model is set to 'gpt-3.5-turbo'. */ class OpenAIProvider extends Provider { diff --git a/server/utils/agents/aibitat/providers/openrouter.js b/server/utils/agents/aibitat/providers/openrouter.js index 81297ae2..50c0868d 100644 --- a/server/utils/agents/aibitat/providers/openrouter.js +++ b/server/utils/agents/aibitat/providers/openrouter.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the OpenRouter provider. + * The agent provider for the OpenRouter provider. */ class OpenRouterProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -93,6 +93,10 @@ class OpenRouterProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/perplexity.js b/server/utils/agents/aibitat/providers/perplexity.js index 29970fd0..07220695 100644 --- a/server/utils/agents/aibitat/providers/perplexity.js +++ b/server/utils/agents/aibitat/providers/perplexity.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the Perplexity provider. + * The agent provider for the Perplexity provider. */ class PerplexityProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -89,6 +89,10 @@ class PerplexityProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/textgenwebui.js b/server/utils/agents/aibitat/providers/textgenwebui.js index d1e42425..7ef1cf4c 100644 --- a/server/utils/agents/aibitat/providers/textgenwebui.js +++ b/server/utils/agents/aibitat/providers/textgenwebui.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the Oobabooga provider. + * The agent provider for the Oobabooga provider. */ class TextWebGenUiProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -88,6 +88,10 @@ class TextWebGenUiProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/providers/togetherai.js b/server/utils/agents/aibitat/providers/togetherai.js index 4ea5e11c..78cbb512 100644 --- a/server/utils/agents/aibitat/providers/togetherai.js +++ b/server/utils/agents/aibitat/providers/togetherai.js @@ -4,7 +4,7 @@ const InheritMultiple = require("./helpers/classes.js"); const UnTooled = require("./helpers/untooled.js"); /** - * The provider for the TogetherAI provider. + * The agent provider for the TogetherAI provider. */ class TogetherAIProvider extends InheritMultiple([Provider, UnTooled]) { model; @@ -89,6 +89,10 @@ class TogetherAIProvider extends InheritMultiple([Provider, UnTooled]) { completion = response.choices[0].message; } + // The UnTooled class inherited Deduplicator is mostly useful to prevent the agent + // from calling the exact same function over and over in a loop within a single chat exchange + // _but_ we should enable it to call previously used tools in a new chat interaction. + this.deduplicator.reset("runs"); return { result: completion.content, cost: 0, diff --git a/server/utils/agents/aibitat/utils/dedupe.js b/server/utils/agents/aibitat/utils/dedupe.js index 8c4d582d..321f6929 100644 --- a/server/utils/agents/aibitat/utils/dedupe.js +++ b/server/utils/agents/aibitat/utils/dedupe.js @@ -38,6 +38,25 @@ class Deduplicator { return this.#hashes.hasOwnProperty(newSig); } + /** + * Resets the object property for this instance of the Deduplicator class + * @param {('runs'|'cooldowns'|'uniques')} type - The type of prop to reset + */ + reset(type = "runs") { + switch (type) { + case "runs": + this.#hashes = {}; + break; + case "cooldowns": + this.#cooldowns = {}; + break; + case "uniques": + this.#uniques = {}; + break; + } + return; + } + startCooldown( key, parameters = { diff --git a/server/utils/agents/defaults.js b/server/utils/agents/defaults.js index 4e12b906..796a7bbc 100644 --- a/server/utils/agents/defaults.js +++ b/server/utils/agents/defaults.js @@ -25,8 +25,22 @@ const WORKSPACE_AGENT = { const _setting = ( await SystemSettings.get({ label: "default_agent_skills" }) )?.value; + safeJsonParse(_setting, []).forEach((skillName) => { if (!AgentPlugins.hasOwnProperty(skillName)) return; + + // This is a plugin module with many sub-children plugins who + // need to be named via `${parent}#${child}` naming convention + if (Array.isArray(AgentPlugins[skillName].plugin)) { + for (const subPlugin of AgentPlugins[skillName].plugin) { + defaultFunctions.push( + `${AgentPlugins[skillName].name}#${subPlugin.name}` + ); + } + return; + } + + // This is normal single-stage plugin defaultFunctions.push(AgentPlugins[skillName].name); }); diff --git a/server/utils/agents/index.js b/server/utils/agents/index.js index 08ce5558..cf6f312d 100644 --- a/server/utils/agents/index.js +++ b/server/utils/agents/index.js @@ -197,8 +197,60 @@ class AgentHandler { this.invocation = invocation ?? null; } + #parseCallOptions(args, config = {}, pluginName) { + const callOpts = {}; + for (const [param, definition] of Object.entries(config)) { + if ( + definition.required && + (!args.hasOwnProperty(param) || args[param] === null) + ) { + this.log( + `'${param}' required parameter for '${pluginName}' plugin is missing. Plugin may not function or crash agent.` + ); + continue; + } + callOpts[param] = args.hasOwnProperty(param) + ? args[param] + : definition.default || null; + } + return callOpts; + } + #attachPlugins(args) { for (const name of this.#funcsToLoad) { + // Load child plugin + if (name.includes("#")) { + const [parent, childPluginName] = name.split("#"); + if (!AgentPlugins.hasOwnProperty(parent)) { + this.log( + `${parent} is not a valid plugin. Skipping inclusion to agent cluster.` + ); + continue; + } + + const childPlugin = AgentPlugins[parent].plugin.find( + (child) => child.name === childPluginName + ); + if (!childPlugin) { + this.log( + `${parent} does not have child plugin named ${childPluginName}. Skipping inclusion to agent cluster.` + ); + continue; + } + + const callOpts = this.#parseCallOptions( + args, + childPlugin?.startupConfig?.params, + name + ); + this.aibitat.use(childPlugin.plugin(callOpts)); + this.log( + `Attached ${parent}:${childPluginName} plugin to Agent cluster` + ); + continue; + } + + // Load single-stage plugin. if (!AgentPlugins.hasOwnProperty(name)) { this.log( `${name} is not a valid plugin. Skipping inclusion to agent cluster.` @@ -206,24 +258,10 @@ class AgentHandler { continue; } - const callOpts = {}; - for (const [param, definition] of Object.entries( + const callOpts = this.#parseCallOptions( + args, AgentPlugins[name].startupConfig.params - )) { - if ( - definition.required && - (!args.hasOwnProperty(param) || args[param] === null) - ) { - this.log( - `'${param}' required parameter for '${name}' plugin is missing. Plugin may not function or crash agent.` - ); - continue; - } - callOpts[param] = args.hasOwnProperty(param) - ? args[param] - : definition.default || null; - } - + ); const AIbitatPlugin = AgentPlugins[name]; this.aibitat.use(AIbitatPlugin.plugin(callOpts)); this.log(`Attached ${name} plugin to Agent cluster`); diff --git a/server/yarn.lock b/server/yarn.lock index 9e4f184d..d274e574 100644 --- a/server/yarn.lock +++ b/server/yarn.lock @@ -82,6 +82,13 @@ "@azure/core-util" "^1.0.0" tslib "^2.6.2" +"@azure/abort-controller@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.1.0.tgz#788ee78457a55af8a1ad342acb182383d2119249" + integrity sha512-TrRLIoSQVzfAJX9H1JeFjzAoDGcoK1IYX1UImfceTZpsyYfWr09Ss1aHW1y5TrrR3iq6RZLBwJ3E24uwPhwahw== + dependencies: + tslib "^2.2.0" + "@azure/abort-controller@^2.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-2.1.2.tgz#42fe0ccab23841d9905812c58f1082d27784566d" @@ -89,7 +96,7 @@ dependencies: tslib "^2.6.2" -"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0": +"@azure/core-auth@^1.3.0", "@azure/core-auth@^1.4.0", "@azure/core-auth@^1.5.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.7.2.tgz#558b7cb7dd12b00beec07ae5df5907d74df1ebd9" integrity sha512-Igm/S3fDYmnMq1uKS38Ae1/m37B3zigdlZw+kocwEhh5GjyKjPrXKO2J6rzpC1wAxrNil/jX9BJRqBshyjnF3g== @@ -98,6 +105,59 @@ "@azure/core-util" "^1.1.0" tslib "^2.6.2" +"@azure/core-client@^1.3.0", "@azure/core-client@^1.4.0", "@azure/core-client@^1.5.0": + version "1.9.2" + resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" + integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.4.0" + "@azure/core-rest-pipeline" "^1.9.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.6.1" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-http-compat@^2.0.1": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz#d1585ada24ba750dc161d816169b33b35f762f0d" + integrity sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-client" "^1.3.0" + "@azure/core-rest-pipeline" "^1.3.0" + +"@azure/core-lro@^2.2.0": + version "2.7.2" + resolved "https://registry.yarnpkg.com/@azure/core-lro/-/core-lro-2.7.2.tgz#787105027a20e45c77651a98b01a4d3b01b75a08" + integrity sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.2.0" + "@azure/logger" "^1.0.0" + tslib "^2.6.2" + +"@azure/core-paging@^1.1.1": + version "1.6.2" + resolved "https://registry.yarnpkg.com/@azure/core-paging/-/core-paging-1.6.2.tgz#40d3860dc2df7f291d66350b2cfd9171526433e7" + integrity sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA== + dependencies: + tslib "^2.6.2" + +"@azure/core-rest-pipeline@^1.1.0", "@azure/core-rest-pipeline@^1.3.0", "@azure/core-rest-pipeline@^1.8.1", "@azure/core-rest-pipeline@^1.9.1": + version "1.16.0" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.16.0.tgz#631172e2fe0346cf4410d1c8e01ad98d849738e2" + integrity sha512-CeuTvsXxCUmEuxH5g/aceuSl6w2EugvNHKAtKKVdiX915EjJJxAwfzNNWZreNnbxHZ2fi0zaM6wwS23x2JVqSQ== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.4.0" + "@azure/core-tracing" "^1.0.1" + "@azure/core-util" "^1.9.0" + "@azure/logger" "^1.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + "@azure/core-rest-pipeline@^1.13.0", "@azure/core-rest-pipeline@^1.5.0": version "1.15.2" resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.15.2.tgz#421729bbd8cd5f9f50b403e79941f27ac1bdc302" @@ -119,14 +179,14 @@ dependencies: tslib "^2.6.2" -"@azure/core-tracing@^1.0.1": +"@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.1.2.tgz#065dab4e093fb61899988a1cdbc827d9ad90b4ee" integrity sha512-dawW9ifvWAWmUm9/h+/UQ2jrdvjCJ7VJEuCJ6XVNudzcOwm53BFZH4Q845vjfgoUAM8ZxokvVNxNxAITc502YA== dependencies: tslib "^2.6.2" -"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0", "@azure/core-util@^1.3.0", "@azure/core-util@^1.4.0": +"@azure/core-util@^1.0.0", "@azure/core-util@^1.1.0", "@azure/core-util@^1.2.0", "@azure/core-util@^1.3.0", "@azure/core-util@^1.4.0", "@azure/core-util@^1.6.1", "@azure/core-util@^1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.9.0.tgz#469afd7e6452d5388b189f90d33f7756b0b210d1" integrity sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw== @@ -134,6 +194,43 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" +"@azure/identity@^3.4.1": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-3.4.2.tgz#6b01724c9caac7cadab6b63c76584345bda8e2de" + integrity sha512-0q5DL4uyR0EZ4RXQKD8MadGH6zTIcloUoS/RVbCpNpej4pwte0xpqYxk8K97Py2RiuUvI7F4GXpoT4046VfufA== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.5.0" + "@azure/core-client" "^1.4.0" + "@azure/core-rest-pipeline" "^1.1.0" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.6.1" + "@azure/logger" "^1.0.0" + "@azure/msal-browser" "^3.5.0" + "@azure/msal-node" "^2.5.1" + events "^3.0.0" + jws "^4.0.0" + open "^8.0.0" + stoppable "^1.1.0" + tslib "^2.2.0" + +"@azure/keyvault-keys@^4.4.0": + version "4.8.0" + resolved "https://registry.yarnpkg.com/@azure/keyvault-keys/-/keyvault-keys-4.8.0.tgz#1513b3a187bb3a9a372b5980c593962fb793b2ad" + integrity sha512-jkuYxgkw0aaRfk40OQhFqDIupqblIOIlYESWB6DKCVDxQet1pyv86Tfk9M+5uFM0+mCs6+MUHU+Hxh3joiUn4Q== + dependencies: + "@azure/abort-controller" "^1.0.0" + "@azure/core-auth" "^1.3.0" + "@azure/core-client" "^1.5.0" + "@azure/core-http-compat" "^2.0.1" + "@azure/core-lro" "^2.2.0" + "@azure/core-paging" "^1.1.1" + "@azure/core-rest-pipeline" "^1.8.1" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.0.0" + "@azure/logger" "^1.0.0" + tslib "^2.2.0" + "@azure/logger@^1.0.0", "@azure/logger@^1.0.3": version "1.1.2" resolved "https://registry.yarnpkg.com/@azure/logger/-/logger-1.1.2.tgz#3f4b876cefad328dc14aff8b850d63b611e249dc" @@ -141,6 +238,27 @@ dependencies: tslib "^2.6.2" +"@azure/msal-browser@^3.5.0": + version "3.14.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.14.0.tgz#1cb5cab438a9943212aa50c403d11f775c787b21" + integrity sha512-Un85LhOoecJ3HDTS3Uv3UWnXC9/43ZSO+Kc+anSqpZvcEt58SiO/3DuVCAe1A3I5UIBYJNMgTmZPGXQ0MVYrwA== + dependencies: + "@azure/msal-common" "14.10.0" + +"@azure/msal-common@14.10.0": + version "14.10.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.10.0.tgz#215449726717b53d549953db77562cad6cb8421c" + integrity sha512-Zk6DPDz7e1wPgLoLgAp0349Yay9RvcjPM5We/ehuenDNsz/t9QEFI7tRoHpp/e47I4p20XE3FiDlhKwAo3utDA== + +"@azure/msal-node@^2.5.1": + version "2.8.1" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.8.1.tgz#aded28d37eea2e7278c9bd44f2016647390f239c" + integrity sha512-VcZZM+5VvCWRBTOF7SxMKaxrz+EXjntx2u5AQe7QE06e6FuPJElGBrImgNgCh5QmFaNCfVFO+3qNR7UoFD/Gfw== + dependencies: + "@azure/msal-common" "14.10.0" + jsonwebtoken "^9.0.0" + uuid "^8.3.0" + "@azure/openai@1.0.0-beta.10": version "1.0.0-beta.10" resolved "https://registry.yarnpkg.com/@azure/openai/-/openai-1.0.0-beta.10.tgz#13bcf5c5bc34dd27e33dc6aab5db3dc97dd4545b" @@ -427,6 +545,11 @@ resolved "https://registry.yarnpkg.com/@inquirer/type/-/type-1.3.1.tgz#afb95ff78f44fff7e8a00e17d5820db6add2a076" integrity sha512-Pe3PFccjPVJV1vtlfVvm9OnlbxqdnP5QcscFEFEnK5quChf1ufZtM0r8mR5ToWHMxZOh0s8o/qp9ANGRTo/DAw== +"@js-joda/core@^5.5.3": + version "5.6.2" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-5.6.2.tgz#1885d10daa404cea2bd55575f910ab3bbe6c33d7" + integrity sha512-ow4R+7C24xeTjiMTTZ4k6lvxj7MRBqvqLCQjThQff3RjOmIMokMP20LNYVFhGafJtUx/Xo2Qp4qU8eNoTVH0SA== + "@js-sdsl/ordered-map@^4.4.2": version "4.4.2" resolved "https://registry.yarnpkg.com/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz#9299f82874bab9e4c7f9c48d865becbfe8d6907c" @@ -954,6 +1077,11 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.29.6.tgz#4cd8372f9247372edd5fc5af44f67e2032c46e2f" integrity sha512-aX5IFYWlMa7tQ8xZr3b2gtVReCvg7f3LEhjir/JAjX2bJCMVJA5tIPv30wTD4KDfcwMd7DDYY3hFDeGmOgtrZQ== +"@tediousjs/connection-string@^0.5.0": + version "0.5.0" + resolved "https://registry.yarnpkg.com/@tediousjs/connection-string/-/connection-string-0.5.0.tgz#9b3d858c040aac6bdf5584bf45370cef5b6522b4" + integrity sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ== + "@tootallnate/once@1": version "1.1.2" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" @@ -1030,6 +1158,14 @@ resolved "https://registry.yarnpkg.com/@types/pad-left/-/pad-left-2.1.1.tgz#17d906fc75804e1cc722da73623f1d978f16a137" integrity sha512-Xd22WCRBydkGSApl5Bw0PhAOHKSVjNL3E3AwzKaps96IMraPqy5BvZIsBVK6JLwdybUzjHnuWVwpDd0JjTfHXA== +"@types/readable-stream@^4.0.0": + version "4.0.14" + resolved "https://registry.yarnpkg.com/@types/readable-stream/-/readable-stream-4.0.14.tgz#5a76a00e1e3dd6ff921ea2b3fac7485c5a492c19" + integrity sha512-xZn/AuUbCMShGsqH/ehZtGDwQtbx00M9rZ2ENLe4tOjFZ/JFeWMhEZkk2fEe1jAUqqEAURIkFJ7Az/go8mM1/w== + dependencies: + "@types/node" "*" + safe-buffer "~5.1.1" + "@types/retry@0.12.0": version "0.12.0" resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" @@ -1539,6 +1675,16 @@ bl@^5.0.0: inherits "^2.0.4" readable-stream "^3.4.0" +bl@^6.0.3: + version "6.0.12" + resolved "https://registry.yarnpkg.com/bl/-/bl-6.0.12.tgz#77c35b96e13aeff028496c798b75389ddee9c7f8" + integrity sha512-EnEYHilP93oaOa2MnmNEjAcovPS3JlQZOyzGXi3EyEpPhm9qWvdDp7BmAVEVusGzp8LlwQK56Av+OkDoRjzE0w== + dependencies: + "@types/readable-stream" "^4.0.0" + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^4.2.0" + body-parser@1.20.2, body-parser@^1.20.2: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" @@ -1931,6 +2077,11 @@ commander@^10.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== +commander@^11.0.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-11.1.0.tgz#62fdce76006a68e5c1ab3314dc92e800eb83d906" + integrity sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ== + commander@^8.0.0: version "8.3.0" resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" @@ -2147,6 +2298,11 @@ define-data-property@^1.0.1, define-data-property@^1.1.4: es-errors "^1.3.0" gopd "^1.0.1" +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.2.0, define-properties@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" @@ -2166,6 +2322,11 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +denque@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" + integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -2383,6 +2544,20 @@ es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23 unbox-primitive "^1.0.2" which-typed-array "^1.1.15" +es-aggregate-error@^1.0.9: + version "1.0.13" + resolved "https://registry.yarnpkg.com/es-aggregate-error/-/es-aggregate-error-1.0.13.tgz#7f28b77c9d8d09bbcd3a466e4be9fe02fa985201" + integrity sha512-KkzhUUuD2CUMqEc8JEqsXEMDHzDPE8RCjZeUBitsnB1eNcAJWQPiciKsMXe3Yytj4Flw1XLl46Qcf9OxvZha7A== + dependencies: + define-data-property "^1.1.4" + define-properties "^1.2.1" + es-abstract "^1.23.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + globalthis "^1.0.3" + has-property-descriptors "^1.0.2" + set-function-name "^2.0.2" + es-define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" @@ -2622,6 +2797,11 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +events@^3.0.0, events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + execa@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -3028,6 +3208,13 @@ gcp-metadata@^5.3.0: gaxios "^5.0.0" json-bigint "^1.0.0" +generate-function@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" + integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== + dependencies: + is-property "^1.0.2" + generic-pool@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" @@ -3353,7 +3540,7 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@^0.6.2: +iconv-lite@^0.6.2, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -3516,6 +3703,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -3584,6 +3776,11 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== +is-property@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== + is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -3655,6 +3852,13 @@ is-weakset@^2.0.3: call-bind "^1.0.7" get-intrinsic "^1.2.4" +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -3715,6 +3919,11 @@ js-base64@3.7.2: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.2.tgz#816d11d81a8aff241603d19ce5761e13e41d7745" integrity sha512-NnRs6dsyqUXejqk/yv2aiXlAvOs56sLkX6nUdeaNezI5LFFLlsZjOThmwnrcwh5ZZRwZlCMnVAY3CvhIhoVEKQ== +js-md4@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/js-md4/-/js-md4-0.3.2.tgz#cd3b3dc045b0c404556c81ddb5756c23e59d7cf5" + integrity sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA== + js-tiktoken@^1.0.11, js-tiktoken@^1.0.7, js-tiktoken@^1.0.8: version "1.0.11" resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.11.tgz#d7d707b849f703841112660d9d55169424a35344" @@ -3734,6 +3943,11 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" +jsbi@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/jsbi/-/jsbi-4.3.0.tgz#b54ee074fb6fcbc00619559305c8f7e912b04741" + integrity sha512-SnZNcinB4RIcnEyZqFPdGPVgrg2AcnykiBy0sHVJQKHYeaLUvi3Exj+iaPpLnFVkDPZIV4U0yvgC9/R4uEAZ9g== + jsbn@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" @@ -3811,7 +4025,7 @@ jsonwebtoken@^8.5.1: ms "^2.1.1" semver "^5.6.0" -jsonwebtoken@^9.0.2: +jsonwebtoken@^9.0.0, jsonwebtoken@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== @@ -4050,7 +4264,7 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== -long@^5.0.0: +long@^5.0.0, long@^5.2.1: version "5.2.3" resolved "https://registry.yarnpkg.com/long/-/long-5.2.3.tgz#a3ba97f3877cf1d778eccbcb048525ebb77499e1" integrity sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q== @@ -4074,6 +4288,16 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.18.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" + integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== + +lru-cache@^8.0.0: + version "8.0.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-8.0.5.tgz#983fe337f3e176667f8e567cfcce7cb064ea214e" + integrity sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA== + lru-cache@^9.1.2: version "9.1.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.1.2.tgz#255fdbc14b75589d6d0e73644ca167a8db506835" @@ -4327,6 +4551,18 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +mssql@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/mssql/-/mssql-10.0.2.tgz#99f9113a05b8ee32c84704fddc3780554cd89a60" + integrity sha512-GrQ6gzv2xA7ndOvONyZ++4RZsNkr8qDiIpvuFn2pR3TPiSk/cKdmvOrDU3jWgon7EPj7CPgmDiMh7Hgtft2xLg== + dependencies: + "@tediousjs/connection-string" "^0.5.0" + commander "^11.0.0" + debug "^4.3.3" + rfdc "^1.3.0" + tarn "^3.0.2" + tedious "^16.4.0" + multer@^1.4.5-lts.1: version "1.4.5-lts.1" resolved "https://registry.yarnpkg.com/multer/-/multer-1.4.5-lts.1.tgz#803e24ad1984f58edffbc79f56e305aec5cfd1ac" @@ -4350,11 +4586,37 @@ mute-stream@^1.0.0: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-1.0.0.tgz#e31bd9fe62f0aed23520aa4324ea6671531e013e" integrity sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA== +mysql2@^3.9.7: + version "3.9.7" + resolved "https://registry.yarnpkg.com/mysql2/-/mysql2-3.9.7.tgz#843755daf65b5ef08afe545fe14b8fb62824741a" + integrity sha512-KnJT8vYRcNAZv73uf9zpXqNbvBG7DJrs+1nACsjZP1HMJ1TgXEy8wnNilXAn/5i57JizXKtrUtwDB7HxT9DDpw== + dependencies: + denque "^2.1.0" + generate-function "^2.3.1" + iconv-lite "^0.6.3" + long "^5.2.1" + lru-cache "^8.0.0" + named-placeholders "^1.1.3" + seq-queue "^0.0.5" + sqlstring "^2.3.2" + +named-placeholders@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/named-placeholders/-/named-placeholders-1.1.3.tgz#df595799a36654da55dda6152ba7a137ad1d9351" + integrity sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w== + dependencies: + lru-cache "^7.14.1" + napi-build-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806" integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== +native-duplexpair@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/native-duplexpair/-/native-duplexpair-1.0.0.tgz#7899078e64bf3c8a3d732601b3d40ff05db58fa0" + integrity sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -4372,6 +4634,11 @@ node-abi@^3.3.0: dependencies: semver "^7.3.5" +node-abort-controller@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/node-abort-controller/-/node-abort-controller-3.1.1.tgz#a94377e964a9a37ac3976d848cb5c765833b8548" + integrity sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ== + node-addon-api@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762" @@ -4685,6 +4952,15 @@ onnxruntime-web@1.14.0: onnxruntime-common "~1.14.0" platform "^1.3.6" +open@^8.0.0: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + openai@4.38.5: version "4.38.5" resolved "https://registry.yarnpkg.com/openai/-/openai-4.38.5.tgz#87de78eed9f7e63331fb6b1307d8c9dd986b39d0" @@ -4848,6 +5124,62 @@ pend@~1.2.0: resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== +pg-cloudflare@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" + integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== + +pg-connection-string@^2.6.4: + version "2.6.4" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.6.4.tgz#f543862adfa49fa4e14bc8a8892d2a84d754246d" + integrity sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA== + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.6.2.tgz#3a592370b8ae3f02a7c8130d245bc02fa2c5f3f2" + integrity sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg== + +pg-protocol@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.6.1.tgz#21333e6d83b01faaebfe7a33a7ad6bfd9ed38cb3" + integrity sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg== + +pg-types@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@^8.11.5: + version "8.11.5" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.11.5.tgz#e722b0a5f1ed92931c31758ebec3ddf878dd4128" + integrity sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw== + dependencies: + pg-connection-string "^2.6.4" + pg-pool "^3.6.2" + pg-protocol "^1.6.1" + pg-types "^2.1.0" + pgpass "1.x" + optionalDependencies: + pg-cloudflare "^1.1.1" + +pgpass@1.x: + version "1.0.5" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" + integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== + dependencies: + split2 "^4.1.0" + picomatch@^2.0.4, picomatch@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -4882,6 +5214,28 @@ possible-typed-array-names@^1.0.0: resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== + +postgres-date@~1.0.4: + version "1.0.7" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" + integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + posthog-node@^3.1.1: version "3.6.3" resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-3.6.3.tgz#4d3a2a4385e01c4d9e91d01dbde104e60285853d" @@ -4937,6 +5291,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -5110,6 +5469,17 @@ readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@^4.2.0: + version "4.5.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-4.5.2.tgz#9e7fc4c45099baeed934bff6eb97ba6cf2729e09" + integrity sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g== + dependencies: + abort-controller "^3.0.0" + buffer "^6.0.3" + events "^3.3.0" + process "^0.11.10" + string_decoder "^1.3.0" + readdir-glob@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.3.tgz#c3d831f51f5e7bfa62fa2ffbe4b508c640f09584" @@ -5199,6 +5569,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" + integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -5298,6 +5673,11 @@ send@0.18.0: range-parser "~1.2.1" statuses "2.0.1" +seq-queue@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/seq-queue/-/seq-queue-0.0.5.tgz#d56812e1c017a6e4e7c3e3a37a1da6d78dd3c93e" + integrity sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q== + serve-static@1.15.0: version "1.15.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" @@ -5450,7 +5830,12 @@ socks@^2.6.2: ip-address "^9.0.5" smart-buffer "^4.2.0" -sprintf-js@^1.1.3: +split2@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + +sprintf-js@^1.1.2, sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== @@ -5472,6 +5857,11 @@ sqlite@^4.2.1: resolved "https://registry.yarnpkg.com/sqlite/-/sqlite-4.2.1.tgz#d4eedfd1ad702f79110792375f4241a90c75c828" integrity sha512-Tll0Ndvnwkuv5Hn6WIbh26rZiYQORuH1t5m/or9LUpSmDmmyFG89G9fKrSeugMPxwmEIXoVxqTun4LbizTs4uw== +sqlstring@^2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/sqlstring/-/sqlstring-2.3.3.tgz#2ddc21f03bce2c387ed60680e739922c65751d0c" + integrity sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg== + ssri@^8.0.0, ssri@^8.0.1: version "8.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" @@ -5496,6 +5886,11 @@ stdin-discarder@^0.1.0: dependencies: bl "^5.0.0" +stoppable@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" + integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== + stream-read-all@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/stream-read-all/-/stream-read-all-3.0.1.tgz#60762ae45e61d93ba0978cda7f3913790052ad96" @@ -5585,7 +5980,7 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" -string_decoder@^1.1.1: +string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== @@ -5748,6 +6143,28 @@ tar@^6.0.2, tar@^6.1.11, tar@^6.1.2, tar@^6.2.0: mkdirp "^1.0.3" yallist "^4.0.0" +tarn@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/tarn/-/tarn-3.0.2.tgz#73b6140fbb881b71559c4f8bfde3d9a4b3d27693" + integrity sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ== + +tedious@^16.4.0: + version "16.7.1" + resolved "https://registry.yarnpkg.com/tedious/-/tedious-16.7.1.tgz#1190f30fd99a413f1dc9250dee4835cf0788b650" + integrity sha512-NmedZS0NJiTv3CoYnf1FtjxIDUgVYzEmavrc8q2WHRb+lP4deI9BpQfmNnBZZaWusDbP5FVFZCcvzb3xOlNVlQ== + dependencies: + "@azure/identity" "^3.4.1" + "@azure/keyvault-keys" "^4.4.0" + "@js-joda/core" "^5.5.3" + bl "^6.0.3" + es-aggregate-error "^1.0.9" + iconv-lite "^0.6.3" + js-md4 "^0.3.2" + jsbi "^4.3.0" + native-duplexpair "^1.0.0" + node-abort-controller "^3.1.1" + sprintf-js "^1.1.2" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -5794,7 +6211,7 @@ triple-beam@^1.3.0: resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== -tslib@^2.4.0, tslib@^2.5.3, tslib@^2.6.2: +tslib@^2.2.0, tslib@^2.4.0, tslib@^2.5.3, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== @@ -5966,6 +6383,11 @@ url-join@4.0.1, url-join@^4.0.1: resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7" integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA== +url-pattern@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/url-pattern/-/url-pattern-1.0.3.tgz#0409292471b24f23c50d65a47931793d2b5acfc1" + integrity sha512-uQcEj/2puA4aq1R3A2+VNVBgaWYR24FdWjl7VNW83rnWftlhyzOZ/tBjezRiC2UkIzuxC8Top3IekN3vUf1WxA== + url-template@^2.0.8: version "2.0.8" resolved "https://registry.yarnpkg.com/url-template/-/url-template-2.0.8.tgz#fc565a3cccbff7730c775f5641f9555791439f21" @@ -5991,7 +6413,7 @@ uuid-apikey@^1.5.3: encode32 "^1.1.0" uuid "^8.3.1" -uuid@^8.3.1: +uuid@^8.3.0, uuid@^8.3.1: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==