File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -68,7 +68,7 @@ import {
6868 toKnowledgeTagFilterConditions ,
6969} from '@/lib/knowledge/tags/filter-resolution'
7070import { getDocumentTagDefinitions } from '@/lib/knowledge/tags/service'
71- import { validateTagValue } from '@/lib/knowledge/tags/utils'
71+ import { validateTagValue , validateTagValueLength } from '@/lib/knowledge/tags/utils'
7272import { StorageService } from '@/lib/uploads'
7373import { generateKnowledgeBaseFileKey } from '@/lib/uploads/contexts/knowledge-base/knowledge-base-file-manager'
7474import { recordKnowledgeBaseFileOwnership } from '@/lib/uploads/server/metadata'
@@ -252,7 +252,9 @@ async function resolveKnowledgeDocumentTagValueUpdates(
252252 `Tag "${ definition . displayName } " requires a value; use null to clear it`
253253 )
254254 }
255- const validationError = validateTagValue ( definition . displayName , value , definition . fieldType )
255+ const validationError =
256+ validateTagValueLength ( definition . displayName , value ) ??
257+ validateTagValue ( definition . displayName , value , definition . fieldType )
256258 if ( validationError ) {
257259 throw new OrchestrationError ( 'validation' , validationError )
258260 }
Original file line number Diff line number Diff line change @@ -450,6 +450,16 @@ describe('resolveTagMapping', () => {
450450 expect ( tags ?. tag2 ) . toBe ( 'Purchasing' )
451451 } )
452452
453+ it ( 'keeps a value exactly at the limit untouched' , ( ) => {
454+ const atLimit = 'z' . repeat ( 512 )
455+ const tags = resolveTagMapping (
456+ 'fixture' ,
457+ { label : atLimit } ,
458+ { tagSlotMapping : { label : 'tag1' } }
459+ )
460+ expect ( tags ?. tag1 ) . toBe ( atLimit )
461+ } )
462+
453463 it ( 'cuts by code point so a bounded value never ends in half a surrogate pair' , ( ) => {
454464 const tags = resolveTagMapping (
455465 'fixture' ,
Original file line number Diff line number Diff line change @@ -184,6 +184,7 @@ const INDEXED_TEXT_CUT_SUFFIX = '...'
184184 * never inside a surrogate pair. The result always passes the document APIs' own bound.
185185 */
186186function boundIndexedText ( value : string ) : string {
187+ if ( value . length <= MAX_DOCUMENT_INDEXED_TEXT_LENGTH ) return value
187188 return truncateAtCodePoint (
188189 value ,
189190 MAX_DOCUMENT_INDEXED_TEXT_LENGTH - INDEXED_TEXT_CUT_SUFFIX . length ,
Original file line number Diff line number Diff line change @@ -179,6 +179,7 @@ import {
179179 parseNumberValue ,
180180 uncompilableTagFilterError ,
181181 validateTagValue ,
182+ validateTagValueLength ,
182183} from '@/lib/knowledge/tags/utils'
183184import type { ProcessedDocumentTags } from '@/lib/knowledge/types'
184185import { embeddingVectorValues } from '@/lib/knowledge/vector-columns'
@@ -552,7 +553,9 @@ function resolveDocumentTags(
552553
553554 const rawValue = typeof tag . value === 'string' ? tag . value . trim ( ) : tag . value
554555 const actualFieldType = existingDef . fieldType || fieldType
555- const validationError = validateTagValue ( tagName , String ( rawValue ) , actualFieldType )
556+ const validationError =
557+ validateTagValueLength ( tagName , String ( rawValue ) ) ??
558+ validateTagValue ( tagName , String ( rawValue ) , actualFieldType )
556559 if ( validationError ) {
557560 typeErrors . push ( validationError )
558561 }
Original file line number Diff line number Diff line change 22 * @vitest -environment node
33 */
44import { describe , expect , it } from 'vitest'
5- import { coerceTagFilterValue , validateTagValue } from '@/lib/knowledge/tags/utils'
5+ import {
6+ coerceTagFilterValue ,
7+ validateTagValue ,
8+ validateTagValueLength ,
9+ } from '@/lib/knowledge/tags/utils'
610
711describe ( 'coerceTagFilterValue' , ( ) => {
812 it ( 'accepts exactly what validateTagValue accepts' , ( ) => {
@@ -68,3 +72,12 @@ describe('validateTagValue', () => {
6872 expect ( validateTagValue ( 'name' , 'anything' , 'json' ) ) . toBeNull ( )
6973 } )
7074} )
75+
76+ describe ( 'validateTagValueLength' , ( ) => {
77+ it ( 'accepts a value at the indexed-text limit and names the tag past it' , ( ) => {
78+ expect ( validateTagValueLength ( 'Labels' , 'a' . repeat ( 512 ) ) ) . toBeNull ( )
79+ expect ( validateTagValueLength ( 'Labels' , 'a' . repeat ( 513 ) ) ) . toBe (
80+ 'Tag "Labels" cannot exceed 512 characters'
81+ )
82+ } )
83+ } )
Original file line number Diff line number Diff line change 11import { OrchestrationError } from '@/lib/core/orchestration/types'
2+ import { MAX_DOCUMENT_INDEXED_TEXT_LENGTH } from '@/lib/knowledge/constants'
23
34const DATE_ONLY_PATTERN = / ^ \d { 4 } - \d { 2 } - \d { 2 } $ /
45
@@ -94,6 +95,17 @@ export function uncompilableTagFilterError(filter: {
9495 * Validate a tag value against its expected field type
9596 * Returns an error message if invalid, or null if valid
9697 */
98+ /**
99+ * Text tag values sit under an index whose rows Postgres caps in size; a value past
100+ * {@link MAX_DOCUMENT_INDEXED_TEXT_LENGTH} would fail the document write itself, so it is
101+ * refused with a message naming the tag instead.
102+ */
103+ export function validateTagValueLength ( tagName : string , value : string ) : string | null {
104+ return value . length > MAX_DOCUMENT_INDEXED_TEXT_LENGTH
105+ ? `Tag "${ tagName } " cannot exceed ${ MAX_DOCUMENT_INDEXED_TEXT_LENGTH } characters`
106+ : null
107+ }
108+
97109export function validateTagValue ( tagName : string , value : string , fieldType : string ) : string | null {
98110 if ( fieldType !== 'boolean' && fieldType !== 'number' && fieldType !== 'date' ) return null
99111
You can’t perform that action at this time.
0 commit comments