mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
feat(block): Add data enrichment block (#4774)
* feat(enrichment): workflow Enrichment block + /api/tools/enrichment/run Add a generic Enrichment workflow block that runs a code-defined enrichment (Work Email, Phone Number, Company Domain, Company Info, …) and returns its outputs — usable in workflows, not just tables. - New internal endpoint POST /api/tools/enrichment/run (checkInternalAuth + contract) runs the same runEnrichment provider cascade; injects the workspace's hosted/BYOK key via executeTool. - New tool enrichment_run posts to it and surfaces hosted-key cost on the output so the workflow logging session bills it. - New block blocks/blocks/enrichment.ts generated from the enrichment registry: operation dropdown = enrichments, per-enrichment conditional inputs, union of conditional outputs. New registry entries appear automatically. - EnrichmentRunContext.tableId/rowId made optional (workflow path has no row). - Register tool + block; bump api-validation route baseline; add EnrichmentIcon and generated docs page. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: re-trigger CI * refactor(enrichment): share mapFieldType helper between block and tool * fix(enrichment): reserved output keys (matched/provider) win over enrichment outputs --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
f2c17d8072
commit
7f24ae12fd
@@ -0,0 +1,48 @@
|
||||
---
|
||||
title: Enrichment
|
||||
description: Enrich data with a Sim enrichment
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="enrichment"
|
||||
color="#9333EA"
|
||||
/>
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Run a Sim enrichment to look up data — work email, phone number, company domain, company info, and more — from the fields you map in. Uses the same provider cascade as table enrichments.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `enrichment_run`
|
||||
|
||||
Run a Sim enrichment (e.g. Work Email, Phone Number) and return its outputs
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `enrichmentId` | string | Yes | Registry enrichment id \(e.g. "work-email"\) |
|
||||
| `inputs` | json | Yes | Map of the enrichment's input ids to values |
|
||||
|
||||
#### Output
|
||||
|
||||
The exact fields depend on which enrichment ran. `matched` and `provider` are always present.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `matched` | boolean | Whether the enrichment found a result |
|
||||
| `provider` | string | Provider whose result was returned (e.g. "Hunter", "People Data Labs"); `null` on no match |
|
||||
| `email` | string | Work email address (Work Email enrichment) |
|
||||
| `phone` | string | Phone number (Phone Number enrichment) |
|
||||
| `domain` | string | Website domain (Company Domain enrichment) |
|
||||
| `industry` | string | Industry (Company Info enrichment) |
|
||||
| `employeeCount` | number | Employee count (Company Info enrichment) |
|
||||
| `foundedYear` | number | Founded year (Company Info enrichment) |
|
||||
| `description` | string | Company description (Company Info enrichment) |
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
"elevenlabs",
|
||||
"emailbison",
|
||||
"enrich",
|
||||
"enrichment",
|
||||
"evernote",
|
||||
"exa",
|
||||
"extend",
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { NextRequest } from 'next/server'
|
||||
import { NextResponse } from 'next/server'
|
||||
import { runEnrichmentContract } from '@/lib/api/contracts/tools/enrichment'
|
||||
import { getValidationErrorMessage, parseRequest } from '@/lib/api/server'
|
||||
import { checkInternalAuth } from '@/lib/auth/hybrid'
|
||||
import { withRouteHandler } from '@/lib/core/utils/with-route-handler'
|
||||
import { getEnrichment } from '@/enrichments/registry'
|
||||
import { runEnrichment } from '@/enrichments/run'
|
||||
|
||||
const logger = createLogger('EnrichmentRunAPI')
|
||||
|
||||
/**
|
||||
* POST /api/tools/enrichment/run
|
||||
*
|
||||
* Runs a registry enrichment's provider cascade and returns its outputs. Backs
|
||||
* the Enrichment workflow block; called server-to-server by the executor, so it
|
||||
* authenticates with the internal token. The cascade injects the workspace's
|
||||
* BYOK / hosted key via `executeTool` using `workspaceId`.
|
||||
*/
|
||||
export const POST = withRouteHandler(async (request: NextRequest) => {
|
||||
const authResult = await checkInternalAuth(request, { requireWorkflowId: false })
|
||||
if (!authResult.success) {
|
||||
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
|
||||
}
|
||||
|
||||
const parsed = await parseRequest(
|
||||
runEnrichmentContract,
|
||||
request,
|
||||
{},
|
||||
{
|
||||
validationErrorResponse: (error) =>
|
||||
NextResponse.json(
|
||||
{ error: getValidationErrorMessage(error, 'Invalid request') },
|
||||
{
|
||||
status: 400,
|
||||
}
|
||||
),
|
||||
}
|
||||
)
|
||||
if (!parsed.success) return parsed.response
|
||||
|
||||
const { enrichmentId, inputs, workspaceId } = parsed.data.body
|
||||
const enrichment = getEnrichment(enrichmentId)
|
||||
if (!enrichment) {
|
||||
return NextResponse.json({ error: `Unknown enrichment "${enrichmentId}"` }, { status: 400 })
|
||||
}
|
||||
|
||||
const { result, cost, error, provider } = await runEnrichment(enrichment, inputs, {
|
||||
workspaceId,
|
||||
signal: request.signal,
|
||||
})
|
||||
|
||||
logger.info('Enrichment block run', {
|
||||
enrichmentId,
|
||||
matched: Object.keys(result).length > 0,
|
||||
provider,
|
||||
})
|
||||
return NextResponse.json({
|
||||
matched: Object.keys(result).length > 0,
|
||||
result,
|
||||
cost,
|
||||
error,
|
||||
provider,
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,117 @@
|
||||
import { EnrichmentIcon } from '@/components/icons'
|
||||
import type { BlockConfig, OutputFieldDefinition, ParamType } from '@/blocks/types'
|
||||
import { IntegrationType } from '@/blocks/types'
|
||||
import { ALL_ENRICHMENTS, getEnrichment } from '@/enrichments'
|
||||
import { mapFieldType } from '@/enrichments/providers'
|
||||
import type { EnrichmentOutputField } from '@/enrichments/types'
|
||||
import type { EnrichmentRunResponse } from '@/tools/enrichment/types'
|
||||
|
||||
/** Stable subBlock id for an enrichment input (unique across enrichments). */
|
||||
const inputFieldId = (enrichmentId: string, inputId: string) => `${enrichmentId}__${inputId}`
|
||||
|
||||
// One input field per (enrichment, input), shown only for its enrichment.
|
||||
const inputSubBlocks = ALL_ENRICHMENTS.flatMap((enrichment) =>
|
||||
enrichment.inputs.map((input) => ({
|
||||
id: inputFieldId(enrichment.id, input.id),
|
||||
title: input.name,
|
||||
type: 'short-input' as const,
|
||||
placeholder: input.description ?? `Enter ${input.name.toLowerCase()}`,
|
||||
condition: { field: 'operation', value: enrichment.id },
|
||||
required: input.required ? ({ field: 'operation', value: enrichment.id } as const) : undefined,
|
||||
}))
|
||||
)
|
||||
|
||||
// Block input schema: the operation plus every per-enrichment input field.
|
||||
const blockInputs: Record<string, { type: ParamType; description: string }> = {
|
||||
operation: { type: 'string', description: 'Enrichment to run' },
|
||||
}
|
||||
for (const enrichment of ALL_ENRICHMENTS) {
|
||||
for (const input of enrichment.inputs) {
|
||||
blockInputs[inputFieldId(enrichment.id, input.id)] = {
|
||||
type: mapFieldType(input.type),
|
||||
description: `${input.name} (for ${enrichment.name})`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Union of all enrichment outputs, each shown only for the enrichment(s) that
|
||||
// produce it.
|
||||
const outputProducers = new Map<string, { field: EnrichmentOutputField; operations: string[] }>()
|
||||
for (const enrichment of ALL_ENRICHMENTS) {
|
||||
for (const output of enrichment.outputs) {
|
||||
const entry = outputProducers.get(output.id) ?? { field: output, operations: [] }
|
||||
entry.operations.push(enrichment.id)
|
||||
outputProducers.set(output.id, entry)
|
||||
}
|
||||
}
|
||||
// Seed the enrichment outputs first so the reserved `matched` / `provider`
|
||||
// keys (assigned below) always win if a future enrichment ever declares an
|
||||
// output id that collides with them.
|
||||
const blockOutputs: Record<string, OutputFieldDefinition> = {}
|
||||
for (const [id, { field, operations }] of outputProducers) {
|
||||
blockOutputs[id] = {
|
||||
type: mapFieldType(field.type),
|
||||
description: field.name,
|
||||
condition: { field: 'operation', value: operations },
|
||||
}
|
||||
}
|
||||
blockOutputs.matched = {
|
||||
type: 'boolean',
|
||||
description: 'Whether the enrichment found a result',
|
||||
}
|
||||
blockOutputs.provider = {
|
||||
type: 'string',
|
||||
description: 'Provider whose result was returned (e.g. "Hunter", "People Data Labs")',
|
||||
}
|
||||
|
||||
/**
|
||||
* Enrichment block — runs a code-defined Sim enrichment (Work Email, Phone
|
||||
* Number, Company Domain, Company Info, …) and returns its outputs. Generated
|
||||
* from the enrichment registry, so new enrichments appear automatically. Runs
|
||||
* on the workspace's hosted / BYOK key (injected server-side); no credential.
|
||||
*/
|
||||
export const EnrichmentBlock: BlockConfig<EnrichmentRunResponse> = {
|
||||
type: 'enrichment',
|
||||
name: 'Data Enrichment',
|
||||
description: 'Enrich data with a Sim enrichment',
|
||||
longDescription:
|
||||
'Run a Sim enrichment to look up data — work email, phone number, company domain, company info, and more — from the fields you map in. Uses the same provider cascade as table enrichments.',
|
||||
docsLink: 'https://docs.sim.ai/tools/enrichment',
|
||||
category: 'tools',
|
||||
integrationType: IntegrationType.Sales,
|
||||
tags: ['enrichment'],
|
||||
bgColor: '#9333EA',
|
||||
icon: EnrichmentIcon,
|
||||
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Enrichment',
|
||||
type: 'dropdown',
|
||||
options: ALL_ENRICHMENTS.map((e) => ({ label: e.name, id: e.id })),
|
||||
value: () => ALL_ENRICHMENTS[0]?.id ?? '',
|
||||
},
|
||||
...inputSubBlocks,
|
||||
],
|
||||
|
||||
tools: {
|
||||
access: ['enrichment_run'],
|
||||
config: {
|
||||
tool: () => 'enrichment_run',
|
||||
params: (params) => {
|
||||
const enrichment = getEnrichment(params.operation)
|
||||
const inputs: Record<string, unknown> = {}
|
||||
if (enrichment) {
|
||||
for (const input of enrichment.inputs) {
|
||||
const value = params[inputFieldId(enrichment.id, input.id)]
|
||||
if (value !== undefined && value !== '') inputs[input.id] = value
|
||||
}
|
||||
}
|
||||
return { enrichmentId: params.operation, inputs }
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
inputs: blockInputs,
|
||||
outputs: blockOutputs,
|
||||
}
|
||||
@@ -51,6 +51,7 @@ import { ElasticsearchBlock } from '@/blocks/blocks/elasticsearch'
|
||||
import { ElevenLabsBlock } from '@/blocks/blocks/elevenlabs'
|
||||
import { EmailBisonBlock } from '@/blocks/blocks/emailbison'
|
||||
import { EnrichBlock } from '@/blocks/blocks/enrich'
|
||||
import { EnrichmentBlock } from '@/blocks/blocks/enrichment'
|
||||
import { EvaluatorBlock } from '@/blocks/blocks/evaluator'
|
||||
import { EvernoteBlock } from '@/blocks/blocks/evernote'
|
||||
import { ExaBlock } from '@/blocks/blocks/exa'
|
||||
@@ -305,6 +306,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
elevenlabs: ElevenLabsBlock,
|
||||
fathom: FathomBlock,
|
||||
enrich: EnrichBlock,
|
||||
enrichment: EnrichmentBlock,
|
||||
evaluator: EvaluatorBlock,
|
||||
evernote: EvernoteBlock,
|
||||
exa: ExaBlock,
|
||||
|
||||
@@ -1,6 +1,22 @@
|
||||
import type { SVGProps } from 'react'
|
||||
import { useId } from 'react'
|
||||
|
||||
export function EnrichmentIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox='0 0 24 24'
|
||||
fill='currentColor'
|
||||
role='img'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
>
|
||||
<path d='M12 2.5l1.9 4.6 4.6 1.9-4.6 1.9L12 15.5l-1.9-4.6L5.5 9l4.6-1.9L12 2.5z' />
|
||||
<path d='M18.5 14l.95 2.3 2.3.95-2.3.95L18.5 20.5l-.95-2.3-2.3-.95 2.3-.95.95-2.3z' />
|
||||
<path d='M5.5 14.5l.7 1.7 1.7.7-1.7.7-.7 1.7-.7-1.7-1.7-.7 1.7-.7.7-1.7z' />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function AgentMailIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 350 363' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
|
||||
@@ -1,4 +1,25 @@
|
||||
import type { EnrichmentProvider } from '@/enrichments/types'
|
||||
import type {
|
||||
EnrichmentInputField,
|
||||
EnrichmentOutputField,
|
||||
EnrichmentProvider,
|
||||
} from '@/enrichments/types'
|
||||
|
||||
/**
|
||||
* Narrow union of the field types enrichments declare. Assignable to both the
|
||||
* tool `OutputType` and the block `ParamType` unions, so a single mapping
|
||||
* function feeds both sides without per-call casts.
|
||||
*/
|
||||
export type EnrichmentFieldType = 'string' | 'number' | 'boolean' | 'json'
|
||||
|
||||
/** Maps an enrichment input/output column type to a block/tool field type. */
|
||||
export function mapFieldType(
|
||||
type: EnrichmentInputField['type'] | EnrichmentOutputField['type']
|
||||
): EnrichmentFieldType {
|
||||
if (type === 'number') return 'number'
|
||||
if (type === 'boolean') return 'boolean'
|
||||
if (type === 'json') return 'json'
|
||||
return 'string'
|
||||
}
|
||||
|
||||
/** Coerces an unknown input value to a trimmed string (`''` when nullish). */
|
||||
export function str(value: unknown): string {
|
||||
|
||||
@@ -17,6 +17,8 @@ export interface EnrichmentRunOutcome {
|
||||
* of blanking it — a genuine "no match" still leaves this `null`.
|
||||
*/
|
||||
error: string | null
|
||||
/** Label of the provider whose result was returned, or `null` on no match. */
|
||||
provider: string | null
|
||||
}
|
||||
|
||||
/** True when at least one output value in the result is non-empty. */
|
||||
@@ -77,7 +79,7 @@ export async function runEnrichment(
|
||||
const result = provider.mapOutput(response.output)
|
||||
if (result && hasResult(result)) {
|
||||
logger.info('Enrichment hit', { enrichmentId: enrichment.id, provider: provider.id })
|
||||
return { result, cost, error: null }
|
||||
return { result, cost, error: null, provider: provider.label }
|
||||
}
|
||||
} catch (err) {
|
||||
errorCount++
|
||||
@@ -93,5 +95,5 @@ export async function runEnrichment(
|
||||
// No provider hit. Surface an error only when every provider that ran errored
|
||||
// (infra/auth/rate-limit) — a clean miss returns a blank result instead.
|
||||
const error = ranCount > 0 && errorCount === ranCount ? lastError : null
|
||||
return { result: {}, cost, error }
|
||||
return { result: {}, cost, error, provider: null }
|
||||
}
|
||||
|
||||
@@ -21,10 +21,14 @@ export interface EnrichmentOutputField {
|
||||
type: ColumnDefinition['type']
|
||||
}
|
||||
|
||||
/** Per-row execution context handed to a provider's `run()` (runs server-side). */
|
||||
/**
|
||||
* Execution context for an enrichment run (runs server-side). `tableId`/`rowId`
|
||||
* are present for the table per-row path but optional — the workflow block path
|
||||
* (`/api/tools/enrichment/run`) has no table/row and passes only `workspaceId`.
|
||||
*/
|
||||
export interface EnrichmentRunContext {
|
||||
tableId: string
|
||||
rowId: string
|
||||
tableId?: string
|
||||
rowId?: string
|
||||
workspaceId: string
|
||||
signal?: AbortSignal
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { z } from 'zod'
|
||||
import { defineRouteContract } from '@/lib/api/contracts/types'
|
||||
|
||||
export const runEnrichmentBodySchema = z.object({
|
||||
enrichmentId: z.string().min(1, 'enrichmentId is required'),
|
||||
/** Per-enrichment input map: enrichment input id → mapped value. */
|
||||
inputs: z.record(z.string(), z.unknown()).default({}),
|
||||
workspaceId: z.string().min(1, 'workspaceId is required'),
|
||||
})
|
||||
|
||||
const runEnrichmentResponseSchema = z.object({
|
||||
matched: z.boolean(),
|
||||
// untyped-response: per-enrichment output map — keys and value types vary by enrichment
|
||||
result: z.record(z.string(), z.unknown()),
|
||||
cost: z.number(),
|
||||
error: z.string().nullable(),
|
||||
/** Label of the provider whose result was returned, null on no match. */
|
||||
provider: z.string().nullable(),
|
||||
})
|
||||
|
||||
export const runEnrichmentContract = defineRouteContract({
|
||||
method: 'POST',
|
||||
path: '/api/tools/enrichment/run',
|
||||
body: runEnrichmentBodySchema,
|
||||
response: { mode: 'json', schema: runEnrichmentResponseSchema },
|
||||
})
|
||||
|
||||
export type RunEnrichmentBody = z.input<typeof runEnrichmentBodySchema>
|
||||
export type RunEnrichmentResponse = z.output<typeof runEnrichmentResponseSchema>
|
||||
@@ -0,0 +1 @@
|
||||
export { enrichmentRunTool } from './run'
|
||||
@@ -0,0 +1,93 @@
|
||||
import { ALL_ENRICHMENTS } from '@/enrichments'
|
||||
import { mapFieldType } from '@/enrichments/providers'
|
||||
import type { EnrichmentRunParams, EnrichmentRunResponse } from '@/tools/enrichment/types'
|
||||
import type { OutputProperty, ToolConfig } from '@/tools/types'
|
||||
|
||||
/** Union of every distinct output across all registry enrichments. */
|
||||
const enrichmentOutputs: Record<string, OutputProperty> = {}
|
||||
for (const enrichment of ALL_ENRICHMENTS) {
|
||||
for (const output of enrichment.outputs) {
|
||||
if (!enrichmentOutputs[output.id]) {
|
||||
enrichmentOutputs[output.id] = {
|
||||
type: mapFieldType(output.type),
|
||||
description: `${output.name} (from the selected enrichment)`,
|
||||
optional: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a registry enrichment via `/api/tools/enrichment/run`. Selected and fed
|
||||
* by the Enrichment block; the route runs the provider cascade with the
|
||||
* workspace's hosted / BYOK key.
|
||||
*/
|
||||
export const enrichmentRunTool: ToolConfig<EnrichmentRunParams, EnrichmentRunResponse> = {
|
||||
id: 'enrichment_run',
|
||||
name: 'Run Enrichment',
|
||||
description: 'Run a Sim enrichment (e.g. Work Email, Phone Number) and return its outputs',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
enrichmentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Registry enrichment id (e.g. "work-email")',
|
||||
},
|
||||
inputs: {
|
||||
type: 'json',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: "Map of the enrichment's input ids to values",
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/enrichment/run',
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params: EnrichmentRunParams & { _context?: { workspaceId?: string } }) => ({
|
||||
enrichmentId: params.enrichmentId,
|
||||
inputs: params.inputs ?? {},
|
||||
workspaceId: params._context?.workspaceId,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
if (!response.ok || data.error) {
|
||||
return {
|
||||
success: false,
|
||||
output: { matched: false, provider: null },
|
||||
error: data.error || `Enrichment failed (${response.status})`,
|
||||
}
|
||||
}
|
||||
const result = (data.result ?? {}) as Record<string, unknown>
|
||||
const cost = typeof data.cost === 'number' ? data.cost : 0
|
||||
const provider = typeof data.provider === 'string' ? data.provider : null
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
...result,
|
||||
matched: Boolean(data.matched),
|
||||
provider,
|
||||
// Surface hosted-key cost so the workflow logging session bills it,
|
||||
// matching the convention used by hosted-key tools.
|
||||
...(cost > 0 ? { cost: { total: cost } } : {}),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
// Reserved keys go LAST so they always win if an enrichment ever declares an
|
||||
// output id of `matched` or `provider` (later spread / assignment wins in JS).
|
||||
outputs: {
|
||||
...enrichmentOutputs,
|
||||
matched: { type: 'boolean', description: 'Whether the enrichment found a result' },
|
||||
provider: {
|
||||
type: 'string',
|
||||
description: 'Provider whose result was returned (e.g. "Hunter", "People Data Labs")',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface EnrichmentRunParams {
|
||||
/** Registry enrichment id (e.g. `work-email`). */
|
||||
enrichmentId: string
|
||||
/** Map of the enrichment's input ids → values. */
|
||||
inputs: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface EnrichmentRunResponse extends ToolResponse {
|
||||
output: {
|
||||
/** Whether the enrichment found a result. */
|
||||
matched: boolean
|
||||
/** Label of the provider whose result was returned, null on no match. */
|
||||
provider: string | null
|
||||
} & Record<string, unknown>
|
||||
}
|
||||
@@ -633,6 +633,7 @@ import {
|
||||
enrichSearchSimilarCompaniesTool,
|
||||
enrichVerifyEmailTool,
|
||||
} from '@/tools/enrich'
|
||||
import { enrichmentRunTool } from '@/tools/enrichment'
|
||||
import {
|
||||
evernoteCopyNoteTool,
|
||||
evernoteCreateNotebookTool,
|
||||
@@ -4363,6 +4364,7 @@ export const tools: Record<string, ToolConfig> = {
|
||||
enrich_search_posts: enrichSearchPostsTool,
|
||||
enrich_search_similar_companies: enrichSearchSimilarCompaniesTool,
|
||||
enrich_verify_email: enrichVerifyEmailTool,
|
||||
enrichment_run: enrichmentRunTool,
|
||||
extend_parser: extendParserTool,
|
||||
extend_parser_v2: extendParserV2Tool,
|
||||
exa_search: exaSearchTool,
|
||||
|
||||
@@ -9,8 +9,8 @@ const QUERY_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/queries')
|
||||
const SELECTOR_HOOKS_DIR = path.join(ROOT, 'apps/sim/hooks/selectors')
|
||||
|
||||
const BASELINE = {
|
||||
totalRoutes: 757,
|
||||
zodRoutes: 757,
|
||||
totalRoutes: 758,
|
||||
zodRoutes: 758,
|
||||
nonZodRoutes: 0,
|
||||
} as const
|
||||
|
||||
|
||||
Reference in New Issue
Block a user