Skip to content
Open
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
177 changes: 177 additions & 0 deletions apps/sim/blocks/blocks/resemble.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import { ResembleIcon } from '@/components/icons'
import type { BlockConfig } from '@/blocks/types'
import { AuthMode, IntegrationType } from '@/blocks/types'
import type { ResembleResponse } from '@/tools/resemble/types'

export const ResembleBlock: BlockConfig<ResembleResponse> = {
type: 'resemble',
name: 'Resemble',
description: 'Deepfake detection, media intelligence, and watermarking',
longDescription:
'Integrate Resemble AI media safety into your workflow: detect deepfakes in audio/image/video, analyze media intelligence, and apply or detect invisible watermarks.',
docsLink: 'https://docs.resemble.ai',
category: 'tools',
integrationType: IntegrationType.Security,
bgColor: '#2E1AC4',
icon: ResembleIcon,
authMode: AuthMode.ApiKey,

subBlocks: [
{
id: 'operation',
title: 'Operation',
type: 'dropdown',
options: [
{ label: 'Deepfake Detection', id: 'resemble_detect' },
{ label: 'Media Intelligence', id: 'resemble_intelligence' },
{ label: 'Detect Watermark', id: 'resemble_watermark_detect' },
{ label: 'Apply Watermark', id: 'resemble_watermark_apply' },
],
value: () => 'resemble_detect',
},
{
id: 'url',
title: 'Media URL',
type: 'short-input',
placeholder: 'https://example.com/media.mp4',
required: true,
},
// Detection toggles
{
id: 'runIntelligence',
title: 'Run Intelligence',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'audioSourceTracing',
title: 'Audio Source Tracing',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'visualize',
title: 'Visualize',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'useReverseSearch',
title: 'Reverse Image Search',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'useOodDetector',
title: 'OOD Detector',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'zeroRetentionMode',
title: 'Zero-Retention Mode',
type: 'switch',
condition: { field: 'operation', value: 'resemble_detect' },
},
{
id: 'modelTypes',
title: 'Model Type',
type: 'dropdown',
options: [
{ label: 'Auto', id: 'auto' },
{ label: 'Image', id: 'image' },
{ label: 'Talking Head', id: 'talking_head' },
],
value: () => 'auto',
condition: { field: 'operation', value: 'resemble_detect' },
},
// Intelligence options
{
id: 'structuredJson',
title: 'Structured JSON',
type: 'switch',
defaultValue: true,
condition: { field: 'operation', value: 'resemble_intelligence' },
},
{
id: 'mediaType',
title: 'Media Type',
type: 'dropdown',
options: [
{ label: 'Auto', id: 'auto' },
{ label: 'Audio', id: 'audio' },
{ label: 'Video', id: 'video' },
{ label: 'Image', id: 'image' },
],
value: () => 'auto',
condition: { field: 'operation', value: 'resemble_intelligence' },
Comment on lines +88 to +107

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 structuredJson parameter has no corresponding UI control

intelligenceTool declares a structuredJson boolean param that controls whether the API returns structured JSON fields. Because no subBlock exists for it in the block definition, the value is always undefined in the UI, which causes the body builder to evaluate p.structuredJson !== false as true — permanently fixing it to structured mode. Users who need raw/unstructured output from the Intelligence operation cannot opt out from the block UI.

},
// Apply-watermark options
{
id: 'strength',
title: 'Strength (0–1)',
type: 'short-input',
placeholder: '0.2',
condition: { field: 'operation', value: 'resemble_watermark_apply' },
},
{
id: 'customMessage',
title: 'Custom Message',
type: 'short-input',
placeholder: 'resembleai',
condition: { field: 'operation', value: 'resemble_watermark_apply' },
},
{
id: 'apiKey',
title: 'API Key',
type: 'short-input',
placeholder: 'Enter your Resemble API key',
required: true,
password: true,
},
],

tools: {
access: [
'resemble_detect',
'resemble_intelligence',
'resemble_watermark_detect',
'resemble_watermark_apply',
],
config: {
tool: (params) => {
switch (params.operation) {
case 'resemble_intelligence':
return 'resemble_intelligence'
case 'resemble_watermark_detect':
return 'resemble_watermark_detect'
case 'resemble_watermark_apply':
return 'resemble_watermark_apply'
default:
return 'resemble_detect'
}
},
},
},

inputs: {
operation: { type: 'string', description: 'Operation to perform' },
url: { type: 'string', description: 'Public HTTPS URL to the media' },
runIntelligence: { type: 'boolean', description: 'Also run media intelligence' },
audioSourceTracing: { type: 'boolean', description: 'Trace the source platform of fake audio' },
visualize: { type: 'boolean', description: 'Generate heatmap artifacts' },
useReverseSearch: { type: 'boolean', description: 'Image-only reverse image search' },
useOodDetector: { type: 'boolean', description: 'Out-of-distribution detection' },
zeroRetentionMode: { type: 'boolean', description: 'Auto-delete media after analysis' },
modelTypes: { type: 'string', description: 'auto | image | talking_head' },
structuredJson: { type: 'boolean', description: 'Return structured JSON fields' },
mediaType: { type: 'string', description: 'auto | audio | video | image' },
strength: { type: 'number', description: 'Watermark strength 0–1' },
customMessage: { type: 'string', description: 'Watermark message' },
apiKey: { type: 'string', description: 'Resemble API key' },
},

outputs: {
result: { type: 'json', description: 'Result from the selected Resemble operation' },
},
}
2 changes: 2 additions & 0 deletions apps/sim/blocks/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ import { RDSBlock, RDSBlockMeta } from '@/blocks/blocks/rds'
import { RedditBlock, RedditBlockMeta } from '@/blocks/blocks/reddit'
import { RedisBlock, RedisBlockMeta } from '@/blocks/blocks/redis'
import { ReductoBlock, ReductoBlockMeta, ReductoV2Block } from '@/blocks/blocks/reducto'
import { ResembleBlock } from '@/blocks/blocks/resemble'
import { ResendBlock, ResendBlockMeta } from '@/blocks/blocks/resend'
import { ResponseBlock } from '@/blocks/blocks/response'
import { RevenueCatBlock, RevenueCatBlockMeta } from '@/blocks/blocks/revenuecat'
Expand Down Expand Up @@ -536,6 +537,7 @@ const BLOCK_REGISTRY: Record<string, BlockConfig> = {
sendblue: SendblueBlock,
sendgrid: SendGridBlock,
sentry: SentryBlock,
resemble: ResembleBlock,
serper: SerperBlock,
servicenow: ServiceNowBlock,
ses: SESBlock,
Expand Down
19 changes: 19 additions & 0 deletions apps/sim/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7584,3 +7584,22 @@ export function WizaIcon(props: SVGProps<SVGSVGElement>) {
</svg>
)
}

