Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/HandleEscapeChar #612

Merged
merged 1 commit into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions packages/components/nodes/chains/LLMChain/LLMChain.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ICommonObject, INode, INodeData, INodeOutputsValue, INodeParams } from '../../../src/Interface'
import { getBaseClasses } from '../../../src/utils'
import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils'
import { LLMChain } from 'langchain/chains'
import { BaseLanguageModel } from 'langchain/base_language'
import { ConsoleCallbackHandler, CustomChainHandler } from '../../../src/handler'
Expand Down Expand Up @@ -73,15 +73,19 @@ class LLMChain_Chains implements INode {
console.log('\x1b[92m\x1b[1m\n*****OUTPUT PREDICTION*****\n\x1b[0m\x1b[0m')
// eslint-disable-next-line no-console
console.log(res)
return res
/**
* Apply string transformation to convert special chars:
* FROM: hello i am ben\n\n\thow are you?
* TO: hello i am benFLOWISE_NEWLINEFLOWISE_NEWLINEFLOWISE_TABhow are you?
*/
return handleEscapeCharacters(res, false)
}
}

async run(nodeData: INodeData, input: string, options: ICommonObject): Promise<string> {
const inputVariables = nodeData.instance.prompt.inputVariables as string[] // ["product"]
const chain = nodeData.instance as LLMChain
const promptValues = nodeData.inputs?.prompt.promptValues as ICommonObject

const res = await runPrediction(inputVariables, chain, input, promptValues, options)
// eslint-disable-next-line no-console
console.log('\x1b[93m\x1b[1m\n*****FINAL RESULT*****\n\x1b[0m\x1b[0m')
Expand All @@ -95,14 +99,21 @@ const runPrediction = async (
inputVariables: string[],
chain: LLMChain,
input: string,
promptValues: ICommonObject,
promptValuesRaw: ICommonObject,
options: ICommonObject
) => {
const loggerHandler = new ConsoleCallbackHandler(options.logger)
const isStreaming = options.socketIO && options.socketIOClientId
const socketIO = isStreaming ? options.socketIO : undefined
const socketIOClientId = isStreaming ? options.socketIOClientId : ''

/**
* Apply string transformation to reverse converted special chars:
* FROM: { "value": "hello i am benFLOWISE_NEWLINEFLOWISE_NEWLINEFLOWISE_TABhow are you?" }
* TO: { "value": "hello i am ben\n\n\thow are you?" }
*/
const promptValues = handleEscapeCharacters(promptValuesRaw, true)

if (inputVariables.length === 1) {
if (isStreaming) {
const handler = new CustomChainHandler(socketIO, socketIOClientId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class ChatPromptTemplate_Prompts implements INode {

let promptValues: ICommonObject = {}
if (promptValuesStr) {
promptValues = JSON.parse(promptValuesStr.replace(/\s/g, ''))
promptValues = JSON.parse(promptValuesStr)
}
// @ts-ignore
prompt.promptValues = promptValues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ class FewShotPromptTemplate_Prompts implements INode {
const examplePrompt = nodeData.inputs?.examplePrompt as PromptTemplate

const inputVariables = getInputVariables(suffix)
const examples: Example[] = JSON.parse(examplesStr.replace(/\s/g, ''))
const examples: Example[] = JSON.parse(examplesStr)

try {
const obj: FewShotPromptTemplateInput = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class PromptTemplate_Prompts implements INode {

let promptValues: ICommonObject = {}
if (promptValuesStr) {
promptValues = JSON.parse(promptValuesStr.replace(/\s/g, ''))
promptValues = JSON.parse(promptValuesStr)
}

const inputVariables = getInputVariables(template)
Expand Down
34 changes: 34 additions & 0 deletions packages/components/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,40 @@ export const getEnvironmentVariable = (name: string): string | undefined => {
}
}

// reference https://www.freeformatter.com/json-escape.html
const jsonEscapeCharacters = [
{ escape: '"', value: 'FLOWISE_DOUBLE_QUOTE' },
{ escape: '\n', value: 'FLOWISE_NEWLINE' },
{ escape: '\b', value: 'FLOWISE_BACKSPACE' },
{ escape: '\f', value: 'FLOWISE_FORM_FEED' },
{ escape: '\r', value: 'FLOWISE_CARRIAGE_RETURN' },
{ escape: '\t', value: 'FLOWISE_TAB' },
{ escape: '\\', value: 'FLOWISE_BACKSLASH' }
]

function handleEscapesJSONParse(input: string, reverse: Boolean): string {
for (const element of jsonEscapeCharacters) {
input = reverse ? input.replaceAll(element.value, element.escape) : input.replaceAll(element.escape, element.value)
}
return input
}

function iterateEscapesJSONParse(input: any, reverse: Boolean): any {
for (const element in input) {
const type = typeof input[element]
if (type === 'string') input[element] = handleEscapesJSONParse(input[element], reverse)
else if (type === 'object') input[element] = iterateEscapesJSONParse(input[element], reverse)
}
return input
}

export function handleEscapeCharacters(input: any, reverse: Boolean): any {
const type = typeof input
if (type === 'string') return handleEscapesJSONParse(input, reverse)
else if (type === 'object') return iterateEscapesJSONParse(input, reverse)
return input
}

/*
* List of dependencies allowed to be import in vm2
*/
Expand Down
2 changes: 1 addition & 1 deletion packages/components/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"lib": ["ES2020"],
"lib": ["ES2020", "ES2021.String"],
"experimentalDecorators": true /* Enable experimental support for TC39 stage 2 draft decorators. */,
"emitDecoratorMetadata": true /* Emit design-type metadata for decorated declarations in source files. */,
"target": "ES2020", // or higher
Expand Down
9 changes: 7 additions & 2 deletions packages/server/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
IOverrideConfig
} from '../Interface'
import { cloneDeep, get, omit, merge } from 'lodash'
import { ICommonObject, getInputVariables, IDatabaseEntity } from 'flowise-components'
import { ICommonObject, getInputVariables, IDatabaseEntity, handleEscapeCharacters } from 'flowise-components'
import { scryptSync, randomBytes, timingSafeEqual } from 'crypto'
import { ChatFlow } from '../entity/ChatFlow'
import { ChatMessage } from '../entity/ChatMessage'
Expand Down Expand Up @@ -325,8 +325,13 @@ export const getVariableValue = (paramValue: string, reactFlowNodes: IReactFlowN
const variableEndIdx = startIdx
const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)

/**
* Apply string transformation to convert special chars:
* FROM: hello i am ben\n\n\thow are you?
* TO: hello i am benFLOWISE_NEWLINEFLOWISE_NEWLINEFLOWISE_TABhow are you?
*/
if (isAcceptVariable && variableFullPath === QUESTION_VAR_PREFIX) {
variableDict[`{{${variableFullPath}}}`] = question
variableDict[`{{${variableFullPath}}}`] = handleEscapeCharacters(question, false)
}

// Split by first occurrence of '.' to get just nodeId
Expand Down