fix(enrichments): remove Icypeas providers and show Running on in-flight cells (#5572)

* fix(enrichments): remove Icypeas providers and show Running on in-flight cells

* fix(enrichments): keep previous value visible while an enrichment cell reruns
This commit is contained in:
Theodore Li
2026-07-10 18:23:19 -04:00
committed by GitHub
parent 5d3809a0e3
commit 43e61a1eaf
5 changed files with 8 additions and 88 deletions
@@ -85,6 +85,12 @@ export function resolveCellRender({
return resolveLinkKind(text, currentWorkspaceId) ?? { kind: 'value', text }
}
// Enrichment outputs share an empty blockId, so `runningBlockIds` can never
// match them — the group-level `running` status is the only "worker picked
// this up" signal an enrichment cell has. Checked after the value so a
// rerun keeps showing the previous output until the new result lands.
if (isEnrichmentOutput && exec?.status === 'running') return { kind: 'running' }
if (inFlight && !(groupHasBlockErrors && !blockRunning)) {
// A `pending` cell whose jobId starts with `paused-` is mid-pause
// (workflow yielded for human-in-the-loop). Render as Pending rather
@@ -19,7 +19,6 @@ describe('email-verification enrichment cascade', () => {
'zerobounce',
'neverbounce',
'millionverifier',
'icypeas',
'enrow',
])
})
@@ -39,31 +38,6 @@ describe('email-verification enrichment cascade', () => {
})
})
describe('icypeas', () => {
const p = provider('icypeas')
it('maps FOUND/DEBITED to deliverable and NOT_FOUND to undeliverable', () => {
expect(p.toolId).toBe('icypeas_verify_email')
expect(p.buildParams(emailInput)).toEqual({ email: 'john@acme.com' })
expect(p.buildParams({ email: '' })).toBeNull()
expect(p.mapOutput({ status: 'FOUND' })).toEqual({ status: 'valid', deliverable: true })
expect(p.mapOutput({ status: 'DEBITED' })).toEqual({ status: 'valid', deliverable: true })
expect(p.mapOutput({ status: 'NOT_FOUND' })).toEqual({
status: 'invalid',
deliverable: false,
})
expect(p.mapOutput({ status: 'DEBITED_NOT_FOUND' })).toEqual({
status: 'invalid',
deliverable: false,
})
})
it('falls through on inconclusive statuses', () => {
expect(p.mapOutput({ status: 'BAD_INPUT' })).toBeNull()
expect(p.mapOutput({ status: 'INSUFFICIENT_FUNDS' })).toBeNull()
expect(p.mapOutput({ status: 'ABORTED' })).toBeNull()
expect(p.mapOutput({})).toBeNull()
})
})
describe('enrow', () => {
const p = provider('enrow')
it('maps the valid/invalid qualifier and falls through otherwise', () => {
@@ -5,7 +5,7 @@ import type { EnrichmentConfig } from '@/enrichments/types'
/**
* Email Verification enrichment. Checks an email address's deliverability via a
* verifier waterfall — ZeroBounce first (highest coverage), then NeverBounce,
* then MillionVerifier, then Icypeas, then Enrow. A provider that returns a
* then MillionVerifier, then Enrow. A provider that returns a
* definitive verdict (valid / invalid / catch_all / disposable / etc.) fills the
* cell; a provider that can only return `unknown` falls through to the next so
* the row gets the most confident answer available. All providers support hosted
@@ -68,26 +68,6 @@ export const emailVerificationEnrichment: EnrichmentConfig = {
return { status, deliverable: output.deliverable === true }
},
}),
toolProvider({
id: 'icypeas',
label: 'Icypeas',
toolId: 'icypeas_verify_email',
buildParams: (inputs) => {
const email = str(inputs.email)
if (!email) return null
return { email }
},
mapOutput: (output) => {
// FOUND/DEBITED → deliverable, NOT_FOUND/DEBITED_NOT_FOUND → undeliverable.
// Bad input / insufficient funds / aborted are inconclusive → fall through.
const status = str(output.status)
if (status === 'FOUND' || status === 'DEBITED')
return { status: 'valid', deliverable: true }
if (status === 'NOT_FOUND' || status === 'DEBITED_NOT_FOUND')
return { status: 'invalid', deliverable: false }
return null
},
}),
toolProvider({
id: 'enrow',
label: 'Enrow',
@@ -26,7 +26,6 @@ describe('work-email enrichment cascade', () => {
'datagma',
'leadmagic',
'dropcontact',
'icypeas',
'enrow',
])
})
@@ -130,25 +129,6 @@ describe('work-email enrichment cascade', () => {
})
})
describe('icypeas', () => {
const p = provider('icypeas')
it('splits the name when possible and keeps mononym rows', () => {
expect(p.toolId).toBe('icypeas_find_email')
expect(p.buildParams(nameDomain)).toEqual({
firstname: 'John',
lastname: 'Doe',
domainOrCompany: 'acme.com',
})
// single-token name runs with firstname alone (lastname is optional)
expect(p.buildParams({ fullName: 'Cher', companyDomain: 'acme.com' })).toEqual({
firstname: 'Cher',
domainOrCompany: 'acme.com',
})
expect(p.buildParams({ fullName: 'John Doe' })).toBeNull()
expect(p.mapOutput({ email: 'j@acme.com' })).toEqual({ email: 'j@acme.com' })
})
})
describe('enrow', () => {
const p = provider('enrow')
it('maps full name + company domain', () => {
+1 -21
View File
@@ -8,7 +8,7 @@ import type { EnrichmentConfig } from '@/enrichments/types'
* available identifiers (company domain, LinkedIn URL) via a provider waterfall:
* deterministic finders first (Hunter, Findymail by name then by LinkedIn), then
* enrichment/reveal providers (Prospeo, Wiza), then People Data Labs as a broad
* record-match fallback, then Datagma, LeadMagic, Dropcontact, Icypeas, and Enrow
* record-match fallback, then Datagma, LeadMagic, Dropcontact, and Enrow
* as additional finders. Each provider opportunistically uses whatever
* identifiers the row provides and self-skips when it has none usable, so adding
* more inputs widens coverage. First email wins; all providers support hosted keys.
@@ -189,26 +189,6 @@ export const workEmailEnrichment: EnrichmentConfig = {
return email ? { email } : null
},
}),
toolProvider({
id: 'icypeas',
label: 'Icypeas',
toolId: 'icypeas_find_email',
buildParams: (inputs) => {
// Icypeas only requires domainOrCompany; firstname/lastname are optional,
// so a mononym still runs with firstname alone rather than self-skipping.
const fullName = str(inputs.fullName)
const domainOrCompany = normalizeDomain(inputs.companyDomain)
if (!fullName || !domainOrCompany) return null
const name = splitName(inputs.fullName)
return name
? { firstname: name.firstName, lastname: name.lastName, domainOrCompany }
: { firstname: fullName, domainOrCompany }
},
mapOutput: (output) => {
const email = str(output.email)
return email ? { email } : null
},
}),
toolProvider({
id: 'enrow',
label: 'Enrow',