Skip to content

Commit 4ce0851

Browse files
authored
Fix chatflow's type null or blank (#5065)
* fix(entities/ChatFlow.ts): make type column non-nullable with default value * fix(postgres/ModifyChatflowType): set default type and make column non-nullable * fix(sqlite/ModifyChatflowType): set default type and make column non-nullable * fix(mysql/ModifyChatflowType): set default type and make column non-nullable * chore(sqlite/ModifyChatflowType): standardize type column to VARCHAR(20) * chore(postgres/ModifyChatflowType): standardize type column to VARCHAR(20) * fix(mariadb/ModifyChatflowType): set default type and make column non-nullable * chore: rename ChatflowType to EnumChatflowType and update references * feat(chatflows): add chatflow type validation * fix(chatflows): empty string bypassing type validation on update
1 parent 44087bc commit 4ce0851

File tree

10 files changed

+183
-76
lines changed

10 files changed

+183
-76
lines changed

packages/server/src/database/entities/ChatFlow.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
import { Entity, Column, CreateDateColumn, UpdateDateColumn, PrimaryGeneratedColumn } from 'typeorm'
33
import { ChatflowType, IChatFlow } from '../../Interface'
44

5+
export enum EnumChatflowType {
6+
CHATFLOW = 'CHATFLOW',
7+
AGENTFLOW = 'AGENTFLOW',
8+
MULTIAGENT = 'MULTIAGENT',
9+
ASSISTANT = 'ASSISTANT'
10+
}
11+
512
@Entity()
613
export class ChatFlow implements IChatFlow {
714
@PrimaryGeneratedColumn('uuid')
@@ -40,7 +47,7 @@ export class ChatFlow implements IChatFlow {
4047
@Column({ nullable: true, type: 'text' })
4148
category?: string
4249

43-
@Column({ nullable: true, type: 'text' })
50+
@Column({ type: 'varchar', length: 20, default: EnumChatflowType.CHATFLOW })
4451
type?: ChatflowType
4552

4653
@Column({ type: 'timestamp' })
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
import { EnumChatflowType } from '../../entities/ChatFlow'
3+
4+
export class ModifyChatflowType1755066758601 implements MigrationInterface {
5+
public async up(queryRunner: QueryRunner): Promise<void> {
6+
await queryRunner.query(`
7+
UPDATE \`chat_flow\` SET \`type\` = '${EnumChatflowType.CHATFLOW}' WHERE \`type\` IS NULL OR \`type\` = '';
8+
`)
9+
await queryRunner.query(`
10+
ALTER TABLE \`chat_flow\` MODIFY COLUMN \`type\` VARCHAR(20) NOT NULL DEFAULT '${EnumChatflowType.CHATFLOW}';
11+
`)
12+
}
13+
14+
public async down(): Promise<void> {}
15+
}

packages/server/src/database/migrations/mariadb/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEnt
3636
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
3737
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
3838
import { ModifyExecutionDataColumnType1747902489801 } from './1747902489801-ModifyExecutionDataColumnType'
39+
import { ModifyChatflowType1755066758601 } from './1755066758601-ModifyChatflowType'
3940

4041
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mariadb/1720230151482-AddAuthTables'
4142
import { AddWorkspace1725437498242 } from '../../../enterprise/database/migrations/mariadb/1725437498242-AddWorkspace'
@@ -98,5 +99,6 @@ export const mariadbMigrations = [
9899
FixOpenSourceAssistantTable1743758056188,
99100
AddErrorToEvaluationRun1744964560174,
100101
ExecutionLinkWorkspaceId1746862866554,
101-
ModifyExecutionDataColumnType1747902489801
102+
ModifyExecutionDataColumnType1747902489801,
103+
ModifyChatflowType1755066758601
102104
]
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
import { EnumChatflowType } from '../../entities/ChatFlow'
3+
4+
export class ModifyChatflowType1755066758601 implements MigrationInterface {
5+
public async up(queryRunner: QueryRunner): Promise<void> {
6+
await queryRunner.query(`
7+
UPDATE \`chat_flow\` SET \`type\` = '${EnumChatflowType.CHATFLOW}' WHERE \`type\` IS NULL OR \`type\` = '';
8+
`)
9+
await queryRunner.query(`
10+
ALTER TABLE \`chat_flow\` MODIFY COLUMN \`type\` VARCHAR(20) NOT NULL DEFAULT '${EnumChatflowType.CHATFLOW}';
11+
`)
12+
}
13+
14+
public async down(): Promise<void> {}
15+
}

packages/server/src/database/migrations/mysql/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpe
3737
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
3838
import { FixErrorsColumnInEvaluationRun1746437114935 } from './1746437114935-FixErrorsColumnInEvaluationRun'
3939
import { ModifyExecutionDataColumnType1747902489801 } from './1747902489801-ModifyExecutionDataColumnType'
40+
import { ModifyChatflowType1755066758601 } from './1755066758601-ModifyChatflowType'
4041

4142
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/mysql/1720230151482-AddAuthTables'
4243
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/mysql/1720230151484-AddWorkspace'
@@ -100,5 +101,6 @@ export const mysqlMigrations = [
100101
AddErrorToEvaluationRun1744964560174,
101102
FixErrorsColumnInEvaluationRun1746437114935,
102103
ExecutionLinkWorkspaceId1746862866554,
103-
ModifyExecutionDataColumnType1747902489801
104+
ModifyExecutionDataColumnType1747902489801,
105+
ModifyChatflowType1755066758601
104106
]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
import { EnumChatflowType } from '../../entities/ChatFlow'
3+
4+
export class ModifyChatflowType1755066758601 implements MigrationInterface {
5+
public async up(queryRunner: QueryRunner): Promise<void> {
6+
await queryRunner.query(`
7+
UPDATE "chat_flow" SET "type" = '${EnumChatflowType.CHATFLOW}' WHERE "type" IS NULL OR "type" = '';
8+
`)
9+
await queryRunner.query(`
10+
ALTER TABLE "chat_flow" ALTER COLUMN "type" SET DEFAULT '${EnumChatflowType.CHATFLOW}';
11+
`)
12+
await queryRunner.query(`
13+
ALTER TABLE "chat_flow" ALTER COLUMN "type" TYPE VARCHAR(20);
14+
`)
15+
await queryRunner.query(`
16+
ALTER TABLE "chat_flow" ALTER COLUMN "type" SET NOT NULL;
17+
`)
18+
}
19+
20+
public async down(): Promise<void> {}
21+
}

packages/server/src/database/migrations/postgres/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEnt
3636
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
3737
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
3838
import { ModifyExecutionSessionIdFieldType1748450230238 } from './1748450230238-ModifyExecutionSessionIdFieldType'
39+
import { ModifyChatflowType1755066758601 } from './1755066758601-ModifyChatflowType'
3940

4041
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/postgres/1720230151482-AddAuthTables'
4142
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/postgres/1720230151484-AddWorkspace'
@@ -98,5 +99,6 @@ export const postgresMigrations = [
9899
FixOpenSourceAssistantTable1743758056188,
99100
AddErrorToEvaluationRun1744964560174,
100101
ExecutionLinkWorkspaceId1746862866554,
101-
ModifyExecutionSessionIdFieldType1748450230238
102+
ModifyExecutionSessionIdFieldType1748450230238,
103+
ModifyChatflowType1755066758601
102104
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm'
2+
import { EnumChatflowType } from '../../entities/ChatFlow'
3+
4+
export class ModifyChatflowType1755066758601 implements MigrationInterface {
5+
public async up(queryRunner: QueryRunner): Promise<void> {
6+
await queryRunner.query(`
7+
CREATE TABLE "temp_chat_flow" (
8+
"id" varchar PRIMARY KEY NOT NULL,
9+
"name" varchar NOT NULL,
10+
"flowData" text NOT NULL,
11+
"deployed" boolean,
12+
"isPublic" boolean,
13+
"apikeyid" varchar,
14+
"chatbotConfig" text,
15+
"createdDate" datetime NOT NULL DEFAULT (datetime('now')),
16+
"updatedDate" datetime NOT NULL DEFAULT (datetime('now')),
17+
"apiConfig" TEXT,
18+
"analytic" TEXT,
19+
"category" TEXT,
20+
"speechToText" TEXT,
21+
"type" VARCHAR(20) NOT NULL DEFAULT '${EnumChatflowType.CHATFLOW}',
22+
"workspaceId" TEXT,
23+
"followUpPrompts" TEXT,
24+
FOREIGN KEY ("workspaceId") REFERENCES "workspace"("id")
25+
);
26+
`)
27+
28+
await queryRunner.query(`
29+
INSERT INTO "temp_chat_flow" ("id", "name", "flowData", "deployed", "isPublic", "apikeyid", "chatbotConfig", "createdDate", "updatedDate", "apiConfig", "analytic", "category", "speechToText", "type", "workspaceId", "followUpPrompts")
30+
SELECT "id", "name", "flowData", "deployed", "isPublic", "apikeyid", "chatbotConfig", "createdDate", "updatedDate", "apiConfig", "analytic", "category", "speechToText",
31+
CASE WHEN "type" IS NULL OR "type" = '' THEN '${EnumChatflowType.CHATFLOW}' ELSE "type" END, "workspaceId", "followUpPrompts" FROM "chat_flow";
32+
`)
33+
34+
await queryRunner.query(`DROP TABLE "chat_flow";`)
35+
36+
await queryRunner.query(`ALTER TABLE "temp_chat_flow" RENAME TO "chat_flow";`)
37+
}
38+
39+
public async down(): Promise<void> {}
40+
}

packages/server/src/database/migrations/sqlite/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { AddSeqNoToDatasetRow1733752119696 } from './1733752119696-AddSeqNoToDat
3434
import { AddExecutionEntity1738090872625 } from './1738090872625-AddExecutionEntity'
3535
import { FixOpenSourceAssistantTable1743758056188 } from './1743758056188-FixOpenSourceAssistantTable'
3636
import { AddErrorToEvaluationRun1744964560174 } from './1744964560174-AddErrorToEvaluationRun'
37+
import { ModifyChatflowType1755066758601 } from './1755066758601-ModifyChatflowType'
3738

3839
import { AddAuthTables1720230151482 } from '../../../enterprise/database/migrations/sqlite/1720230151482-AddAuthTables'
3940
import { AddWorkspace1720230151484 } from '../../../enterprise/database/migrations/sqlite/1720230151484-AddWorkspace'
@@ -94,5 +95,6 @@ export const sqliteMigrations = [
9495
AddExecutionEntity1738090872625,
9596
FixOpenSourceAssistantTable1743758056188,
9697
AddErrorToEvaluationRun1744964560174,
97-
ExecutionLinkWorkspaceId1746862866554
98+
ExecutionLinkWorkspaceId1746862866554,
99+
ModifyChatflowType1755066758601
98100
]

packages/server/src/services/chatflows/index.ts

Lines changed: 72 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { In } from 'typeorm'
44
import { ChatflowType, IReactFlowObject } from '../../Interface'
55
import { FLOWISE_COUNTER_STATUS, FLOWISE_METRIC_COUNTERS } from '../../Interface.Metrics'
66
import { UsageCacheManager } from '../../UsageCacheManager'
7-
import { ChatFlow } from '../../database/entities/ChatFlow'
7+
import { ChatFlow, EnumChatflowType } from '../../database/entities/ChatFlow'
88
import { ChatMessage } from '../../database/entities/ChatMessage'
99
import { ChatMessageFeedback } from '../../database/entities/ChatMessageFeedback'
1010
import { UpsertHistory } from '../../database/entities/UpsertHistory'
@@ -20,6 +20,15 @@ import { utilGetUploadsConfig } from '../../utils/getUploadsConfig'
2020
import logger from '../../utils/logger'
2121
import { updateStorageUsage } from '../../utils/quotaUsage'
2222

23+
export const enum ChatflowErrorMessage {
24+
INVALID_CHATFLOW_TYPE = 'Invalid Chatflow Type'
25+
}
26+
27+
export function validateChatflowType(type: ChatflowType | undefined) {
28+
if (!Object.values(EnumChatflowType).includes(type as EnumChatflowType))
29+
throw new InternalFlowiseError(StatusCodes.BAD_REQUEST, ChatflowErrorMessage.INVALID_CHATFLOW_TYPE)
30+
}
31+
2332
// Check if chatflow valid for streaming
2433
const checkIfChatflowIsValidForStreaming = async (chatflowId: string): Promise<any> => {
2534
try {
@@ -254,57 +263,51 @@ const saveChatflow = async (
254263
subscriptionId: string,
255264
usageCacheManager: UsageCacheManager
256265
): Promise<any> => {
257-
try {
258-
const appServer = getRunningExpressApp()
259-
260-
let dbResponse: ChatFlow
261-
if (containsBase64File(newChatFlow)) {
262-
// we need a 2-step process, as we need to save the chatflow first and then update the file paths
263-
// this is because we need the chatflow id to create the file paths
264-
265-
// step 1 - save with empty flowData
266-
const incomingFlowData = newChatFlow.flowData
267-
newChatFlow.flowData = JSON.stringify({})
268-
const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow)
269-
const step1Results = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow)
270-
271-
// step 2 - convert base64 to file paths and update the chatflow
272-
step1Results.flowData = await updateFlowDataWithFilePaths(
273-
step1Results.id,
274-
incomingFlowData,
275-
orgId,
276-
workspaceId,
277-
subscriptionId,
278-
usageCacheManager
279-
)
280-
await _checkAndUpdateDocumentStoreUsage(step1Results, newChatFlow.workspaceId)
281-
dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(step1Results)
282-
} else {
283-
const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow)
284-
dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow)
285-
}
286-
await appServer.telemetry.sendTelemetry(
287-
'chatflow_created',
288-
{
289-
version: await getAppVersion(),
290-
chatflowId: dbResponse.id,
291-
flowGraph: getTelemetryFlowObj(JSON.parse(dbResponse.flowData)?.nodes, JSON.parse(dbResponse.flowData)?.edges)
292-
},
293-
orgId
294-
)
295-
296-
appServer.metricsProvider?.incrementCounter(
297-
dbResponse?.type === 'MULTIAGENT' ? FLOWISE_METRIC_COUNTERS.AGENTFLOW_CREATED : FLOWISE_METRIC_COUNTERS.CHATFLOW_CREATED,
298-
{ status: FLOWISE_COUNTER_STATUS.SUCCESS }
299-
)
300-
301-
return dbResponse
302-
} catch (error) {
303-
throw new InternalFlowiseError(
304-
StatusCodes.INTERNAL_SERVER_ERROR,
305-
`Error: chatflowsService.saveChatflow - ${getErrorMessage(error)}`
266+
validateChatflowType(newChatFlow.type)
267+
const appServer = getRunningExpressApp()
268+
269+
let dbResponse: ChatFlow
270+
if (containsBase64File(newChatFlow)) {
271+
// we need a 2-step process, as we need to save the chatflow first and then update the file paths
272+
// this is because we need the chatflow id to create the file paths
273+
274+
// step 1 - save with empty flowData
275+
const incomingFlowData = newChatFlow.flowData
276+
newChatFlow.flowData = JSON.stringify({})
277+
const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow)
278+
const step1Results = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow)
279+
280+
// step 2 - convert base64 to file paths and update the chatflow
281+
step1Results.flowData = await updateFlowDataWithFilePaths(
282+
step1Results.id,
283+
incomingFlowData,
284+
orgId,
285+
workspaceId,
286+
subscriptionId,
287+
usageCacheManager
306288
)
289+
await _checkAndUpdateDocumentStoreUsage(step1Results, newChatFlow.workspaceId)
290+
dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(step1Results)
291+
} else {
292+
const chatflow = appServer.AppDataSource.getRepository(ChatFlow).create(newChatFlow)
293+
dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(chatflow)
307294
}
295+
await appServer.telemetry.sendTelemetry(
296+
'chatflow_created',
297+
{
298+
version: await getAppVersion(),
299+
chatflowId: dbResponse.id,
300+
flowGraph: getTelemetryFlowObj(JSON.parse(dbResponse.flowData)?.nodes, JSON.parse(dbResponse.flowData)?.edges)
301+
},
302+
orgId
303+
)
304+
305+
appServer.metricsProvider?.incrementCounter(
306+
dbResponse?.type === 'MULTIAGENT' ? FLOWISE_METRIC_COUNTERS.AGENTFLOW_CREATED : FLOWISE_METRIC_COUNTERS.CHATFLOW_CREATED,
307+
{ status: FLOWISE_COUNTER_STATUS.SUCCESS }
308+
)
309+
310+
return dbResponse
308311
}
309312

310313
const updateChatflow = async (
@@ -314,29 +317,27 @@ const updateChatflow = async (
314317
workspaceId: string,
315318
subscriptionId: string
316319
): Promise<any> => {
317-
try {
318-
const appServer = getRunningExpressApp()
319-
if (updateChatFlow.flowData && containsBase64File(updateChatFlow)) {
320-
updateChatFlow.flowData = await updateFlowDataWithFilePaths(
321-
chatflow.id,
322-
updateChatFlow.flowData,
323-
orgId,
324-
workspaceId,
325-
subscriptionId,
326-
appServer.usageCacheManager
327-
)
328-
}
329-
const newDbChatflow = appServer.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow)
330-
await _checkAndUpdateDocumentStoreUsage(newDbChatflow, chatflow.workspaceId)
331-
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(newDbChatflow)
332-
333-
return dbResponse
334-
} catch (error) {
335-
throw new InternalFlowiseError(
336-
StatusCodes.INTERNAL_SERVER_ERROR,
337-
`Error: chatflowsService.updateChatflow - ${getErrorMessage(error)}`
320+
const appServer = getRunningExpressApp()
321+
if (updateChatFlow.flowData && containsBase64File(updateChatFlow)) {
322+
updateChatFlow.flowData = await updateFlowDataWithFilePaths(
323+
chatflow.id,
324+
updateChatFlow.flowData,
325+
orgId,
326+
workspaceId,
327+
subscriptionId,
328+
appServer.usageCacheManager
338329
)
339330
}
331+
if (updateChatFlow.type || updateChatFlow.type === '') {
332+
validateChatflowType(updateChatFlow.type)
333+
} else {
334+
updateChatFlow.type = chatflow.type
335+
}
336+
const newDbChatflow = appServer.AppDataSource.getRepository(ChatFlow).merge(chatflow, updateChatFlow)
337+
await _checkAndUpdateDocumentStoreUsage(newDbChatflow, chatflow.workspaceId)
338+
const dbResponse = await appServer.AppDataSource.getRepository(ChatFlow).save(newDbChatflow)
339+
340+
return dbResponse
340341
}
341342

342343
// Get specific chatflow chatbotConfig via id (PUBLIC endpoint, used to retrieve config for embedded chat)

0 commit comments

Comments
 (0)