Merge branch 'main' into feature/RateLimit

This commit is contained in:
chungyau97
2023-08-30 21:35:40 +08:00
5 changed files with 237 additions and 13 deletions
@@ -1,14 +1,34 @@
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { SqlDatabaseChain, SqlDatabaseChainInput } from 'langchain/chains/sql_db'
import { getBaseClasses } from '../../../src/utils'
import { getBaseClasses, getInputVariables } from '../../../src/utils'
import { DataSource } from 'typeorm'
import { SqlDatabase } from 'langchain/sql_db'
import { BaseLanguageModel } from 'langchain/base_language'
import { PromptTemplate, PromptTemplateInput } from 'langchain/prompts'
import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler'
import { DataSourceOptions } from 'typeorm/data-source'
type DatabaseType = 'sqlite' | 'postgres' | 'mssql' | 'mysql'
const defaultPrompt = `Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. Unless the user specifies in his question a specific number of examples he wishes to obtain, always limit your query to at most {top_k} results. You can order the results by a relevant column to return the most interesting examples in the database.
Never query for all the columns from a specific table, only ask for a the few relevant columns given the question.
Pay attention to use only the column names that you can see in the schema description. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.
Use the following format:
Question: "Question here"
SQLQuery: "SQL Query to run"
SQLResult: "Result of the SQLQuery"
Answer: "Final answer here"
Only use the tables listed below.
{table_info}
Question: {input}`
class SqlDatabaseChain_Chains implements INode {
label: string
name: string
@@ -23,7 +43,7 @@ class SqlDatabaseChain_Chains implements INode {
constructor() {
this.label = 'Sql Database Chain'
this.name = 'sqlDatabaseChain'
this.version = 1.0
this.version = 2.0
this.type = 'SqlDatabaseChain'
this.icon = 'sqlchain.svg'
this.category = 'Chains'
@@ -64,6 +84,19 @@ class SqlDatabaseChain_Chains implements INode {
name: 'url',
type: 'string',
placeholder: '1270.0.0.1:5432/chinook'
},
{
label: 'Custom Prompt',
name: 'customPrompt',
type: 'string',
description:
'You can provide custom prompt to the chain. This will override the existing default prompt used. See <a target="_blank" href="https://python.langchain.com/docs/integrations/tools/sqlite#customize-prompt">guide</a>',
warning:
'Prompt must include 3 input variables: {input}, {dialect}, {table_info}. You can refer to official guide from description above',
rows: 4,
placeholder: defaultPrompt,
additionalParams: true,
optional: true
}
]
}
@@ -72,8 +105,9 @@ class SqlDatabaseChain_Chains implements INode {
const databaseType = nodeData.inputs?.database as DatabaseType
const model = nodeData.inputs?.model as BaseLanguageModel
const url = nodeData.inputs?.url
const customPrompt = nodeData.inputs?.customPrompt as string
const chain = await getSQLDBChain(databaseType, url, model)
const chain = await getSQLDBChain(databaseType, url, model, customPrompt)
return chain
}
@@ -81,8 +115,9 @@ class SqlDatabaseChain_Chains implements INode {
const databaseType = nodeData.inputs?.database as DatabaseType
const model = nodeData.inputs?.model as BaseLanguageModel
const url = nodeData.inputs?.url
const customPrompt = nodeData.inputs?.customPrompt as string
const chain = await getSQLDBChain(databaseType, url, model)
const chain = await getSQLDBChain(databaseType, url, model, customPrompt)
const loggerHandler = new ConsoleCallbackHandler(options.logger)
if (options.socketIO && options.socketIOClientId) {
@@ -96,7 +131,7 @@ class SqlDatabaseChain_Chains implements INode {
}
}
const getSQLDBChain = async (databaseType: DatabaseType, url: string, llm: BaseLanguageModel) => {
const getSQLDBChain = async (databaseType: DatabaseType, url: string, llm: BaseLanguageModel, customPrompt?: string) => {
const datasource = new DataSource(
databaseType === 'sqlite'
? {
@@ -119,6 +154,14 @@ const getSQLDBChain = async (databaseType: DatabaseType, url: string, llm: BaseL
verbose: process.env.DEBUG === 'true' ? true : false
}
if (customPrompt) {
const options: PromptTemplateInput = {
template: customPrompt,
inputVariables: getInputVariables(customPrompt)
}
obj.prompt = new PromptTemplate(options)
}
const chain = new SqlDatabaseChain(obj)
return chain
}
@@ -0,0 +1,149 @@
import { ICommonObject, INode, INodeData, INodeParams } from '../../../src/Interface'
import { getBaseClasses, getCredentialData, getCredentialParam } from '../../../src/utils'
import { ChatOpenAI, OpenAIChatInput } from 'langchain/chat_models/openai'
class ChatOpenAIFineTuned_ChatModels implements INode {
label: string
name: string
version: number
type: string
icon: string
category: string
description: string
baseClasses: string[]
credential: INodeParams
inputs: INodeParams[]
constructor() {
this.label = 'ChatOpenAI Fine-Tuned'
this.name = 'chatOpenAIFineTuned'
this.version = 1.0
this.type = 'ChatOpenAI-FineTuned'
this.icon = 'openai.png'
this.category = 'Chat Models'
this.description = 'Wrapper around fine-tuned OpenAI LLM that use the Chat endpoint'
this.baseClasses = [this.type, ...getBaseClasses(ChatOpenAI)]
this.credential = {
label: 'Connect Credential',
name: 'credential',
type: 'credential',
credentialNames: ['openAIApi']
}
this.inputs = [
{
label: 'Model Name',
name: 'modelName',
type: 'string',
placeholder: 'ft:gpt-3.5-turbo:my-org:custom_suffix:id'
},
{
label: 'Temperature',
name: 'temperature',
type: 'number',
step: 0.1,
default: 0.9,
optional: true
},
{
label: 'Max Tokens',
name: 'maxTokens',
type: 'number',
step: 1,
optional: true,
additionalParams: true
},
{
label: 'Top Probability',
name: 'topP',
type: 'number',
step: 0.1,
optional: true,
additionalParams: true
},
{
label: 'Frequency Penalty',
name: 'frequencyPenalty',
type: 'number',
step: 0.1,
optional: true,
additionalParams: true
},
{
label: 'Presence Penalty',
name: 'presencePenalty',
type: 'number',
step: 0.1,
optional: true,
additionalParams: true
},
{
label: 'Timeout',
name: 'timeout',
type: 'number',
step: 1,
optional: true,
additionalParams: true
},
{
label: 'BasePath',
name: 'basepath',
type: 'string',
optional: true,
additionalParams: true
},
{
label: 'BaseOptions',
name: 'baseOptions',
type: 'json',
optional: true,
additionalParams: true
}
]
}
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
const temperature = nodeData.inputs?.temperature as string
const modelName = nodeData.inputs?.modelName as string
const maxTokens = nodeData.inputs?.maxTokens as string
const topP = nodeData.inputs?.topP as string
const frequencyPenalty = nodeData.inputs?.frequencyPenalty as string
const presencePenalty = nodeData.inputs?.presencePenalty as string
const timeout = nodeData.inputs?.timeout as string
const streaming = nodeData.inputs?.streaming as boolean
const basePath = nodeData.inputs?.basepath as string
const baseOptions = nodeData.inputs?.baseOptions
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
const obj: Partial<OpenAIChatInput> & { openAIApiKey?: string } = {
temperature: parseFloat(temperature),
modelName,
openAIApiKey,
streaming: streaming ?? true
}
if (maxTokens) obj.maxTokens = parseInt(maxTokens, 10)
if (topP) obj.topP = parseFloat(topP)
if (frequencyPenalty) obj.frequencyPenalty = parseFloat(frequencyPenalty)
if (presencePenalty) obj.presencePenalty = parseFloat(presencePenalty)
if (timeout) obj.timeout = parseInt(timeout, 10)
let parsedBaseOptions: any | undefined = undefined
if (baseOptions) {
try {
parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions)
} catch (exception) {
throw new Error("Invalid JSON in the ChatOpenAI's BaseOptions: " + exception)
}
}
const model = new ChatOpenAI(obj, {
basePath,
baseOptions: parsedBaseOptions
})
return model
}
}
module.exports = { nodeClass: ChatOpenAIFineTuned_ChatModels }
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

@@ -125,6 +125,13 @@ class OpenAI_LLMs implements INode {
type: 'string',
optional: true,
additionalParams: true
},
{
label: 'BaseOptions',
name: 'baseOptions',
type: 'json',
optional: true,
additionalParams: true
}
]
}
@@ -141,6 +148,7 @@ class OpenAI_LLMs implements INode {
const bestOf = nodeData.inputs?.bestOf as string
const streaming = nodeData.inputs?.streaming as boolean
const basePath = nodeData.inputs?.basepath as string
const baseOptions = nodeData.inputs?.baseOptions
const credentialData = await getCredentialData(nodeData.credential ?? '', options)
const openAIApiKey = getCredentialParam('openAIApiKey', credentialData, nodeData)
@@ -160,8 +168,19 @@ class OpenAI_LLMs implements INode {
if (batchSize) obj.batchSize = parseInt(batchSize, 10)
if (bestOf) obj.bestOf = parseInt(bestOf, 10)
let parsedBaseOptions: any | undefined = undefined
if (baseOptions) {
try {
parsedBaseOptions = typeof baseOptions === 'object' ? baseOptions : JSON.parse(baseOptions)
} catch (exception) {
throw new Error("Invalid JSON in the OpenAI's BaseOptions: " + exception)
}
}
const model = new OpenAI(obj, {
basePath
basePath,
baseOptions: parsedBaseOptions
})
return model
}
@@ -157,17 +157,17 @@
},
{
"width": 300,
"height": 423,
"height": 475,
"id": "sqlDatabaseChain_0",
"position": {
"x": 1229.0092429246013,
"y": 231.59431102290245
"x": 1206.5244299447634,
"y": 201.04431101230608
},
"type": "customNode",
"data": {
"id": "sqlDatabaseChain_0",
"label": "Sql Database Chain",
"version": 1,
"version": 2,
"name": "sqlDatabaseChain",
"type": "SqlDatabaseChain",
"baseClasses": ["SqlDatabaseChain", "BaseChain", "Runnable"],
@@ -205,6 +205,18 @@
"type": "string",
"placeholder": "1270.0.0.1:5432/chinook",
"id": "sqlDatabaseChain_0-input-url-string"
},
{
"label": "Custom Prompt",
"name": "customPrompt",
"type": "string",
"description": "You can provide custom prompt to the chain. This will override the existing default prompt used. See <a target=\"_blank\" href=\"https://python.langchain.com/docs/integrations/tools/sqlite#customize-prompt\">guide</a>",
"warning": "Prompt must include 3 input variables: {input}, {dialect}, {table_info}. You can refer to official guide from description above",
"rows": 4,
"placeholder": "Given an input question, first create a syntactically correct {dialect} query to run, then look at the results of the query and return the answer. Unless the user specifies in his question a specific number of examples he wishes to obtain, always limit your query to at most {top_k} results. You can order the results by a relevant column to return the most interesting examples in the database.\n\nNever query for all the columns from a specific table, only ask for a the few relevant columns given the question.\n\nPay attention to use only the column names that you can see in the schema description. Be careful to not query for columns that do not exist. Also, pay attention to which column is in which table.\n\nUse the following format:\n\nQuestion: \"Question here\"\nSQLQuery: \"SQL Query to run\"\nSQLResult: \"Result of the SQLQuery\"\nAnswer: \"Final answer here\"\n\nOnly use the tables listed below.\n\n{table_info}\n\nQuestion: {input}",
"additionalParams": true,
"optional": true,
"id": "sqlDatabaseChain_0-input-customPrompt-string"
}
],
"inputAnchors": [
@@ -218,7 +230,8 @@
"inputs": {
"model": "{{chatOpenAI_0.data.instance}}",
"database": "sqlite",
"url": ""
"url": "",
"customPrompt": ""
},
"outputAnchors": [
{
@@ -233,8 +246,8 @@
},
"selected": false,
"positionAbsolute": {
"x": 1229.0092429246013,
"y": 231.59431102290245
"x": 1206.5244299447634,
"y": 201.04431101230608
},
"dragging": false
}