Skip to content

Commit 7457184

Browse files
fix(knowledge): surface KB description validation errors and raise limit to 10k (#5347)
1 parent 8d6cec1 commit 7457184

5 files changed

Lines changed: 64 additions & 17 deletions

File tree

apps/sim/app/workspace/[workspaceId]/knowledge/components/create-base-modal/create-base-modal.tsx

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@ import {
1717
type ComboboxOption,
1818
cn,
1919
Loader,
20+
toast,
2021
} from '@sim/emcn'
2122
import { createLogger } from '@sim/logger'
2223
import { getErrorMessage } from '@sim/utils/errors'
2324
import { X } from 'lucide-react'
2425
import { useParams } from 'next/navigation'
25-
import { useForm } from 'react-hook-form'
26+
import { type FieldErrors, useForm } from 'react-hook-form'
2627
import { z } from 'zod'
2728
import type { StrategyOptions } from '@/lib/chunkers/types'
29+
import { KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH } from '@/lib/knowledge/constants'
2830
import { formatFileSize, validateKnowledgeBaseFile } from '@/lib/uploads/utils/file-utils'
2931
import { ACCEPT_ATTRIBUTE } from '@/lib/uploads/utils/validation'
3032
import { useKnowledgeUpload } from '@/app/workspace/[workspaceId]/knowledge/hooks/use-knowledge-upload'
@@ -58,7 +60,13 @@ const FormSchema = z
5860
.min(1, 'Name is required')
5961
.max(100, 'Name must be less than 100 characters')
6062
.refine((value) => value.trim().length > 0, 'Name cannot be empty'),
61-
description: z.string().max(500, 'Description must be less than 500 characters').optional(),
63+
description: z
64+
.string()
65+
.max(
66+
KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH,
67+
`Description must be ${KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH} characters or less`
68+
)
69+
.optional(),
6270
minChunkSize: z
6371
.number()
6472
.min(1, 'Min chunk size must be at least 1 character')
@@ -223,6 +231,15 @@ export const CreateBaseModal = memo(function CreateBaseModal({
223231
const isSubmitting =
224232
createKnowledgeBaseMutation.isPending || deleteKnowledgeBaseMutation.isPending || isUploading
225233

234+
const onInvalid = (formErrors: FieldErrors<FormInputValues>) => {
235+
const firstMessage = Object.values(formErrors).find(
236+
(fieldError) => typeof fieldError?.message === 'string'
237+
)?.message
238+
toast.error(
239+
typeof firstMessage === 'string' ? firstMessage : 'Please fix the highlighted fields'
240+
)
241+
}
242+
226243
const onSubmit = async (data: FormValues) => {
227244
setSubmitStatus(null)
228245

@@ -292,7 +309,7 @@ export const CreateBaseModal = memo(function CreateBaseModal({
292309
<ChipModal open={open} onOpenChange={handleClose} srTitle='Create Knowledge Base' size='lg'>
293310
<ChipModalHeader onClose={() => handleClose(false)}>Create Knowledge Base</ChipModalHeader>
294311

295-
<form onSubmit={handleSubmit(onSubmit)} className='flex min-h-0 flex-1 flex-col'>
312+
<form onSubmit={handleSubmit(onSubmit, onInvalid)} className='flex min-h-0 flex-1 flex-col'>
296313
<button type='submit' hidden disabled={isSubmitting || !nameValue?.trim()} />
297314
<ChipModalBody>
298315
<input
@@ -317,7 +334,7 @@ export const CreateBaseModal = memo(function CreateBaseModal({
317334
/>
318335
</ChipModalField>
319336

320-
<ChipModalField type='custom' title='Description'>
337+
<ChipModalField type='custom' title='Description' error={errors.description?.message}>
321338
<ChipTextarea
322339
placeholder='Describe this knowledge base (optional)'
323340
rows={4}
@@ -520,7 +537,7 @@ export const CreateBaseModal = memo(function CreateBaseModal({
520537
: 'Creating...'
521538
: 'Creating...'
522539
: 'Create',
523-
onClick: handleSubmit(onSubmit),
540+
onClick: handleSubmit(onSubmit, onInvalid),
524541
disabled: isSubmitting || !nameValue?.trim(),
525542
}}
526543
/>

apps/sim/app/workspace/[workspaceId]/knowledge/components/edit-knowledge-base-modal/edit-knowledge-base-modal.tsx

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ import {
88
ChipModalField,
99
ChipModalFooter,
1010
ChipModalHeader,
11+
toast,
1112
} from '@sim/emcn'
1213
import { createLogger } from '@sim/logger'
1314
import { getErrorMessage } from '@sim/utils/errors'
15+
import { KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH } from '@/lib/knowledge/constants'
1416
import type { ChunkingConfig } from '@/lib/knowledge/types'
1517

1618
const logger = createLogger('EditKnowledgeBaseModal')
@@ -60,31 +62,36 @@ export const EditKnowledgeBaseModal = memo(function EditKnowledgeBaseModal({
6062
}
6163
}
6264

63-
const validate = (): boolean => {
64-
let valid = true
65+
const validate = (): string | null => {
66+
let firstError: string | null = null
6567

6668
if (!name.trim()) {
6769
setNameError('Name is required')
68-
valid = false
70+
firstError ??= 'Name is required'
6971
} else if (name.trim().length > 100) {
7072
setNameError('Name must be less than 100 characters')
71-
valid = false
73+
firstError ??= 'Name must be less than 100 characters'
7274
} else {
7375
setNameError(null)
7476
}
7577

76-
if (description.length > 500) {
77-
setDescriptionError('Description must be less than 500 characters')
78-
valid = false
78+
if (description.length > KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH) {
79+
const message = `Description must be ${KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH} characters or less`
80+
setDescriptionError(message)
81+
firstError ??= message
7982
} else {
8083
setDescriptionError(null)
8184
}
8285

83-
return valid
86+
return firstError
8487
}
8588

8689
const handleSubmit = async () => {
87-
if (!validate()) return
90+
const validationError = validate()
91+
if (validationError) {
92+
toast.error(validationError)
93+
return
94+
}
8895

8996
setIsSubmitting(true)
9097
setError(null)

apps/sim/lib/api/contracts/knowledge/base.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
} from '@/lib/api/contracts/knowledge/shared'
88
import { defineRouteContract } from '@/lib/api/contracts/types'
99
import type { StrategyOptions } from '@/lib/chunkers/types'
10+
import { KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH } from '@/lib/knowledge/constants'
1011

1112
export const knowledgeScopeSchema = z.enum(['active', 'archived', 'all'])
1213
export type KnowledgeScope = z.output<typeof knowledgeScopeSchema>
@@ -51,7 +52,13 @@ export const chunkingConfigSchema = z
5152

5253
export const createKnowledgeBaseBodySchema = z.object({
5354
name: z.string().min(1, 'Name is required'),
54-
description: z.string().optional(),
55+
description: z
56+
.string()
57+
.max(
58+
KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH,
59+
`Description must be ${KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH} characters or less`
60+
)
61+
.optional(),
5562
workspaceId: z.string().min(1, 'Workspace ID is required'),
5663
embeddingModel: z.literal('text-embedding-3-small').default('text-embedding-3-small'),
5764
embeddingDimension: z.literal(1536).default(1536),

apps/sim/lib/api/contracts/v1/knowledge/index.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
successResponseSchema,
66
} from '@/lib/api/contracts/knowledge/shared'
77
import { defineRouteContract } from '@/lib/api/contracts/types'
8+
import { KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH } from '@/lib/knowledge/constants'
89

910
/**
1011
* Public API v1 schemas (`/api/v1/knowledge/**`)
@@ -36,7 +37,13 @@ export const v1ListKnowledgeBasesQuerySchema = z.object({
3637
export const v1CreateKnowledgeBaseBodySchema = z.object({
3738
workspaceId: z.string().min(1, 'Workspace ID is required'),
3839
name: z.string().min(1, 'Name is required').max(255, 'Name must be 255 characters or less'),
39-
description: z.string().max(1000, 'Description must be 1000 characters or less').optional(),
40+
description: z
41+
.string()
42+
.max(
43+
KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH,
44+
`Description must be ${KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH} characters or less`
45+
)
46+
.optional(),
4047
chunkingConfig: v1ChunkingConfigSchema.optional().default({
4148
maxSize: 1024,
4249
minSize: 100,
@@ -54,7 +61,13 @@ export const v1UpdateKnowledgeBaseBodySchema = z
5461
.object({
5562
workspaceId: z.string().min(1, 'Workspace ID is required'),
5663
name: z.string().min(1).max(255, 'Name must be 255 characters or less').optional(),
57-
description: z.string().max(1000, 'Description must be 1000 characters or less').optional(),
64+
description: z
65+
.string()
66+
.max(
67+
KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH,
68+
`Description must be ${KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH} characters or less`
69+
)
70+
.optional(),
5871
chunkingConfig: z
5972
.object({
6073
maxSize: z.number().min(100).max(4000),

apps/sim/lib/knowledge/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
/** Max character length for a knowledge base description, enforced at every layer (UI, internal API, v1 API). */
2+
export const KNOWLEDGE_BASE_DESCRIPTION_MAX_LENGTH = 10_000
3+
14
export const TAG_SLOT_CONFIG = {
25
text: {
36
slots: ['tag1', 'tag2', 'tag3', 'tag4', 'tag5', 'tag6', 'tag7'] as const,

0 commit comments

Comments
 (0)