From bb94b051990c96e1edd623a0bea652e8f47c6159 Mon Sep 17 00:00:00 2001 From: juliet Date: Thu, 20 Aug 2026 19:48:23 -0400 Subject: [PATCH] Remove the Image Similarity Score signal (#1043) * Remove the Image Similarity Score signal The signal was never functional: it registered with a permanent "coming soon" disabled message and no implementation behind it, a holdover from Coop's SaaS origins. Removes the signal class, its registration, the SignalType enum value, and the GraphQL enum value. The legacy public.enum_signals_type Postgres enum still lists the value, but no table references that type, so no migration is needed. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wx3nmYtSe8Cb37QQ8WY3mx * Show ROOST as developer for built-in signals and drop their pricing row Built-in signals (no integration) now display ROOST as the developer in the signal gallery and detail view, and the detail view omits the Pricing Structure row for them since pricing only applies to third-party integrations. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wx3nmYtSe8Cb37QQ8WY3mx * Remove the Contains Image (exact match) signal Same cleanup as Image Similarity Score: the signal was a permanently disabled stub whose run() throws "not implemented", showing the same "coming soon" message. Bank-backed image matching is served by the HMA image similarity signals. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wx3nmYtSe8Cb37QQ8WY3mx * Regenerate codegen output with lockfile-pinned prettier The committed generated.ts was formatted by a stale local prettier 3.8.3, which failed both check_formatting and the byte-for-byte check_generated_graphql diff against CI's pinned 3.9.6 output. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Wx3nmYtSe8Cb37QQ8WY3mx --------- Co-authored-by: Claude Fable 5 --- client/src/graphql/generated.ts | 2 - client/src/models/signal.ts | 2 - .../RuleFormSignalModalMenuItem.tsx | 2 +- .../RuleFormSignalModalSignalDetailView.tsx | 30 +++--- server/graphql/generated.ts | 2 - server/graphql/modules/signal.ts | 2 - .../helpers/instantiateBuiltInSignals.ts | 4 - .../signals/ImageExactMatchSignal.ts | 87 ----------------- .../signals/ImageSimilarityScoreSignal.ts | 94 ------------------- .../signalsService/types/SignalArgsByType.ts | 4 - .../signalsService/types/SignalType.ts | 4 - 11 files changed, 19 insertions(+), 214 deletions(-) delete mode 100644 server/services/signalsService/signals/ImageExactMatchSignal.ts delete mode 100644 server/services/signalsService/signals/ImageSimilarityScoreSignal.ts diff --git a/client/src/graphql/generated.ts b/client/src/graphql/generated.ts index 22799d7..e30a54f 100644 --- a/client/src/graphql/generated.ts +++ b/client/src/graphql/generated.ts @@ -4443,10 +4443,8 @@ export const GQLSignalType = { Custom: 'CUSTOM', GeoContainedWithin: 'GEO_CONTAINED_WITHIN', GoogleContentSafetyApiImage: 'GOOGLE_CONTENT_SAFETY_API_IMAGE', - ImageExactMatch: 'IMAGE_EXACT_MATCH', ImageSimilarityDoesNotMatch: 'IMAGE_SIMILARITY_DOES_NOT_MATCH', ImageSimilarityMatch: 'IMAGE_SIMILARITY_MATCH', - ImageSimilarityScore: 'IMAGE_SIMILARITY_SCORE', OpenAiGraphicViolenceImageModel: 'OPEN_AI_GRAPHIC_VIOLENCE_IMAGE_MODEL', OpenAiGraphicViolenceTextModel: 'OPEN_AI_GRAPHIC_VIOLENCE_TEXT_MODEL', OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', diff --git a/client/src/models/signal.ts b/client/src/models/signal.ts index 5c74378..efb1241 100644 --- a/client/src/models/signal.ts +++ b/client/src/models/signal.ts @@ -46,10 +46,8 @@ export function integrationForSignalType(type: string) { case 'AGGREGATION': case 'CUSTOM': case 'GEO_CONTAINED_WITHIN': - case 'IMAGE_EXACT_MATCH': case 'IMAGE_SIMILARITY_MATCH': case 'IMAGE_SIMILARITY_DOES_NOT_MATCH': - case 'IMAGE_SIMILARITY_SCORE': case 'TEXT_MATCHING_CONTAINS_REGEX': case 'TEXT_MATCHING_CONTAINS_TEXT': case 'TEXT_MATCHING_CONTAINS_VARIANT': diff --git a/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalMenuItem.tsx b/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalMenuItem.tsx index eaf93e5..8e67f4c 100644 --- a/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalMenuItem.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalMenuItem.tsx @@ -12,7 +12,7 @@ export function vendorName(signal: GQLSignal) { return 'Custom'; } if (!signal.integration) { - return 'Coop'; + return 'ROOST'; } if (signal.integrationTitle) { return signal.integrationTitle; diff --git a/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalSignalDetailView.tsx b/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalSignalDetailView.tsx index 15d916e..9601505 100644 --- a/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalSignalDetailView.tsx +++ b/client/src/webpages/dashboard/rules/rule_form/signal_modal/RuleFormSignalModalSignalDetailView.tsx @@ -32,7 +32,7 @@ export default function RuleFormSignalModalSignalDetailView(props: { .replace(/_/g, ' ') .toLowerCase() .replace(/^([a-z])|\s+([a-z])/g, (m) => m.toUpperCase()) - : 'Coop'); + : 'ROOST'); // Signals use the logo-with-background variant. const rawLogoSrc = signal.integrationLogoWithBackgroundUrl ?? @@ -76,17 +76,23 @@ export default function RuleFormSignalModalSignalDetailView(props: { }, ] : []), - { - label: 'Pricing Structure', - value: (() => { - switch (signal.pricingStructure.type) { - case GQLSignalPricingStructureType.Free: - return 'Free'; - case GQLSignalPricingStructureType.Subscription: - return 'Subscription'; - } - })(), - }, + // Pricing only applies to third-party integrations; built-in signals + // ship with Coop and showing "Free" for them is noise. + ...(signal.integration != null + ? [ + { + label: 'Pricing Structure', + value: (() => { + switch (signal.pricingStructure.type) { + case GQLSignalPricingStructureType.Free: + return 'Free'; + case GQLSignalPricingStructureType.Subscription: + return 'Subscription'; + } + })(), + }, + ] + : []), ...(signal.supportedLanguages.__typename === 'Languages' && !signal.supportedLanguages.languages.length diff --git a/server/graphql/generated.ts b/server/graphql/generated.ts index 8b5ecbb..23fc12f 100644 --- a/server/graphql/generated.ts +++ b/server/graphql/generated.ts @@ -4511,10 +4511,8 @@ export const GQLSignalType = { Custom: 'CUSTOM', GeoContainedWithin: 'GEO_CONTAINED_WITHIN', GoogleContentSafetyApiImage: 'GOOGLE_CONTENT_SAFETY_API_IMAGE', - ImageExactMatch: 'IMAGE_EXACT_MATCH', ImageSimilarityDoesNotMatch: 'IMAGE_SIMILARITY_DOES_NOT_MATCH', ImageSimilarityMatch: 'IMAGE_SIMILARITY_MATCH', - ImageSimilarityScore: 'IMAGE_SIMILARITY_SCORE', OpenAiGraphicViolenceImageModel: 'OPEN_AI_GRAPHIC_VIOLENCE_IMAGE_MODEL', OpenAiGraphicViolenceTextModel: 'OPEN_AI_GRAPHIC_VIOLENCE_TEXT_MODEL', OpenAiHateTextModel: 'OPEN_AI_HATE_TEXT_MODEL', diff --git a/server/graphql/modules/signal.ts b/server/graphql/modules/signal.ts index a6aae1c..25a426b 100644 --- a/server/graphql/modules/signal.ts +++ b/server/graphql/modules/signal.ts @@ -94,8 +94,6 @@ const typeDefs = /* GraphQL */ ` TEXT_MATCHING_NOT_CONTAINS_REGEX TEXT_MATCHING_CONTAINS_VARIANT TEXT_SIMILARITY_SCORE - IMAGE_EXACT_MATCH - IMAGE_SIMILARITY_SCORE IMAGE_SIMILARITY_DOES_NOT_MATCH IMAGE_SIMILARITY_MATCH GOOGLE_CONTENT_SAFETY_API_IMAGE diff --git a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts index ec236dd..445da0c 100644 --- a/server/services/signalsService/helpers/instantiateBuiltInSignals.ts +++ b/server/services/signalsService/helpers/instantiateBuiltInSignals.ts @@ -7,10 +7,8 @@ import { type UserScore } from '../../userStatisticsService/userStatisticsServic import { type UserStrikeService } from '../../userStrikeService/index.js'; import AggregationSignal from '../signals/aggregation/AggregationSignal.js'; import GeoContainedWithinSignal from '../signals/GeoContainedWithinSignal.js'; -import ImageExactMatchSignal from '../signals/ImageExactMatchSignal.js'; import ImageSimilarityDoesNotMatchSignal from '../signals/ImageSimilarityDoesNotMatch.js'; import ImageSimilarityMatchSignal from '../signals/ImageSimilarityMatch.js'; -import ImageSimilarityScoreSignal from '../signals/ImageSimilarityScoreSignal.js'; import { type SignalBase, type SignalInputType, @@ -77,8 +75,6 @@ export function instantiateBuiltInSignals( [SignalType.TEXT_MATCHING_CONTAINS_VARIANT]: new TextMatchingContainsVariantSignal(), [SignalType.TEXT_SIMILARITY_SCORE]: new TextSimilarityScoreSignal(), - [SignalType.IMAGE_EXACT_MATCH]: new ImageExactMatchSignal(), - [SignalType.IMAGE_SIMILARITY_SCORE]: new ImageSimilarityScoreSignal(), [SignalType.IMAGE_SIMILARITY_DOES_NOT_MATCH]: new ImageSimilarityDoesNotMatchSignal(hmaService), [SignalType.IMAGE_SIMILARITY_MATCH]: new ImageSimilarityMatchSignal( diff --git a/server/services/signalsService/signals/ImageExactMatchSignal.ts b/server/services/signalsService/signals/ImageExactMatchSignal.ts deleted file mode 100644 index b3f7e6c..0000000 --- a/server/services/signalsService/signals/ImageExactMatchSignal.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { ScalarTypes } from '@roostorg/coop-types'; - -import { SignalPricingStructure as SignalPricingStructureType } from '../types/SignalPricingStructure.js'; -import { SignalType } from '../types/SignalType.js'; -import SignalBase, { - type SignalDisabledInfo, - type SignalInput, -} from './SignalBase.js'; - -export default class ImageExactMatchSignal extends SignalBase< - ScalarTypes['IMAGE'], - { scalarType: ScalarTypes['BOOLEAN'] } -> { - override get id() { - return { type: SignalType.IMAGE_EXACT_MATCH }; - } - - override get displayName() { - return 'Contains image'; - } - - override get description() { - return ( - 'Detects whether any predefined images' + - "exactly match any of the content's images" - ); - } - - override get supportedLanguages() { - return 'ALL' as const; - } - - get pricingStructure(): SignalPricingStructureType { - return SignalPricingStructureType.FREE; - } - - override async getDisabledInfo(_: unknown): Promise { - return { - disabled: true, - disabledMessage: 'Support for image comparison signals is coming soon!', - }; - } - - override get outputType() { - return { scalarType: ScalarTypes.BOOLEAN }; - } - - override get eligibleInputs() { - return [ScalarTypes.IMAGE]; - } - - override get needsMatchingValues() { - return true; - } - - override getCost() { - return 10; - } - - override get allowedInAutomatedRules() { - return true; - } - - override get recommendedThresholds() { - return null; - } - - override get needsActionPenalties() { - return false; - } - - override get integration() { - return null; - } - - override get docsUrl() { - return null; - } - - override get eligibleSubcategories() { - return []; - } - - async run(_input: SignalInput): Promise { - throw new Error('not implemented'); - } -} diff --git a/server/services/signalsService/signals/ImageSimilarityScoreSignal.ts b/server/services/signalsService/signals/ImageSimilarityScoreSignal.ts deleted file mode 100644 index 0638b50..0000000 --- a/server/services/signalsService/signals/ImageSimilarityScoreSignal.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { ScalarTypes } from '@roostorg/coop-types'; - -import { SignalPricingStructure as SignalPricingStructureType } from '../types/SignalPricingStructure.js'; -import { SignalType } from '../types/SignalType.js'; -import SignalBase, { - type SignalDisabledInfo, - type SignalInput, -} from './SignalBase.js'; - -export default class ImageSimilarityScoreSignal extends SignalBase< - ScalarTypes['IMAGE'], - { scalarType: ScalarTypes['NUMBER'] } -> { - override get id() { - return { type: SignalType.IMAGE_SIMILARITY_SCORE }; - } - - override get outputType() { - return { scalarType: ScalarTypes.NUMBER }; - } - - override get displayName() { - return 'Image similarity score'; - } - - override get supportedLanguages() { - return 'ALL' as const; - } - - get pricingStructure(): SignalPricingStructureType { - return SignalPricingStructureType.FREE; - } - - override get description() { - return ( - "Computes the similarity between the content's" + - ' images and a predefined image. If the content has multiple' + - ' images, the maximum similarity score of all the images' + - ' will be used.' - ); - } - - override async getDisabledInfo(_: unknown): Promise { - return { - disabled: true, - disabledMessage: 'Support for image comparison signals is coming soon!', - }; - } - - override get eligibleInputs() { - return [ScalarTypes.IMAGE]; - } - - override get needsMatchingValues() { - return true; - } - - /** - * Estimated cost of hashing image and computing similarity score - */ - override getCost() { - return 10; - } - - override get allowedInAutomatedRules() { - return true; - } - - override get eligibleSubcategories() { - return []; - } - - override get needsActionPenalties() { - return false; - } - - override get docsUrl() { - return null; - } - - override get recommendedThresholds() { - return null; - } - - override get integration() { - return null; - } - - override async run( - _input: SignalInput, - ): Promise { - throw new Error('not implemented'); - } -} diff --git a/server/services/signalsService/types/SignalArgsByType.ts b/server/services/signalsService/types/SignalArgsByType.ts index 83ac4ce..6d4fef8 100644 --- a/server/services/signalsService/types/SignalArgsByType.ts +++ b/server/services/signalsService/types/SignalArgsByType.ts @@ -17,8 +17,6 @@ export type SignalArgsByType = Satisfies< [SignalType.TEXT_MATCHING_NOT_CONTAINS_REGEX]: undefined; [SignalType.TEXT_MATCHING_CONTAINS_VARIANT]: undefined; [SignalType.TEXT_SIMILARITY_SCORE]: undefined; - [SignalType.IMAGE_EXACT_MATCH]: undefined; - [SignalType.IMAGE_SIMILARITY_SCORE]: undefined; [SignalType.IMAGE_SIMILARITY_DOES_NOT_MATCH]: undefined; [SignalType.IMAGE_SIMILARITY_MATCH]: undefined; [SignalType.GEO_CONTAINED_WITHIN]: undefined; @@ -59,8 +57,6 @@ export type RuntimeSignalArgsByType = Satisfies< [SignalType.TEXT_MATCHING_NOT_CONTAINS_REGEX]: undefined; [SignalType.TEXT_MATCHING_CONTAINS_VARIANT]: undefined; [SignalType.TEXT_SIMILARITY_SCORE]: undefined; - [SignalType.IMAGE_EXACT_MATCH]: undefined; - [SignalType.IMAGE_SIMILARITY_SCORE]: undefined; [SignalType.IMAGE_SIMILARITY_DOES_NOT_MATCH]: undefined; [SignalType.IMAGE_SIMILARITY_MATCH]: undefined; [SignalType.GEO_CONTAINED_WITHIN]: undefined; diff --git a/server/services/signalsService/types/SignalType.ts b/server/services/signalsService/types/SignalType.ts index f6570ec..6c58e04 100644 --- a/server/services/signalsService/types/SignalType.ts +++ b/server/services/signalsService/types/SignalType.ts @@ -27,8 +27,6 @@ export const BuiltInExternalSignalType = makeEnumLike([ 'TEXT_MATCHING_NOT_CONTAINS_REGEX', 'TEXT_MATCHING_CONTAINS_VARIANT', 'TEXT_SIMILARITY_SCORE', - 'IMAGE_EXACT_MATCH', - 'IMAGE_SIMILARITY_SCORE', 'IMAGE_SIMILARITY_DOES_NOT_MATCH', 'IMAGE_SIMILARITY_MATCH', 'GEO_CONTAINED_WITHIN', @@ -120,8 +118,6 @@ export function integrationForSignalType(type: SignalType | string) { case 'CUSTOM': case 'GEO_CONTAINED_WITHIN': case 'GOOGLE_CLOUD_TRANSLATE_MODEL': - case 'IMAGE_EXACT_MATCH': - case 'IMAGE_SIMILARITY_SCORE': case 'IMAGE_SIMILARITY_DOES_NOT_MATCH': case 'IMAGE_SIMILARITY_MATCH': case 'TEXT_MATCHING_CONTAINS_REGEX': -- 2.51.2