Skip to content

Commit 969a478

Browse files
committed
consolidate
1 parent 6b42434 commit 969a478

4 files changed

Lines changed: 195 additions & 238 deletions

File tree

apps/sim/tools/knowledge/create_document.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { KnowledgeCreateDocumentResponse } from '@/tools/knowledge/types'
2+
import { formatDocumentTagsForAPI, parseDocumentTags } from '@/tools/params'
23
import { enrichKBTagsSchema } from '@/tools/schema-enrichers'
3-
import { formatDocumentTagsForAPI, parseDocumentTags } from '@/tools/tag-utils'
44
import type { ToolConfig } from '@/tools/types'
55

66
export const knowledgeCreateDocumentTool: ToolConfig<any, KnowledgeCreateDocumentResponse> = {

apps/sim/tools/knowledge/search.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { KnowledgeSearchResponse } from '@/tools/knowledge/types'
2+
import { parseTagFilters } from '@/tools/params'
23
import { enrichKBTagFiltersSchema } from '@/tools/schema-enrichers'
3-
import { parseTagFilters } from '@/tools/tag-utils'
44
import type { ToolConfig } from '@/tools/types'
55

66
export const knowledgeSearchTool: ToolConfig<any, KnowledgeSearchResponse> = {

apps/sim/tools/params.ts

Lines changed: 193 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
import { createLogger } from '@sim/logger'
2+
import type { StructuredFilter } from '@/lib/knowledge/types'
23
import { extractInputFieldsFromBlocks } from '@/lib/workflows/input-format'
34
import {
45
evaluateSubBlockCondition,
56
type SubBlockCondition,
67
} from '@/lib/workflows/subblocks/visibility'
78
import type { SubBlockConfig as BlockSubBlockConfig } from '@/blocks/types'
8-
import { isEmptyTagValue } from '@/tools/tag-utils'
99
import type { ParameterVisibility, ToolConfig } from '@/tools/types'
1010
import { getTool } from '@/tools/utils'
1111

12-
// Re-export for backwards compatibility
13-
export { isEmptyTagValue } from '@/tools/tag-utils'
14-
1512
const logger = createLogger('ToolsParams')
1613
type ToolParamDefinition = ToolConfig['params'][string]
1714

@@ -22,6 +19,198 @@ export function isNonEmpty(value: unknown): boolean {
2219
return value !== undefined && value !== null && value !== ''
2320
}
2421

22+
// ============================================================================
23+
// Tag/Value Parsing Utilities
24+
// ============================================================================
25+
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+
25214
export interface Option {
26215
label: string
27216
value: string

0 commit comments

Comments
 (0)