Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 103 additions & 56 deletions apps/docs/components/icons.tsx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/docs/content/docs/en/tools/onepassword.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BlockInfoCard } from "@/components/ui/block-info-card"

<BlockInfoCard
type="onepassword"
color="#145FE4"
color="#FFFFFF"
/>

{/* MANUAL-CONTENT-START:intro */}
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/blocks/blocks/onepassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const OnePasswordBlock: BlockConfig = {
docsLink: 'https://docs.sim.ai/tools/onepassword',
category: 'tools',
integrationType: IntegrationType.Security,
bgColor: '#145FE4',
bgColor: '#FFFFFF',
icon: OnePasswordIcon,
authMode: AuthMode.ApiKey,

Expand Down
159 changes: 103 additions & 56 deletions apps/sim/components/icons.tsx

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion apps/sim/lib/integrations/integrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"name": "1Password",
"description": "Manage secrets and items in 1Password vaults",
"longDescription": "Access and manage secrets stored in 1Password vaults using the Connect API or Service Account SDK. List vaults, retrieve items with their fields and secrets, create new items, update existing ones, delete items, and resolve secret references.",
"bgColor": "#145FE4",
"bgColor": "#FFFFFF",
"iconName": "OnePasswordIcon",
"docsUrl": "https://docs.sim.ai/tools/onepassword",
"operations": [
Expand Down
128 changes: 128 additions & 0 deletions apps/sim/providers/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
supportsThinking,
supportsToolUsageControl,
supportsVerbosity,
transformBlockTool,
updateOllamaProviderModels,
} from '@/providers/utils'

Expand Down Expand Up @@ -1514,3 +1515,130 @@ describe('Provider/Model Blacklist', () => {
})
})
})

describe('transformBlockTool multi-instance unique IDs', () => {
const tableBlockDef = {
type: 'table',
inputs: {},
subBlocks: [
{ id: 'operation', type: 'dropdown' },
{ id: 'tableSelector', type: 'table-selector', canonicalParamId: 'tableId', mode: 'basic' },
{
id: 'manualTableId',
type: 'short-input',
canonicalParamId: 'tableId',
mode: 'advanced',
},
],
tools: {
access: ['table_query_rows', 'table_insert_row'],
config: { tool: () => 'table_query_rows' },
},
}

const getAllBlocks = () => [tableBlockDef]
const getTool = (id: string) => ({
id,
name: 'Query Rows',
description: 'Query table rows',
params: {},
})

const transformTable = (
params: Record<string, unknown>,
canonicalModes?: Record<string, 'basic' | 'advanced'>
) =>
transformBlockTool(
{ type: 'table', operation: 'query_rows', params },
{ selectedOperation: 'query_rows', getAllBlocks, getTool, canonicalModes }
)

it('appends the table id when stored under the basic selector subblock key', async () => {
const result = await transformTable({ tableSelector: 'tbl_abc' })
expect(result?.id).toBe('table_query_rows_tbl_abc')
})

it('appends the table id resolved from the advanced manual input', async () => {
const result = await transformTable(
{ manualTableId: 'tbl_xyz' },
{ 'table:tableId': 'advanced' }
)
expect(result?.id).toBe('table_query_rows_tbl_xyz')
})

it('appends the canonical table id when already present in params', async () => {
const result = await transformTable({ tableId: 'tbl_direct' })
expect(result?.id).toBe('table_query_rows_tbl_direct')
})

it('falls back to the base tool id when no table is selected', async () => {
const result = await transformTable({})
expect(result?.id).toBe('table_query_rows')
})
})

describe('transformBlockTool knowledge-base multi-instance unique IDs', () => {
const knowledgeBlockDef = {
type: 'knowledge',
inputs: {},
subBlocks: [
{ id: 'operation', type: 'dropdown' },
{
id: 'knowledgeBaseSelector',
type: 'knowledge-base-selector',
canonicalParamId: 'knowledgeBaseId',
mode: 'basic',
},
{
id: 'manualKnowledgeBaseId',
type: 'short-input',
canonicalParamId: 'knowledgeBaseId',
mode: 'advanced',
},
],
tools: {
access: ['knowledge_search', 'knowledge_upload_chunk'],
config: { tool: () => 'knowledge_search' },
},
}

const getAllBlocks = () => [knowledgeBlockDef]
const getTool = (id: string) => ({
id,
name: 'Search',
description: 'Search the knowledge base',
params: {},
})

const transformKb = (
params: Record<string, unknown>,
canonicalModes?: Record<string, 'basic' | 'advanced'>
) =>
transformBlockTool(
{ type: 'knowledge', operation: 'search', params },
{ selectedOperation: 'search', getAllBlocks, getTool, canonicalModes }
)

it('appends the knowledge base id when stored under the basic selector subblock key', async () => {
const result = await transformKb({ knowledgeBaseSelector: 'kb_abc' })
expect(result?.id).toBe('knowledge_search_kb_abc')
})

it('appends the knowledge base id resolved from the advanced manual input', async () => {
const result = await transformKb(
{ manualKnowledgeBaseId: 'kb_xyz' },
{ 'knowledge:knowledgeBaseId': 'advanced' }
)
expect(result?.id).toBe('knowledge_search_kb_xyz')
})

it('appends the canonical knowledge base id when already present in params', async () => {
const result = await transformKb({ knowledgeBaseId: 'kb_direct' })
expect(result?.id).toBe('knowledge_search_kb_direct')
})

it('falls back to the base tool id when no knowledge base is selected', async () => {
const result = await transformKb({})
expect(result?.id).toBe('knowledge_search')
})
})
62 changes: 51 additions & 11 deletions apps/sim/providers/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,39 @@ export function extractAndParseJSON(content: string): any {
}
}

