Compare commits
1 Commits
main
...
feature/gp
| Author | SHA1 | Date |
|---|---|---|
|
|
b97816b561 |
|
|
@ -0,0 +1,52 @@
|
||||||
|
import { INode, INodeData, INodeParams } from '../../../src/Interface'
|
||||||
|
import { getBaseClasses } from '../../../src/utils'
|
||||||
|
import { GPT4All } from './core'
|
||||||
|
|
||||||
|
class GPT4All_LLMs implements INode {
|
||||||
|
label: string
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
icon: string
|
||||||
|
category: string
|
||||||
|
description: string
|
||||||
|
baseClasses: string[]
|
||||||
|
inputs: INodeParams[]
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.label = 'GPT4All'
|
||||||
|
this.name = 'gpt4All'
|
||||||
|
this.type = 'GPT4All'
|
||||||
|
this.icon = 'gpt4all.png'
|
||||||
|
this.category = 'LLMs'
|
||||||
|
this.description = 'Wrapper around GP4All large language models'
|
||||||
|
this.baseClasses = [this.type, ...getBaseClasses(GPT4All)]
|
||||||
|
this.inputs = [
|
||||||
|
{
|
||||||
|
label: 'Model File Path',
|
||||||
|
name: 'modelFilePath',
|
||||||
|
type: 'string',
|
||||||
|
placeholder: `C:\\gpt4all\\gpt4all-lora-quantized.bin`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Executable File Path',
|
||||||
|
name: 'executablePath',
|
||||||
|
type: 'string',
|
||||||
|
placeholder: `C:\\gpt4all\\gpt4all-lora-quantized-win64`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(nodeData: INodeData): Promise<any> {
|
||||||
|
const modelPath = nodeData.inputs?.modelFilePath as string
|
||||||
|
const executablePath = nodeData.inputs?.executablePath as string
|
||||||
|
|
||||||
|
const llm = new GPT4All({
|
||||||
|
modelPath,
|
||||||
|
executablePath
|
||||||
|
})
|
||||||
|
|
||||||
|
return llm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { nodeClass: GPT4All_LLMs }
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
import { spawn } from 'child_process'
|
||||||
|
import { BaseLLM, BaseLLMParams } from 'langchain/llms/base'
|
||||||
|
import { Generation, LLMResult } from 'langchain/schema'
|
||||||
|
|
||||||
|
export class GPT4All extends BaseLLM {
|
||||||
|
executablePath: string
|
||||||
|
modelPath: string
|
||||||
|
promptTimeout: number
|
||||||
|
client: ReturnType<typeof spawn> | null = null
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
config: {
|
||||||
|
executablePath?: string
|
||||||
|
modelPath?: string
|
||||||
|
promptTimeout?: number
|
||||||
|
} & BaseLLMParams
|
||||||
|
) {
|
||||||
|
super(config ?? {})
|
||||||
|
const executablePath = config.executablePath
|
||||||
|
if (!executablePath) {
|
||||||
|
throw new Error(`Executable path must be provided`)
|
||||||
|
}
|
||||||
|
this.executablePath = executablePath
|
||||||
|
|
||||||
|
const modelPath = config.modelPath
|
||||||
|
if (!modelPath) {
|
||||||
|
throw new Error(`Model path must be provided`)
|
||||||
|
}
|
||||||
|
this.modelPath = modelPath
|
||||||
|
|
||||||
|
this.promptTimeout = Math.min(1000, config.promptTimeout || 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
if (this.client !== null) {
|
||||||
|
this.client.kill()
|
||||||
|
this.client = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(): Promise<void> {
|
||||||
|
if (this.client !== null) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
let spawnArgs = [this.executablePath, '--model', this.modelPath]
|
||||||
|
|
||||||
|
this.client = spawn(spawnArgs[0], spawnArgs.slice(1), { stdio: ['pipe', 'pipe', 'ignore'] })
|
||||||
|
|
||||||
|
// wait for the bot to be ready
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
this.client?.stdout?.on('data', (data) => {
|
||||||
|
if (data.toString().includes('>')) {
|
||||||
|
resolve(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_llmType(): string {
|
||||||
|
return 'gpt4all'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call out to GPT4All's generate method.
|
||||||
|
*
|
||||||
|
* @param prompt - The prompt to pass into the model.
|
||||||
|
* @param stop - Optional list of stop words to use when generating.
|
||||||
|
*
|
||||||
|
* @returns the full LLM response.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* import { GPT4All } from "./Gpt4All.ts";
|
||||||
|
* const gpt4All = new GPT4All();
|
||||||
|
* const response = await gpt4All.call("Tell me a joke.")
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async _call(prompt: string, _stop?: string[]): Promise<string> {
|
||||||
|
await this.open()
|
||||||
|
const response = await sendMessageAndWaitForResult(this.client, prompt, this.promptTimeout)
|
||||||
|
this.close()
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
|
const ansiEscapeSequences = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g
|
||||||
|
const finalResponse = response.replace(ansiEscapeSequences, '')
|
||||||
|
return finalResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call out to GPT4All's generate method.
|
||||||
|
*
|
||||||
|
* @param prompts - The prompts to pass into the model.
|
||||||
|
* @param stop - Optional list of stop words to use when generating.
|
||||||
|
*
|
||||||
|
* @returns the full LLM output.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* ```ts
|
||||||
|
* import { GPT4All } from "./Gpt4All.ts";
|
||||||
|
* const gpt4All = new GPT4All();
|
||||||
|
* const response = await gpt4All.generate(["Tell me a joke."])
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
async _generate(prompts: Array<string>, stop?: string[]): Promise<LLMResult> {
|
||||||
|
const generations: Array<Array<Generation>> = []
|
||||||
|
for await (const prompt of prompts) {
|
||||||
|
const result = await this._call(prompt, stop)
|
||||||
|
generations.push([{ text: result }])
|
||||||
|
}
|
||||||
|
return { generations }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendMessageAndWaitForResult(client: any, prompt: string, promptTimeout: number): Promise<string> {
|
||||||
|
if (client === null) {
|
||||||
|
throw new Error('Client is not initialized.')
|
||||||
|
}
|
||||||
|
|
||||||
|
client.stdin.write(prompt + '\n')
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let response: string = ''
|
||||||
|
let timeoutId: NodeJS.Timeout
|
||||||
|
|
||||||
|
const onStdoutData = (data: Buffer) => {
|
||||||
|
const text = data.toString()
|
||||||
|
if (timeoutId) {
|
||||||
|
clearTimeout(timeoutId)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.includes('>')) {
|
||||||
|
terminateAndResolve(response) // Remove the trailing "\f" delimiter
|
||||||
|
} else {
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
|
terminateAndResolve(response)
|
||||||
|
}, promptTimeout) // Set a timeout of
|
||||||
|
}
|
||||||
|
response += text
|
||||||
|
}
|
||||||
|
|
||||||
|
const onStdoutError = (err: Error) => {
|
||||||
|
client.stdout.removeListener('data', onStdoutData)
|
||||||
|
client.stdout.removeListener('error', onStdoutError)
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
const terminateAndResolve = (finalResponse: string) => {
|
||||||
|
client.stdout.removeListener('data', onStdoutData)
|
||||||
|
client.stdout.removeListener('error', onStdoutError)
|
||||||
|
// check for > at the end and remove it
|
||||||
|
if (finalResponse.endsWith('>')) {
|
||||||
|
finalResponse = finalResponse.slice(0, -1)
|
||||||
|
}
|
||||||
|
resolve(finalResponse)
|
||||||
|
}
|
||||||
|
|
||||||
|
client.stdout.on('data', onStdoutData)
|
||||||
|
client.stdout.on('error', onStdoutError)
|
||||||
|
})
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 4.3 KiB |
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"lib": ["ES2020"],
|
"lib": ["ES2020", "ES2021.String"],
|
||||||
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */,
|
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */,
|
||||||
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
|
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
|
||||||
"target": "ES2020", // or higher
|
"target": "ES2020", // or higher
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue