diff --git a/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts new file mode 100644 index 000000000..1f6d9fddb --- /dev/null +++ b/packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts @@ -0,0 +1,81 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { SqlDatabaseChain } from 'langchain/chains' +import { getBaseClasses } from '../../../src/utils' +import { DataSource } from 'typeorm' +import { SqlDatabase } from 'langchain/sql_db' +import { BaseLLM } from 'langchain/llms/base' + +class SqlDatabaseChain_Chains implements INode { + label: string + name: string + type: string + icon: string + category: string + baseClasses: string[] + description: string + inputs: INodeParams[] + + constructor() { + this.label = 'Sql Database Chain' + this.name = 'sqlDatabaseChain' + this.type = 'SqlDatabaseChain' + this.icon = 'sqlchain.svg' + this.category = 'Chains' + this.description = 'Answer questions over a SQL database' + this.baseClasses = [this.type, ...getBaseClasses(SqlDatabaseChain)] + this.inputs = [ + { + label: 'LLM', + name: 'llm', + type: 'BaseLLM' + }, + { + label: 'Database', + name: 'database', + type: 'options', + options: [ + { + label: 'SQlite', + name: 'sqlite' + } + ], + default: 'sqlite' + }, + { + label: 'Database File Path', + name: 'dbFilePath', + type: 'string', + placeholder: 'C:/Users/chinook.db' + } + ] + } + + async init(nodeData: INodeData): Promise { + const databaseType = nodeData.inputs?.database + const llm = nodeData.inputs?.llm as BaseLLM + const dbFilePath = nodeData.inputs?.dbFilePath + + const datasource = new DataSource({ + type: databaseType, + database: dbFilePath + }) + + const db = await SqlDatabase.fromDataSourceParams({ + appDataSource: datasource + }) + + const chain = new SqlDatabaseChain({ + llm, + database: db + }) + return chain + } + + async run(nodeData: INodeData, input: string): Promise { + const chain = nodeData.instance as SqlDatabaseChain + const res = await chain.run(input) + return res + } +} + +module.exports = { nodeClass: SqlDatabaseChain_Chains } diff --git a/packages/components/nodes/chains/SqlDatabaseChain/sqlchain.svg b/packages/components/nodes/chains/SqlDatabaseChain/sqlchain.svg new file mode 100644 index 000000000..dcf937b35 --- /dev/null +++ b/packages/components/nodes/chains/SqlDatabaseChain/sqlchain.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts b/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts new file mode 100644 index 000000000..8be10f742 --- /dev/null +++ b/packages/components/nodes/documentloaders/Cheerio/Cheerio.ts @@ -0,0 +1,64 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { TextSplitter } from 'langchain/text_splitter' +import { CheerioWebBaseLoader } from 'langchain/document_loaders/web/cheerio' + +class Cheerio_DocumentLoaders implements INode { + label: string + name: string + description: string + type: string + icon: string + category: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Cheerio Web Scraper' + this.name = 'cheerioWebScraper' + this.type = 'Document' + this.icon = 'cheerio.svg' + this.category = 'Document Loaders' + this.description = `Load data from webpages` + this.baseClasses = [this.type] + this.inputs = [ + { + label: 'URL', + name: 'url', + type: 'string' + }, + { + label: 'Text Splitter', + name: 'textSplitter', + type: 'TextSplitter', + optional: true + } + ] + } + + async init(nodeData: INodeData): Promise { + const textSplitter = nodeData.inputs?.textSplitter as TextSplitter + let url = nodeData.inputs?.url as string + + var urlPattern = new RegExp( + '^(https?:\\/\\/)?' + // validate protocol + '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // validate domain name + '((\\d{1,3}\\.){3}\\d{1,3}))' + // validate OR ip (v4) address + '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*' + // validate port and path + '(\\?[;&a-z\\d%_.~+=-]*)?' + // validate query string + '(\\#[-a-z\\d_]*)?$', + 'i' + ) // validate fragment locator + + const loader = new CheerioWebBaseLoader(urlPattern.test(url.trim()) ? url.trim() : '') + + if (textSplitter) { + const docs = await loader.loadAndSplit(textSplitter) + return docs + } else { + const docs = await loader.load() + return docs + } + } +} + +module.exports = { nodeClass: Cheerio_DocumentLoaders } diff --git a/packages/components/nodes/documentloaders/Cheerio/cheerio.svg b/packages/components/nodes/documentloaders/Cheerio/cheerio.svg new file mode 100644 index 000000000..8e3334b9f --- /dev/null +++ b/packages/components/nodes/documentloaders/Cheerio/cheerio.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/packages/components/nodes/embeddings/CohereEmbedding/CohereEmbedding.ts b/packages/components/nodes/embeddings/CohereEmbedding/CohereEmbedding.ts new file mode 100644 index 000000000..923cf6c64 --- /dev/null +++ b/packages/components/nodes/embeddings/CohereEmbedding/CohereEmbedding.ts @@ -0,0 +1,40 @@ +import { INode, INodeData, INodeParams } from '../../../src/Interface' +import { getBaseClasses } from '../../../src/utils' +import { CohereEmbeddings } from 'langchain/embeddings/cohere' + +class CohereEmbedding_Embeddings implements INode { + label: string + name: string + type: string + icon: string + category: string + description: string + baseClasses: string[] + inputs: INodeParams[] + + constructor() { + this.label = 'Cohere Embeddings' + this.name = 'cohereEmbeddings' + this.type = 'CohereEmbeddings' + this.icon = 'cohere.png' + this.category = 'Embeddings' + this.description = 'Cohere API to generate embeddings for a given text' + this.baseClasses = [this.type, ...getBaseClasses(CohereEmbeddings)] + this.inputs = [ + { + label: 'Cohere API Key', + name: 'cohereApiKey', + type: 'password' + } + ] + } + + async init(nodeData: INodeData): Promise { + const apiKey = nodeData.inputs?.cohereApiKey as string + + const model = new CohereEmbeddings({ apiKey }) + return model + } +} + +module.exports = { nodeClass: CohereEmbedding_Embeddings } diff --git a/packages/components/nodes/embeddings/CohereEmbedding/cohere.png b/packages/components/nodes/embeddings/CohereEmbedding/cohere.png new file mode 100644 index 000000000..266adeac2 Binary files /dev/null and b/packages/components/nodes/embeddings/CohereEmbedding/cohere.png differ diff --git a/packages/components/package.json b/packages/components/package.json index d75cda9d5..0e845475b 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -20,7 +20,9 @@ "@huggingface/inference": "^1.6.3", "@pinecone-database/pinecone": "^0.0.12", "axios": "^0.27.2", + "cheerio": "^1.0.0-rc.12", "chromadb": "^1.3.1", + "cohere-ai": "^6.2.0", "d3-dsv": "2", "dotenv": "^16.0.0", "express": "^4.17.3", diff --git a/packages/server/marketplaces/SQL DB Chain.json b/packages/server/marketplaces/SQL DB Chain.json new file mode 100644 index 000000000..4d8b35c8a --- /dev/null +++ b/packages/server/marketplaces/SQL DB Chain.json @@ -0,0 +1,163 @@ +{ + "description": "Answer questions over a SQL database", + "nodes": [ + { + "width": 300, + "height": 424, + "id": "sqlDatabaseChain_0", + "position": { + "x": 1271.2742585099204, + "y": 232.91561199714107 + }, + "type": "customNode", + "data": { + "id": "sqlDatabaseChain_0", + "label": "Sql Database Chain", + "name": "sqlDatabaseChain", + "type": "SqlDatabaseChain", + "baseClasses": ["SqlDatabaseChain", "BaseChain"], + "category": "Chains", + "description": "Answer questions over a SQL database", + "inputParams": [ + { + "label": "Database", + "name": "database", + "type": "options", + "options": [ + { + "label": "SQlite", + "name": "sqlite" + } + ], + "default": "sqlite" + }, + { + "label": "Database File Path", + "name": "dbFilePath", + "type": "string", + "placeholder": "C:/Users/chinook.db" + } + ], + "inputAnchors": [ + { + "label": "LLM", + "name": "llm", + "type": "BaseLLM", + "id": "sqlDatabaseChain_0-input-llm-BaseLLM" + } + ], + "inputs": { + "llm": "{{openAI_0.data.instance}}", + "database": "sqlite", + "dbFilePath": "" + }, + "outputAnchors": [ + { + "id": "sqlDatabaseChain_0-output-sqlDatabaseChain-SqlDatabaseChain|BaseChain", + "name": "sqlDatabaseChain", + "label": "SqlDatabaseChain", + "type": "SqlDatabaseChain | BaseChain" + } + ], + "selected": false + }, + "selected": false, + "positionAbsolute": { + "x": 1271.2742585099204, + "y": 232.91561199714107 + }, + "dragging": false + }, + { + "width": 300, + "height": 472, + "id": "openAI_0", + "position": { + "x": 867.8574087065126, + "y": 209.58625096303308 + }, + "type": "customNode", + "data": { + "id": "openAI_0", + "label": "OpenAI", + "name": "openAI", + "type": "OpenAI", + "baseClasses": ["OpenAI", "BaseLLM", "BaseLanguageModel"], + "category": "LLMs", + "description": "Wrapper around OpenAI large language models", + "inputParams": [ + { + "label": "OpenAI Api Key", + "name": "openAIApiKey", + "type": "password" + }, + { + "label": "Model Name", + "name": "modelName", + "type": "options", + "options": [ + { + "label": "text-davinci-003", + "name": "text-davinci-003" + }, + { + "label": "text-davinci-002", + "name": "text-davinci-002" + }, + { + "label": "text-curie-001", + "name": "text-curie-001" + }, + { + "label": "text-babbage-001", + "name": "text-babbage-001" + } + ], + "default": "text-davinci-003", + "optional": true + }, + { + "label": "Temperature", + "name": "temperature", + "type": "number", + "default": 0.7, + "optional": true + } + ], + "inputAnchors": [], + "inputs": { + "modelName": "text-davinci-003", + "temperature": "0" + }, + "outputAnchors": [ + { + "id": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel", + "name": "openAI", + "label": "OpenAI", + "type": "OpenAI | BaseLLM | BaseLanguageModel" + } + ], + "selected": false + }, + "selected": false, + "positionAbsolute": { + "x": 867.8574087065126, + "y": 209.58625096303308 + }, + "dragging": false + } + ], + "edges": [ + { + "source": "openAI_0", + "sourceHandle": "openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel", + "target": "sqlDatabaseChain_0", + "targetHandle": "sqlDatabaseChain_0-input-llm-BaseLLM", + "type": "buttonedge", + "id": "openAI_0-openAI_0-output-openAI-OpenAI|BaseLLM|BaseLanguageModel-sqlDatabaseChain_0-sqlDatabaseChain_0-input-llm-BaseLLM", + "data": { + "label": "" + } + } + ] +} diff --git a/packages/ui/src/views/chatmessage/ChatMessage.js b/packages/ui/src/views/chatmessage/ChatMessage.js index 4fbb54e68..b6632b1ea 100644 --- a/packages/ui/src/views/chatmessage/ChatMessage.js +++ b/packages/ui/src/views/chatmessage/ChatMessage.js @@ -39,6 +39,7 @@ export const ChatMessage = ({ chatflowid }) => { const customization = useSelector((state) => state.customization) const { confirm } = useConfirm() const dispatch = useDispatch() + const ps = useRef() useNotifier() const enqueueSnackbar = (...args) => dispatch(enqueueSnackbarAction(...args)) @@ -54,7 +55,6 @@ export const ChatMessage = ({ chatflowid }) => { } ]) - const messagesEndRef = useRef(null) const inputRef = useRef(null) const anchorRef = useRef(null) const prevOpen = useRef(open) @@ -115,7 +115,9 @@ export const ChatMessage = ({ chatflowid }) => { } const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) + if (ps.current) { + ps.current.scrollTo({ top: Number.MAX_SAFE_INTEGER, behavior: 'smooth' }) + } } const addChatMessage = async (message, type) => { @@ -286,7 +288,7 @@ export const ChatMessage = ({ chatflowid }) => {
-
+
{messages.map((message, index) => { return ( // The latest message sent by the user will be animated while waiting for a response @@ -331,7 +333,6 @@ export const ChatMessage = ({ chatflowid }) => { ) })} -