/**
* Resolves canonical pair ids (e.g. `tableId`, `knowledgeBaseId`) from a tool's
* raw params, filling them in from their basic/advanced selector subblock source
* values when the canonical key isn't already present.
*
* Selector subblocks persist their value under the subblock id (e.g.
* `tableSelector`), not the canonical id, so any lookup that keys off the
* canonical id — like the unique-tool-id suffix below — must resolve it first.
* Mode selection mirrors {@link transformBlockTool}'s execution-time
* `paramsTransform` so the resolved id matches the params the tool actually runs
* with.
*
* @returns The params with canonical resource ids resolved (non-destructive)
*/
function resolveCanonicalResourceParams(
params: Record<string, any>,
canonicalGroups: CanonicalGroup[],
blockType: string,
canonicalModes?: Record<string, 'basic' | 'advanced'>
): Record<string, any> {
if (canonicalGroups.length === 0) return params
const resolved = { ...params }
for (const group of canonicalGroups) {
const existing = resolved[group.canonicalId]
if (existing !== undefined && existing !== null && existing !== '') continue
const { basicValue, advancedValue } = getCanonicalValues(group, params)
const pairMode = canonicalModes?.[`${blockType}:${group.canonicalId}`] ?? 'basic'
const chosen = pairMode === 'advanced' ? advancedValue : basicValue
if (chosen !== undefined) resolved[group.canonicalId] = chosen
}
return resolved
}

/**
* Transforms a block tool into a provider tool config with operation selection
*
Expand Down Expand Up @@ -549,14 +582,25 @@ export async function transformBlockTool(
userProvidedParams
)

const canonicalGroups: CanonicalGroup[] = blockDef?.subBlocks
? Object.values(buildCanonicalIndex(blockDef.subBlocks).groupsById).filter(isCanonicalPair)
: []

const resolvedResourceParams = resolveCanonicalResourceParams(
userProvidedParams,
canonicalGroups,
block.type,
canonicalModes
)

let uniqueToolId = toolConfig.id
let toolName = toolConfig.name
let toolDescription = enrichedDescription || toolConfig.description

if (toolId === 'workflow_executor' && userProvidedParams.workflowId) {
uniqueToolId = `${toolConfig.id}_${userProvidedParams.workflowId}`
if (toolId === 'workflow_executor' && resolvedResourceParams.workflowId) {
uniqueToolId = `${toolConfig.id}_${resolvedResourceParams.workflowId}`

const workflowMetadata = await fetchWorkflowMetadata(userProvidedParams.workflowId)
const workflowMetadata = await fetchWorkflowMetadata(resolvedResourceParams.workflowId)
if (workflowMetadata) {
toolName = workflowMetadata.name || toolConfig.name
if (
Expand All @@ -566,21 +610,17 @@ export async function transformBlockTool(
toolDescription = workflowMetadata.description
}
}
} else if (toolId.startsWith('knowledge_') && userProvidedParams.knowledgeBaseId) {
uniqueToolId = `${toolConfig.id}_${userProvidedParams.knowledgeBaseId}`
} else if (toolId.startsWith('table_') && userProvidedParams.tableId) {
uniqueToolId = `${toolConfig.id}_${userProvidedParams.tableId}`
} else if (toolId.startsWith('knowledge_') && resolvedResourceParams.knowledgeBaseId) {
uniqueToolId = `${toolConfig.id}_${resolvedResourceParams.knowledgeBaseId}`
} else if (toolId.startsWith('table_') && resolvedResourceParams.tableId) {
uniqueToolId = `${toolConfig.id}_${resolvedResourceParams.tableId}`
}

const blockParamsFn = blockDef?.tools?.config?.params as
| ((p: Record<string, any>) => Record<string, any>)
| undefined
const blockInputDefs = blockDef?.inputs as Record<string, any> | undefined

const canonicalGroups: CanonicalGroup[] = blockDef?.subBlocks
? Object.values(buildCanonicalIndex(blockDef.subBlocks).groupsById).filter(isCanonicalPair)
: []

const needsTransform = blockParamsFn || blockInputDefs || canonicalGroups.length > 0
const paramsTransform = needsTransform
? (params: Record<string, any>): Record<string, any> => {
Expand Down
Loading