11import { createLogger } from '@sim/logger'
2- import type { StructuredFilter } from '@/lib/knowledge/types'
32import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format'
43import {
54 evaluateSubBlockCondition ,
65 type SubBlockCondition ,
76} from '@/lib/workflows/subblocks/visibility'
87import type { SubBlockConfig as BlockSubBlockConfig } from '@/blocks/types'
8+ import { isEmptyTagValue } from '@/tools/shared/tags'
99import type { ParameterVisibility , ToolConfig } from '@/tools/types'
1010import { getTool } from '@/tools/utils'
1111
@@ -23,194 +23,6 @@ export function isNonEmpty(value: unknown): boolean {
2323// Tag/Value Parsing Utilities
2424// ============================================================================
2525
26- /**
27- * Document tag entry format used in create_document tool
28- */
29- export interface DocumentTagEntry {
30- tagName : string
31- value : string
32- }
33-
34- /**
35- * Tag filter entry format used in search tool
36- */
37- export interface TagFilterEntry {
38- tagName : string
39- tagSlot ?: string
40- tagValue : string | number | boolean
41- fieldType ?: string
42- operator ?: string
43- valueTo ?: string | number
44- }
45-
46- /**
47- * Checks if a tag value is effectively empty (unfilled/default entry)
48- */
49- function isEmptyTagEntry ( entry : Record < string , unknown > ) : boolean {
50- if ( ! entry . tagName || ( typeof entry . tagName === 'string' && entry . tagName . trim ( ) === '' ) ) {
51- return true
52- }
53- return false
54- }
55-
56- /**
57- * Checks if a tag-based value is effectively empty (only contains default/unfilled entries).
58- * Works for both documentTags and tagFilters parameters in various formats.
59- *
60- * @param value - The tag value to check (can be JSON string, array, or object)
61- * @returns true if the value is empty or only contains unfilled entries
62- */
63- export function isEmptyTagValue ( value : unknown ) : boolean {
64- if ( ! value ) return true
65-
66- // Handle JSON string format
67- if ( typeof value === 'string' ) {
68- try {
69- const parsed = JSON . parse ( value )
70- if ( ! Array . isArray ( parsed ) ) return false
71- if ( parsed . length === 0 ) return true
72- return parsed . every ( ( entry : Record < string , unknown > ) => isEmptyTagEntry ( entry ) )
73- } catch {
74- return false
75- }
76- }
77-
78- // Handle array format directly
79- if ( Array . isArray ( value ) ) {
80- if ( value . length === 0 ) return true
81- return value . every ( ( entry : Record < string , unknown > ) => isEmptyTagEntry ( entry ) )
82- }
83-
84- // Handle object format (LLM format: { "Category": "foo", "Priority": 5 })
85- if ( typeof value === 'object' && value !== null ) {
86- const entries = Object . entries ( value )
87- if ( entries . length === 0 ) return true
88- return entries . every ( ( [ , val ] ) => val === undefined || val === null || val === '' )
89- }
90-
91- return false
92- }
93-
94- /**
95- * Filters valid document tags from an array, removing empty entries
96- */
97- function filterValidDocumentTags ( tags : unknown [ ] ) : DocumentTagEntry [ ] {
98- return tags
99- . filter ( ( entry ) : entry is Record < string , unknown > => {
100- if ( typeof entry !== 'object' || entry === null ) return false
101- const e = entry as Record < string , unknown >
102- if ( ! e . tagName || ( typeof e . tagName === 'string' && e . tagName . trim ( ) === '' ) ) return false
103- if ( e . value === undefined || e . value === null || e . value === '' ) return false
104- return true
105- } )
106- . map ( ( entry ) => ( {
107- tagName : String ( entry . tagName ) ,
108- value : String ( entry . value ) ,
109- } ) )
110- }
111-
112- /**
113- * Parses document tags from various formats into a normalized array format.
114- * Used by create_document tool to handle tags from both UI and LLM sources.
115- *
116- * @param value - Document tags in object, array, or JSON string format
117- * @returns Normalized array of document tag entries, or empty array if invalid
118- */
119- export function parseDocumentTags ( value : unknown ) : DocumentTagEntry [ ] {
120- if ( ! value ) return [ ]
121-
122- // Handle object format from LLM: { "Category": "foo", "Priority": 5 }
123- if ( typeof value === 'object' && ! Array . isArray ( value ) && value !== null ) {
124- return Object . entries ( value )
125- . filter ( ( [ tagName , tagValue ] ) => {
126- if ( ! tagName || tagName . trim ( ) === '' ) return false
127- if ( tagValue === undefined || tagValue === null || tagValue === '' ) return false
128- return true
129- } )
130- . map ( ( [ tagName , tagValue ] ) => ( {
131- tagName,
132- value : String ( tagValue ) ,
133- } ) )
134- }
135-
136- // Handle JSON string format from UI
137- if ( typeof value === 'string' ) {
138- try {
139- const parsed = JSON . parse ( value )
140- if ( Array . isArray ( parsed ) ) {
141- return filterValidDocumentTags ( parsed )
142- }
143- } catch {
144- // Invalid JSON, return empty
145- }
146- return [ ]
147- }
148-
149- // Handle array format directly
150- if ( Array . isArray ( value ) ) {
151- return filterValidDocumentTags ( value )
152- }
153-
154- return [ ]
155- }
156-
157- /**
158- * Parses tag filters from various formats into a normalized StructuredFilter array.
159- * Used by search tool to handle tag filters from both UI and LLM sources.
160- *
161- * @param value - Tag filters in array or JSON string format
162- * @returns Normalized array of structured filters, or empty array if invalid
163- */
164- export function parseTagFilters ( value : unknown ) : StructuredFilter [ ] {
165- if ( ! value ) return [ ]
166-
167- let tagFilters = value
168-
169- // Handle JSON string format
170- if ( typeof tagFilters === 'string' ) {
171- try {
172- tagFilters = JSON . parse ( tagFilters )
173- } catch {
174- return [ ]
175- }
176- }
177-
178- // Must be an array at this point
179- if ( ! Array . isArray ( tagFilters ) ) return [ ]
180-
181- return tagFilters
182- . filter ( ( filter ) : filter is Record < string , unknown > => {
183- if ( typeof filter !== 'object' || filter === null ) return false
184- const f = filter as Record < string , unknown >
185- if ( ! f . tagName || ( typeof f . tagName === 'string' && f . tagName . trim ( ) === '' ) ) return false
186- if ( f . fieldType === 'boolean' ) {
187- return f . tagValue !== undefined
188- }
189- if ( f . tagValue === undefined || f . tagValue === null ) return false
190- if ( typeof f . tagValue === 'string' && f . tagValue . trim ( ) . length === 0 ) return false
191- return true
192- } )
193- . map ( ( filter ) => ( {
194- tagName : filter . tagName as string ,
195- tagSlot : ( filter . tagSlot as string ) || '' ,
196- fieldType : ( filter . fieldType as string ) || 'text' ,
197- operator : ( filter . operator as string ) || 'eq' ,
198- value : filter . tagValue as string | number | boolean ,
199- valueTo : filter . valueTo as string | number | undefined ,
200- } ) )
201- }
202-
203- /**
204- * Converts parsed document tags to the format expected by the create document API.
205- * Returns the documentTagsData JSON string if there are valid tags.
206- */
207- export function formatDocumentTagsForAPI ( tags : DocumentTagEntry [ ] ) : { documentTagsData ?: string } {
208- if ( tags . length === 0 ) return { }
209- return {
210- documentTagsData : JSON . stringify ( tags ) ,
211- }
212- }
213-
21426export interface Option {
21527 label : string
21628 value : string
0 commit comments