2
0

🐛 (dify) Fix conversation ID being overwritten randomly

Closes #1452
This commit is contained in:
Baptiste Arnaud
2024-04-23 11:23:04 +02:00
parent fadcd3af22
commit 7f39d5ac79
9 changed files with 132 additions and 42 deletions

View File

@@ -132,7 +132,13 @@ export const ZodFieldLayout = ({
<DropdownList <DropdownList
currentItem={data ?? layout?.defaultValue} currentItem={data ?? layout?.defaultValue}
onItemSelect={onDataChange} onItemSelect={onDataChange}
items={innerSchema._def.values} items={
layout?.hiddenItems
? innerSchema._def.values.filter(
(v: any) => !layout.hiddenItems.includes(v)
)
: innerSchema._def.values
}
label={layout?.label} label={layout?.label}
helperText={ helperText={
layout?.helperText ? ( layout?.helperText ? (

View File

@@ -0,0 +1,15 @@
---
title: Dify.AI
---
This block allows you to integrate your Dify.AI's assistant in your typebot.
## Create Chat Message
This action sends a user message to your agent. Then you can save `Answer` to a variable and display it in your typebot.
You are expected to provide the following parameters:
- `Query`: The user message you want to send to your agent.
- `Conversation ID`: The conversation ID you want to use for this message. If you don't provide one, a new conversation will be created. This variable content will be updated automatically if a new conversation is created.
- `User`: The user email used to identify the user in the conversation.

View File

@@ -125,7 +125,8 @@
"editor/blocks/integrations/zemantic-ai", "editor/blocks/integrations/zemantic-ai",
"editor/blocks/integrations/mistral", "editor/blocks/integrations/mistral",
"editor/blocks/integrations/elevenlabs", "editor/blocks/integrations/elevenlabs",
"editor/blocks/integrations/anthropic" "editor/blocks/integrations/anthropic",
"editor/blocks/integrations/dify-ai"
] ]
} }
] ]

View File

