Skip to content

Commit 20a3941

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
Fix canonical tag filter edge cases
1 parent 5e89280 commit 20a3941

5 files changed

Lines changed: 38 additions & 37 deletions

File tree

apps/sim/ee/workspace-forking/lib/remap/remap-references.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1570,7 +1570,8 @@ describe('canonical mode policy (fork/promote)', () => {
15701570
toolId: 'knowledge_search',
15711571
params: {
15721572
knowledgeBaseId: 'kb-src',
1573-
tagFilters: filterValue,
1573+
tagFilters: '',
1574+
manualTagFilters: filterValue,
15741575
},
15751576
},
15761577
{
@@ -1588,7 +1589,8 @@ describe('canonical mode policy (fork/promote)', () => {
15881589

15891590
expect(result.params).toEqual({
15901591
knowledgeBaseId: 'kb-dst',
1591-
tagFilters: expected,
1592+
tagFilters: '',
1593+
manualTagFilters: expected,
15921594
})
15931595
})
15941596

apps/sim/ee/workspace-forking/lib/remap/remap-references.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -457,23 +457,12 @@ export function remapToolBlockResources(
457457
)
458458
const toolBlockSubBlocks = (opts.blockConfigs?.[tool.type] ?? getBlock(tool.type))?.subBlocks
459459
const gates = createCanonicalModeGates(toolBlockSubBlocks, toolValues, scopedModes)
460-
const isActiveManualCanonicalKey = (paramKey: string): boolean => {
461-
const pairConfigs =
462-
toolBlockSubBlocks?.filter((config) => config.canonicalParamId === paramKey) ?? []
463-
const hasRawAdvancedValue = pairConfigs.some(
464-
(config) =>
465-
config.mode === 'advanced' && config.id !== paramKey && isNonEmptyValue(params[config.id])
466-
)
467-
return pairConfigs.length > 0 && gates.isAdvancedActiveGroup(paramKey) && !hasRawAdvancedValue
468-
}
469460

470461
// Clear DORMANT member keys first: a stale inactive value must not survive the copy (and must
471462
// never be recorded). Not a dependent-clear seed - the pair's ACTIVE member carries the live
472-
// value, and only ITS remap clears dependents. A key matching both a basic member id and the
473-
// pair's canonical parameter holds the live advanced value in nested tool storage.
463+
// value, and only ITS remap clears dependents.
474464
for (const paramKey of Object.keys(params)) {
475465
if (!gates.isDormantMember(paramKey)) continue
476-
if (isActiveManualCanonicalKey(paramKey)) continue
477466
const currentValue = params[paramKey]
478467
if (currentValue == null || currentValue === '') continue
479468
setParam(paramKey, '')

apps/sim/providers/utils.test.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,6 +1681,18 @@ describe('transformBlockTool knowledge-base multi-instance unique IDs', () => {
16811681
canonicalParamId: 'knowledgeBaseId',
16821682
mode: 'advanced',
16831683
},
1684+
{
1685+
id: 'tagFilters',
1686+
type: 'knowledge-tag-filters',
1687+
canonicalParamId: 'tagFilters',
1688+
mode: 'basic',
1689+
},
1690+
{
1691+
id: 'manualTagFilters',
1692+
type: 'knowledge-tag-filters',
1693+
canonicalParamId: 'tagFilters',
1694+
mode: 'advanced',
1695+
},
16841696
],
16851697
tools: {
16861698
access: ['knowledge_search', 'knowledge_upload_chunk'],
@@ -1729,4 +1741,19 @@ describe('transformBlockTool knowledge-base multi-instance unique IDs', () => {
17291741
const result = await transformKb({})
17301742
expect(result?.id).toBe('knowledge_search')
17311743
})
1744+
1745+
it('executes a nested tool with only its active advanced tag filters', async () => {
1746+
const basicFilters = '[{"tagName":"stale-basic","tagValue":"docs"}]'
1747+
const advancedFilters = '[{"tagId":"<start.tagId>","tagValue":"docs"}]'
1748+
const result = await transformKb(
1749+
{ tagFilters: basicFilters, manualTagFilters: advancedFilters },
1750+
{ '0:tagFilters': 'advanced' },
1751+
0
1752+
)
1753+
1754+
expect(result?.paramsTransform?.(result.params ?? {})).toMatchObject({
1755+
tagFilters: advancedFilters,
1756+
})
1757+
expect(result?.paramsTransform?.(result.params ?? {})).not.toHaveProperty('manualTagFilters')
1758+
})
17321759
})

apps/sim/tools/shared/tags.test.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,10 @@ describe('tag filter parsing', () => {
7878
expect(isEmptyTagValue([{ tagId: 'tag-definition-id', tagValue: '' }])).toBe(false)
7979
})
8080

81-
it('preserves a configured filter whose dynamic tag ID resolved empty for API validation', () => {
81+
it('ignores an unfinished filter whose identifier is still empty', () => {
8282
const filter = { tagName: '', tagId: '', tagValue: 'api', operator: 'eq' }
8383

84-
expect(isEmptyTagValue([filter])).toBe(false)
85-
expect(parseTagFilters([filter])).toEqual([
86-
{
87-
tagSlot: '',
88-
fieldType: 'text',
89-
operator: 'eq',
90-
value: 'api',
91-
valueTo: undefined,
92-
},
93-
])
84+
expect(isEmptyTagValue([filter])).toBe(true)
85+
expect(parseTagFilters([filter])).toEqual([])
9486
})
9587
})

apps/sim/tools/shared/tags.ts

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,7 @@ interface TagFilterEntry {
2727
function isEmptyTagEntry(entry: Record<string, unknown>): boolean {
2828
const hasTagName = typeof entry.tagName === 'string' && entry.tagName.trim().length > 0
2929
const hasTagId = typeof entry.tagId === 'string' && entry.tagId.trim().length > 0
30-
if (hasTagName || hasTagId) return false
31-
32-
if ('tagValue' in entry) {
33-
if (entry.tagValue === undefined || entry.tagValue === null) return true
34-
return typeof entry.tagValue === 'string' ? entry.tagValue.trim().length === 0 : false
35-
}
36-
37-
return true
30+
return !hasTagName && !hasTagId
3831
}
3932

4033
/**
@@ -149,13 +142,11 @@ export function parseTagFilters(value: unknown): StructuredFilter[] {
149142
const hasTagName = typeof f.tagName === 'string' && f.tagName.trim().length > 0
150143
const hasTagId = typeof f.tagId === 'string' && f.tagId.trim().length > 0
151144
if (f.fieldType === 'boolean') {
152-
return (
153-
f.tagValue !== undefined && (hasTagName || hasTagId || 'tagName' in f || 'tagId' in f)
154-
)
145+
return f.tagValue !== undefined && (hasTagName || hasTagId)
155146
}
156147
if (f.tagValue === undefined || f.tagValue === null) return false
157148
if (typeof f.tagValue === 'string' && f.tagValue.trim().length === 0) return false
158-
return hasTagName || hasTagId || 'tagName' in f || 'tagId' in f
149+
return hasTagName || hasTagId
159150
})
160151
.map((filter) => {
161152
const tagId =

0 commit comments

Comments
 (0)