export function ResembleIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
{...props}
viewBox='0 0 24 24'
fill='none'
stroke='currentColor'
strokeWidth='1.6'
role='img'
xmlns='http://www.w3.org/2000/svg'
>
<path d='M12 2.5l7 2.4v5c0 4.3-2.9 8.2-7 9.6-4.1-1.4-7-5.3-7-9.6v-5l7-2.4z' strokeLinejoin='round' />
<line x1='9' y1='10.5' x2='9' y2='13.5' strokeLinecap='round' />
<line x1='12' y1='8.5' x2='12' y2='15.5' strokeLinecap='round' />
<line x1='15' y1='10' x2='15' y2='14' strokeLinecap='round' />
</svg>
)
}
10 changes: 10 additions & 0 deletions apps/sim/tools/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2435,6 +2435,12 @@ import {
redisTtlTool,
} from '@/tools/redis'
import { reductoParserTool, reductoParserV2Tool } from '@/tools/reducto'
import {
detectTool as resembleDetectTool,
intelligenceTool as resembleIntelligenceTool,
watermarkApplyTool as resembleWatermarkApplyTool,
watermarkDetectTool as resembleWatermarkDetectTool,
} from '@/tools/resemble'
import {
resendCreateContactTool,
resendDeleteContactTool,
Expand Down Expand Up @@ -4020,6 +4026,10 @@ export const tools: Record<string, ToolConfig> = {
github_repo_info_v2: githubRepoInfoV2Tool,
github_latest_commit: githubLatestCommitTool,
github_latest_commit_v2: githubLatestCommitV2Tool,
resemble_detect: resembleDetectTool,
resemble_intelligence: resembleIntelligenceTool,
resemble_watermark_detect: resembleWatermarkDetectTool,
resemble_watermark_apply: resembleWatermarkApplyTool,
serper_search: serperSearchTool,
similarweb_website_overview: similarwebWebsiteOverviewTool,
similarweb_traffic_visits: similarwebTrafficVisitsTool,
Expand Down
121 changes: 121 additions & 0 deletions apps/sim/tools/resemble/detect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import type { ResembleDetectParams, ResembleResponse } from '@/tools/resemble/types'
import { authHeaders, baseOf, pollResource, rItem, sanitize } from '@/tools/resemble/utils'
import type { ToolConfig } from '@/tools/types'

export const detectTool: ToolConfig<ResembleDetectParams, ResembleResponse> = {
id: 'resemble_detect',
name: 'Resemble Deepfake Detection',
description: 'Detect whether media (audio, image, or video) is a deepfake / AI-generated.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'Resemble API key',
},
url: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Public HTTPS URL to the media',
},
runIntelligence: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Also run media intelligence',
},
audioSourceTracing: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Trace the source platform of fake audio',
},
visualize: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Generate heatmap artifacts',
},
useReverseSearch: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Image-only reverse image search',
},
useOodDetector: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Out-of-distribution detection',
},
zeroRetentionMode: {
type: 'boolean',
required: false,
visibility: 'user-only',
description: 'Auto-delete media after analysis',
},
modelTypes: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'auto | image | talking_head',
},
maxWaitSeconds: {
type: 'number',
required: false,
visibility: 'user-only',
description: 'Max seconds to poll for the result',
},
baseUrl: {
type: 'string',
required: false,
visibility: 'user-only',
description: 'API base URL override',
},
},
request: {
url: (p) => `${baseOf(p)}/detect`,
method: 'POST',
headers: (p) => authHeaders(p),
body: (p) => {
const b: Record<string, any> = { url: p.url }
if (p.runIntelligence) b.intelligence = true
if (p.audioSourceTracing) b.audio_source_tracing = true
if (p.visualize) b.visualize = true
if (p.useReverseSearch) b.use_reverse_search = true
if (p.useOodDetector) b.use_ood_detector = true
if (p.zeroRetentionMode) b.zero_retention_mode = true
if (p.modelTypes && p.modelTypes !== 'auto') b.model_types = p.modelTypes
return b
},
},
transformResponse: async (response: Response, params?: ResembleDetectParams) => {
const text = await response.text()
let data: any
try {
data = JSON.parse(text)
} catch {
data = { raw: text }
}
if (!response.ok)
throw new Error((data && data.message) || `Resemble API error: HTTP ${response.status}`)
const uuid = rItem(data).uuid
if (uuid && params) {
data = await pollResource(
baseOf(params),
`/detect/${uuid}`,
authHeaders(params),
params.maxWaitSeconds || 120
)
}
return { success: true, output: { result: sanitize(data) } }
},
outputs: {
result: {
type: 'json',
description: 'Detection result (label, score, metrics, optional intelligence).',
},
},
}
5 changes: 5 additions & 0 deletions apps/sim/tools/resemble/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { detectTool } from '@/tools/resemble/detect'
export { intelligenceTool } from '@/tools/resemble/intelligence'
export * from '@/tools/resemble/types'
export { watermarkApplyTool } from '@/tools/resemble/watermark_apply'
export { watermarkDetectTool } from '@/tools/resemble/watermark_detect'
Loading