@@ -19591,7 +19591,7 @@
"query": { "query": {
"type": "string" "type": "string"
}, },
"conversation_id": { "conversationVariableId": {
"type": "string" "type": "string"
}, },
"user": { "user": {
@@ -19629,6 +19629,10 @@
} }
} }
} }
},
"conversation_id": {
"type": "string",
"description": "Deprecated, use `conversationVariableId` instead"
} }
}, },
"required": [ "required": [

View File

@@ -10419,7 +10419,7 @@
"query": { "query": {
"type": "string" "type": "string"
}, },
"conversation_id": { "conversationVariableId": {
"type": "string" "type": "string"
}, },
"user": { "user": {
@@ -10457,6 +10457,10 @@
} }
} }
} }
},
"conversation_id": {
"type": "string",
"description": "Deprecated, use `conversationVariableId` instead"
} }
}, },
"required": [ "required": [

View File

@@ -4,45 +4,67 @@ import { auth } from '../auth'
import { defaultBaseUrl } from '../constants' import { defaultBaseUrl } from '../constants'
import { Chunk } from '../types' import { Chunk } from '../types'
import ky from 'ky' import ky from 'ky'
import { deprecatedCreateChatMessageOptions } from '../deprecated'
export const createChatMessage = createAction({ export const createChatMessage = createAction({
auth, auth,
name: 'Create Chat Message', name: 'Create Chat Message',
options: option.object({ options: option
query: option.string.layout({ .object({
label: 'Query', query: option.string.layout({
placeholder: 'User input/question content', label: 'Query',
inputType: 'textarea', placeholder: 'User input/question content',
isRequired: true, inputType: 'textarea',
}), isRequired: true,
conversation_id: option.string.layout({
label: 'Conversation ID',
moreInfoTooltip:
'Used to remember the conversation with the user. If empty, a new conversation id is created.',
}),
user: option.string.layout({
label: 'User',
moreInfoTooltip:
'The user identifier, defined by the developer, must ensure uniqueness within the app.',
}),
inputs: option.keyValueList.layout({
accordion: 'Inputs',
}),
responseMapping: option
.saveResponseArray(['Answer', 'Conversation ID', 'Total Tokens'] as const)
.layout({
accordion: 'Save response',
}), }),
}),
conversationVariableId: option.string.layout({
label: 'Conversation ID',
moreInfoTooltip:
'Used to remember the conversation with the user. If empty, a new conversation ID is created.',
inputType: 'variableDropdown',
}),
user: option.string.layout({
label: 'User',
moreInfoTooltip:
'The user identifier, defined by the developer, must ensure uniqueness within the app.',
}),
inputs: option.keyValueList.layout({
accordion: 'Inputs',
}),
responseMapping: option
.saveResponseArray(
['Answer', 'Conversation ID', 'Total Tokens'] as const,
{
item: {
hiddenItems: ['Conversation ID'],
},
}
)
.layout({
accordion: 'Save response',
}),
})
.merge(deprecatedCreateChatMessageOptions),
getSetVariableIds: ({ responseMapping }) => getSetVariableIds: ({ responseMapping }) =>
responseMapping?.map((r) => r.variableId).filter(isDefined) ?? [], responseMapping?.map((r) => r.variableId).filter(isDefined) ?? [],
run: { run: {
server: async ({ server: async ({
credentials: { apiEndpoint, apiKey }, credentials: { apiEndpoint, apiKey },
options: { conversation_id, query, user, inputs, responseMapping }, options: {
conversationVariableId,
conversation_id,
query,
user,
inputs,
responseMapping,
},
variables, variables,
logs, logs,
}) => { }) => {
const existingDifyConversationId = conversationVariableId
? variables.get(conversationVariableId)
: conversation_id
try { try {
const response = await ky( const response = await ky(
(apiEndpoint ?? defaultBaseUrl) + '/v1/chat-messages', (apiEndpoint ?? defaultBaseUrl) + '/v1/chat-messages',
@@ -63,7 +85,7 @@ export const createChatMessage = createAction({
}, {}) ?? {}, }, {}) ?? {},
query, query,
response_mode: 'streaming', response_mode: 'streaming',
conversation_id, conversation_id: existingDifyConversationId,
user, user,
files: [], files: [],
}), }),
@@ -139,9 +161,20 @@ export const createChatMessage = createAction({
if (item === 'Answer') if (item === 'Answer')
variables.set(mapping.variableId, convertNonMarkdownLinks(answer)) variables.set(mapping.variableId, convertNonMarkdownLinks(answer))
if (item === 'Conversation ID' && isNotEmpty(conversationId)) if (
item === 'Conversation ID' &&
isNotEmpty(conversationId) &&
isEmpty(existingDifyConversationId?.toString())
)
variables.set(mapping.variableId, conversationId) variables.set(mapping.variableId, conversationId)
if (
conversationVariableId &&
isNotEmpty(conversationId) &&
isEmpty(existingDifyConversationId?.toString())
)
variables.set(conversationVariableId, conversationId)
if (item === 'Total Tokens') if (item === 'Total Tokens')
variables.set(mapping.variableId, totalTokens) variables.set(mapping.variableId, totalTokens)
}) })

View File

@@ -0,0 +1,12 @@
import { option } from '@typebot.io/forge'
export const deprecatedCreateChatMessageOptions = option.object({
conversation_id: option.string
.layout({
label: 'Conversation ID',
moreInfoTooltip:
'Used to remember the conversation with the user. If empty, a new conversation id is created.',
isHidden: true,
})
.describe('Deprecated, use `conversationVariableId` instead'),
})

View File

@@ -1,6 +1,6 @@
import { ZodRawShape } from 'zod' import { ZodRawShape, ZodTypeAny } from 'zod'
import { AuthDefinition, BlockDefinition, ActionDefinition } from './types' import { AuthDefinition, BlockDefinition, ActionDefinition } from './types'
import { z } from './zod' import { ZodLayoutMetadata, z } from './zod'
export const variableStringSchema = z.custom<`{{${string}}}`>((val) => export const variableStringSchema = z.custom<`{{${string}}}`>((val) =>
/^{{.+}}$/g.test(val as string) /^{{.+}}$/g.test(val as string)
@@ -137,17 +137,31 @@ export const option = {
z.object({ [field]: z.undefined() }), z.object({ [field]: z.undefined() }),
...schemas, ...schemas,
]), ]),
saveResponseArray: <I extends readonly [string, ...string[]]>(items: I) => saveResponseArray: <I extends readonly [string, ...string[]]>(
items: I,
layouts?: {
item?: ZodLayoutMetadata<ZodTypeAny>
variableId?: ZodLayoutMetadata<ZodTypeAny>
}
) =>
z z
.array( .array(
z.object({ z.object({
item: z.enum(items).optional().layout({ item: z
placeholder: 'Select a response', .enum(items)
defaultValue: items[0], .optional()
}), .layout({
variableId: z.string().optional().layout({ ...(layouts?.item ?? {}),
inputType: 'variableDropdown', placeholder: 'Select a response',
}), defaultValue: items[0],
}),
variableId: z
.string()
.optional()
.layout({
...(layouts?.variableId ?? {}),
inputType: 'variableDropdown',
}),
}) })
) )
.optional(), .optional(),

View File

@@ -22,6 +22,7 @@ export interface ZodLayoutMetadata<
moreInfoTooltip?: string moreInfoTooltip?: string
isHidden?: boolean isHidden?: boolean
isDebounceDisabled?: boolean isDebounceDisabled?: boolean
hiddenItems?: string[]
} }
declare module 'zod' { declare module 'zod' {