Skip to content

Commit 4aa97b0

Browse files
authored
Bugfix/Escape JSON in Prompt Message (#3901)
add fix to only get variables when there is no colon
1 parent 4c9d46d commit 4aa97b0

File tree

16 files changed

+149
-47
lines changed

16 files changed

+149
-47
lines changed

packages/components/nodes/agents/ConversationalAgent/ConversationalAgent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { AgentStep } from '@langchain/core/agents'
77
import { renderTemplate, MessagesPlaceholder, HumanMessagePromptTemplate, PromptTemplate } from '@langchain/core/prompts'
88
import { RunnableSequence } from '@langchain/core/runnables'
99
import { ChatConversationalAgent } from 'langchain/agents'
10-
import { getBaseClasses } from '../../../src/utils'
10+
import { getBaseClasses, transformBracesWithColon } from '../../../src/utils'
1111
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
1212
import {
1313
IVisionChatModal,
@@ -218,7 +218,7 @@ const prepareAgent = async (
218218
let tools = nodeData.inputs?.tools as Tool[]
219219
tools = flatten(tools)
220220
const memory = nodeData.inputs?.memory as FlowiseMemory
221-
const systemMessage = nodeData.inputs?.systemMessage as string
221+
let systemMessage = nodeData.inputs?.systemMessage as string
222222
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
223223
const inputKey = memory.inputKey ? memory.inputKey : 'input'
224224
const prependMessages = options?.prependMessages
@@ -228,6 +228,8 @@ const prepareAgent = async (
228228
toolNames: tools.map((tool) => tool.name)
229229
})
230230

231+
systemMessage = transformBracesWithColon(systemMessage)
232+
231233
const prompt = ChatConversationalAgent.createPrompt(tools, {
232234
systemMessage: systemMessage ? systemMessage : DEFAULT_PREFIX,
233235
outputParser

packages/components/nodes/agents/ConversationalRetrievalToolAgent/ConversationalRetrievalToolAgent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { RunnableSequence } from '@langchain/core/runnables'
55
import { BaseChatModel } from '@langchain/core/language_models/chat_models'
66
import { ChatPromptTemplate, MessagesPlaceholder, HumanMessagePromptTemplate, PromptTemplate } from '@langchain/core/prompts'
77
import { formatToOpenAIToolMessages } from 'langchain/agents/format_scratchpad/openai_tools'
8-
import { getBaseClasses } from '../../../src/utils'
8+
import { getBaseClasses, transformBracesWithColon } from '../../../src/utils'
99
import { type ToolsAgentStep } from 'langchain/agents/openai/output_parser'
1010
import {
1111
FlowiseMemory,
@@ -212,13 +212,15 @@ const prepareAgent = async (
212212
const model = nodeData.inputs?.model as BaseChatModel
213213
const maxIterations = nodeData.inputs?.maxIterations as string
214214
const memory = nodeData.inputs?.memory as FlowiseMemory
215-
const systemMessage = nodeData.inputs?.systemMessage as string
215+
let systemMessage = nodeData.inputs?.systemMessage as string
216216
let tools = nodeData.inputs?.tools
217217
tools = flatten(tools)
218218
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
219219
const inputKey = memory.inputKey ? memory.inputKey : 'input'
220220
const vectorStoreRetriever = nodeData.inputs?.vectorStoreRetriever as BaseRetriever
221221

222+
systemMessage = transformBracesWithColon(systemMessage)
223+
222224
const prompt = ChatPromptTemplate.fromMessages([
223225
['system', systemMessage ? systemMessage : `You are a helpful AI assistant.`],
224226
new MessagesPlaceholder(memoryKey),

packages/components/nodes/agents/ToolAgent/ToolAgent.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,13 @@ import { BaseChatModel } from '@langchain/core/language_models/chat_models'
77
import { ChatPromptTemplate, MessagesPlaceholder, HumanMessagePromptTemplate, PromptTemplate } from '@langchain/core/prompts'
88
import { formatToOpenAIToolMessages } from 'langchain/agents/format_scratchpad/openai_tools'
99
import { type ToolsAgentStep } from 'langchain/agents/openai/output_parser'
10-
import { extractOutputFromArray, getBaseClasses, handleEscapeCharacters, removeInvalidImageMarkdown } from '../../../src/utils'
10+
import {
11+
extractOutputFromArray,
12+
getBaseClasses,
13+
handleEscapeCharacters,
14+
removeInvalidImageMarkdown,
15+
transformBracesWithColon
16+
} from '../../../src/utils'
1117
import {
1218
FlowiseMemory,
1319
ICommonObject,
@@ -236,13 +242,15 @@ const prepareAgent = async (
236242
const model = nodeData.inputs?.model as BaseChatModel
237243
const maxIterations = nodeData.inputs?.maxIterations as string
238244
const memory = nodeData.inputs?.memory as FlowiseMemory
239-
const systemMessage = nodeData.inputs?.systemMessage as string
245+
let systemMessage = nodeData.inputs?.systemMessage as string
240246
let tools = nodeData.inputs?.tools
241247
tools = flatten(tools)
242248
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
243249
const inputKey = memory.inputKey ? memory.inputKey : 'input'
244250
const prependMessages = options?.prependMessages
245251

252+
systemMessage = transformBracesWithColon(systemMessage)
253+
246254
let prompt = ChatPromptTemplate.fromMessages([
247255
['system', systemMessage],
248256
new MessagesPlaceholder(memoryKey),

packages/components/nodes/agents/XMLAgent/XMLAgent.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { RunnableSequence } from '@langchain/core/runnables'
66
import { Tool } from '@langchain/core/tools'
77
import { ChatPromptTemplate, HumanMessagePromptTemplate, MessagesPlaceholder } from '@langchain/core/prompts'
88
import { formatLogToMessage } from 'langchain/agents/format_scratchpad/log_to_message'
9-
import { getBaseClasses } from '../../../src/utils'
9+
import { getBaseClasses, transformBracesWithColon } from '../../../src/utils'
1010
import {
1111
FlowiseMemory,
1212
ICommonObject,
@@ -222,13 +222,15 @@ const prepareAgent = async (
222222
const model = nodeData.inputs?.model as BaseChatModel
223223
const maxIterations = nodeData.inputs?.maxIterations as string
224224
const memory = nodeData.inputs?.memory as FlowiseMemory
225-
const systemMessage = nodeData.inputs?.systemMessage as string
225+
let systemMessage = nodeData.inputs?.systemMessage as string
226226
let tools = nodeData.inputs?.tools
227227
tools = flatten(tools)
228228
const inputKey = memory.inputKey ? memory.inputKey : 'input'
229229
const memoryKey = memory.memoryKey ? memory.memoryKey : 'chat_history'
230230
const prependMessages = options?.prependMessages
231231

232+
systemMessage = transformBracesWithColon(systemMessage)
233+
232234
let promptMessage = systemMessage ? systemMessage : defaultSystemMessage
233235
if (memory.memoryKey) promptMessage = promptMessage.replaceAll('{chat_history}', `{${memory.memoryKey}}`)
234236
if (memory.inputKey) promptMessage = promptMessage.replaceAll('{input}', `{${memory.inputKey}}`)

packages/components/nodes/chains/ConversationChain/ConversationChain.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
IServerSideEventStreamer
2828
} from '../../../src/Interface'
2929
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
30-
import { getBaseClasses, handleEscapeCharacters } from '../../../src/utils'
30+
import { getBaseClasses, handleEscapeCharacters, transformBracesWithColon } from '../../../src/utils'
3131

3232
let systemMessage = `The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.`
3333
const inputKey = 'input'
@@ -170,7 +170,8 @@ class ConversationChain_Chains implements INode {
170170

171171
const prepareChatPrompt = (nodeData: INodeData, humanImageMessages: MessageContentImageUrl[]) => {
172172
const memory = nodeData.inputs?.memory as FlowiseMemory
173-
const prompt = nodeData.inputs?.systemMessagePrompt as string
173+
let prompt = nodeData.inputs?.systemMessagePrompt as string
174+
prompt = transformBracesWithColon(prompt)
174175
const chatPromptTemplate = nodeData.inputs?.chatPromptTemplate as ChatPromptTemplate
175176
let model = nodeData.inputs?.model as BaseChatModel
176177

packages/components/nodes/chains/SqlDatabaseChain/SqlDatabaseChain.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { SqlDatabaseChain, SqlDatabaseChainInput, DEFAULT_SQL_DATABASE_PROMPT }
66
import { SqlDatabase } from 'langchain/sql_db'
77
import { ICommonObject, INode, INodeData, INodeParams, IServerSideEventStreamer } from '../../../src/Interface'
88
import { ConsoleCallbackHandler, CustomChainHandler, additionalCallbacks } from '../../../src/handler'
9-
import { getBaseClasses, getInputVariables } from '../../../src/utils'
9+
import { getBaseClasses, getInputVariables, transformBracesWithColon } from '../../../src/utils'
1010
import { checkInputs, Moderation, streamResponse } from '../../moderation/Moderation'
1111
import { formatResponse } from '../../outputparsers/OutputParserHelpers'
1212

@@ -247,6 +247,7 @@ const getSQLDBChain = async (
247247
}
248248

249249
if (customPrompt) {
250+
customPrompt = transformBracesWithColon(customPrompt)
250251
const options: PromptTemplateInput = {
251252
template: customPrompt,
252253
inputVariables: getInputVariables(customPrompt)

packages/components/nodes/prompts/ChatPromptTemplate/ChatPromptTemplate.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ICommonObject, IDatabaseEntity, INode, INodeData, INodeParams } from '../../../src/Interface'
2-
import { getBaseClasses } from '../../../src/utils'
2+
import { getBaseClasses, transformBracesWithColon } from '../../../src/utils'
33
import { ChatPromptTemplate, SystemMessagePromptTemplate, HumanMessagePromptTemplate } from '@langchain/core/prompts'
44
import { getVM } from '../../sequentialagents/commonUtils'
55
import { DataSource } from 'typeorm'
@@ -98,14 +98,17 @@ class ChatPromptTemplate_Prompts implements INode {
9898
}
9999

100100
async init(nodeData: INodeData, _: string, options: ICommonObject): Promise<any> {
101-
const systemMessagePrompt = nodeData.inputs?.systemMessagePrompt as string
102-
const humanMessagePrompt = nodeData.inputs?.humanMessagePrompt as string
101+
let systemMessagePrompt = nodeData.inputs?.systemMessagePrompt as string
102+
let humanMessagePrompt = nodeData.inputs?.humanMessagePrompt as string
103103
const promptValuesStr = nodeData.inputs?.promptValues
104104
const tabIdentifier = nodeData.inputs?.[`${TAB_IDENTIFIER}_${nodeData.id}`] as string
105105
const selectedTab = tabIdentifier ? tabIdentifier.split(`_${nodeData.id}`)[0] : 'messageHistoryCode'
106106
const messageHistoryCode = nodeData.inputs?.messageHistoryCode
107107
const messageHistory = nodeData.inputs?.messageHistory
108108

109+
systemMessagePrompt = transformBracesWithColon(systemMessagePrompt)
110+
humanMessagePrompt = transformBracesWithColon(humanMessagePrompt)
111+
109112
let prompt = ChatPromptTemplate.fromMessages([
110113
SystemMessagePromptTemplate.fromTemplate(systemMessagePrompt),
111114
HumanMessagePromptTemplate.fromTemplate(humanMessagePrompt)

packages/components/nodes/prompts/PromptLangfuse/PromptLangfuse.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ICommonObject, INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface'
2-
import { getBaseClasses, getCredentialData, getCredentialParam, getInputVariables } from '../../../src/utils'
2+
import { getBaseClasses, getCredentialData, getCredentialParam, getInputVariables, transformBracesWithColon } from '../../../src/utils'
33
import { PromptTemplateInput } from '@langchain/core/prompts'
44
import { Langfuse } from 'langfuse'
55

@@ -64,7 +64,7 @@ class PromptLangfuse_Prompts implements INode {
6464
})
6565

6666
const langfusePrompt = await langfuse.getPrompt(nodeData.inputs?.template as string)
67-
const template = langfusePrompt.getLangchainPrompt()
67+
let template = langfusePrompt.getLangchainPrompt()
6868

6969
const promptValuesStr = nodeData.inputs?.promptValues
7070

@@ -78,6 +78,7 @@ class PromptLangfuse_Prompts implements INode {
7878
}
7979

8080
const inputVariables = getInputVariables(template)
81+
template = transformBracesWithColon(template)
8182

8283
try {
8384
const options: PromptTemplateInput = {

packages/components/nodes/prompts/PromptTemplate/PromptTemplate.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ICommonObject, INode, INodeData, INodeParams, PromptTemplate } from '../../../src/Interface'
2-
import { getBaseClasses, getInputVariables } from '../../../src/utils'
2+
import { getBaseClasses, getInputVariables, transformBracesWithColon } from '../../../src/utils'
33
import { PromptTemplateInput } from '@langchain/core/prompts'
44

55
class PromptTemplate_Prompts implements INode {
@@ -42,7 +42,7 @@ class PromptTemplate_Prompts implements INode {
4242
}
4343

4444
async init(nodeData: INodeData): Promise<any> {
45-
const template = nodeData.inputs?.template as string
45+
let template = nodeData.inputs?.template as string
4646
const promptValuesStr = nodeData.inputs?.promptValues
4747

4848
let promptValues: ICommonObject = {}
@@ -55,6 +55,7 @@ class PromptTemplate_Prompts implements INode {
5555
}
5656

5757
const inputVariables = getInputVariables(template)
58+
template = transformBracesWithColon(template)
5859

5960
try {
6061
const options: PromptTemplateInput = {

packages/components/nodes/retrievers/PromptRetriever/PromptRetriever.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { transformBracesWithColon } from '../../../src'
12
import { INode, INodeData, INodeParams, PromptRetriever, PromptRetrieverInput } from '../../../src/Interface'
23

34
class PromptRetriever_Retrievers implements INode {
@@ -48,7 +49,8 @@ class PromptRetriever_Retrievers implements INode {
4849
async init(nodeData: INodeData): Promise<any> {
4950
const name = nodeData.inputs?.name as string
5051
const description = nodeData.inputs?.description as string
51-
const systemMessage = nodeData.inputs?.systemMessage as string
52+
let systemMessage = nodeData.inputs?.systemMessage as string
53+
systemMessage = transformBracesWithColon(systemMessage)
5254

5355
const obj = {
5456
name,

packages/components/nodes/sequentialagents/Agent/Agent.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ import {
2929
getVars,
3030
handleEscapeCharacters,
3131
prepareSandboxVars,
32-
removeInvalidImageMarkdown
32+
removeInvalidImageMarkdown,
33+
transformBracesWithColon
3334
} from '../../../src/utils'
3435
import {
3536
customGet,
@@ -456,7 +457,9 @@ class Agent_SeqAgents implements INode {
456457
let tools = nodeData.inputs?.tools
457458
tools = flatten(tools)
458459
let agentSystemPrompt = nodeData.inputs?.systemMessagePrompt as string
460+
agentSystemPrompt = transformBracesWithColon(agentSystemPrompt)
459461
let agentHumanPrompt = nodeData.inputs?.humanMessagePrompt as string
462+
agentHumanPrompt = transformBracesWithColon(agentHumanPrompt)
460463
const agentLabel = nodeData.inputs?.agentName as string
461464
const sequentialNodes = nodeData.inputs?.sequentialNode as ISeqAgentNode[]
462465
const maxIterations = nodeData.inputs?.maxIterations as string

packages/components/nodes/sequentialagents/ConditionAgent/ConditionAgent.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
ISeqAgentNode,
1717
ISeqAgentsState
1818
} from '../../../src/Interface'
19-
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
19+
import { getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars, transformBracesWithColon } from '../../../src/utils'
2020
import {
2121
ExtractTool,
2222
checkCondition,
@@ -388,7 +388,9 @@ class ConditionAgent_SeqAgents implements INode {
388388
const output = nodeData.outputs?.output as string
389389
const sequentialNodes = nodeData.inputs?.sequentialNode as ISeqAgentNode[]
390390
let agentPrompt = nodeData.inputs?.systemMessagePrompt as string
391+
agentPrompt = transformBracesWithColon(agentPrompt)
391392
let humanPrompt = nodeData.inputs?.humanMessagePrompt as string
393+
humanPrompt = transformBracesWithColon(humanPrompt)
392394
const promptValuesStr = nodeData.inputs?.promptValues
393395
const conditionAgentStructuredOutput = nodeData.inputs?.conditionAgentStructuredOutput
394396
const model = nodeData.inputs?.model as BaseChatModel

packages/components/nodes/sequentialagents/LLMNode/LLMNode.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,14 @@ import {
1818
ConversationHistorySelection
1919
} from '../../../src/Interface'
2020
import { AgentExecutor } from '../../../src/agents'
21-
import { extractOutputFromArray, getInputVariables, getVars, handleEscapeCharacters, prepareSandboxVars } from '../../../src/utils'
21+
import {
22+
extractOutputFromArray,
23+
getInputVariables,
24+
getVars,
25+
handleEscapeCharacters,
26+
prepareSandboxVars,
27+
transformBracesWithColon
28+
} from '../../../src/utils'
2229
import {
2330
ExtractTool,
2431
convertStructuredSchemaToZod,
@@ -388,7 +395,9 @@ class LLMNode_SeqAgents implements INode {
388395
tools = flatten(tools)
389396

390397
let systemPrompt = nodeData.inputs?.systemMessagePrompt as string
398+
systemPrompt = transformBracesWithColon(systemPrompt)
391399
let humanPrompt = nodeData.inputs?.humanMessagePrompt as string
400+
humanPrompt = transformBracesWithColon(humanPrompt)
392401
const llmNodeLabel = nodeData.inputs?.llmNodeName as string
393402
const sequentialNodes = nodeData.inputs?.sequentialNode as ISeqAgentNode[]
394403
const model = nodeData.inputs?.model as BaseChatModel

packages/components/src/utils.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,14 +271,39 @@ export const getInputVariables = (paramValue: string): string[] => {
271271
const variableStartIdx = variableStack[variableStack.length - 1].startIdx
272272
const variableEndIdx = startIdx
273273
const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)
274-
inputVariables.push(variableFullPath)
274+
if (!variableFullPath.includes(':')) inputVariables.push(variableFullPath)
275275
variableStack.pop()
276276
}
277277
startIdx += 1
278278
}
279279
return inputVariables
280280
}
281281

282+
/**
283+
* Transform curly braces into double curly braces if the content includes a colon.
284+
* @param input - The original string that may contain { ... } segments.
285+
* @returns The transformed string, where { ... } containing a colon has been replaced with {{ ... }}.
286+
*/
287+
export const transformBracesWithColon = (input: string): string => {
288+
// This regex will match anything of the form `{ ... }` (no nested braces).
289+
// `[^{}]*` means: match any characters that are not `{` or `}` zero or more times.
290+
const regex = /\{([^{}]*?)\}/g
291+
292+
return input.replace(regex, (match, groupContent) => {
293+
// groupContent is the text inside the braces `{ ... }`.
294+
295+
if (groupContent.includes(':')) {
296+
// If there's a colon in the content, we turn { ... } into {{ ... }}
297+
// The match is the full string like: "{ answer: hello }"
298+
// groupContent is the inner part like: " answer: hello "
299+
return `{{${groupContent}}}`
300+
} else {
301+
// Otherwise, leave it as is
302+
return match
303+
}
304+
})
305+
}
306+
282307
/**
283308
* Crawl all available urls given a domain url and limit
284309
* @param {string} url

packages/ui/src/utils/genericHelper.js

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -569,32 +569,24 @@ export const generateRandomGradient = () => {
569569
return gradient
570570
}
571571

572-
export const getInputVariables = (paramValue) => {
573-
let returnVal = paramValue
574-
const variableStack = []
575-
const inputVariables = []
576-
let startIdx = 0
577-
const endIdx = returnVal.length
578-
579-
while (startIdx < endIdx) {
580-
const substr = returnVal.substring(startIdx, startIdx + 1)
581-
582-
// Store the opening double curly bracket
583-
if (substr === '{') {
584-
variableStack.push({ substr, startIdx: startIdx + 1 })
585-
}
572+
export const getInputVariables = (input) => {
573+
// This regex will match single curly-braced substrings
574+
const pattern = /\{([^{}]+)\}/g
575+
const results = []
576+
577+
let match
586578

587-
// Found the complete variable
588-
if (substr === '}' && variableStack.length > 0 && variableStack[variableStack.length - 1].substr === '{') {
589-
const variableStartIdx = variableStack[variableStack.length - 1].startIdx
590-
const variableEndIdx = startIdx
591-
const variableFullPath = returnVal.substring(variableStartIdx, variableEndIdx)
592-
inputVariables.push(variableFullPath)
593-
variableStack.pop()
579+
while ((match = pattern.exec(input)) !== null) {
580+
const inside = match[1].trim()
581+
582+
// Check if there's a colon
583+
if (!inside.includes(':')) {
584+
// If there's no colon, add to results
585+
results.push(inside)
594586
}
595-
startIdx += 1
596587
}
597-
return inputVariables
588+
589+
return results
598590
}
599591

600592
export const removeDuplicateURL = (message) => {

0 commit comments

Comments
 (0)