feat(voice): ship noise suppression treatment to everyone (#3029)

This commit is contained in:
Hampus
2026-09-29 03:43:58 +02:00
committed by GitHub
parent 39c72f0fb0
commit c488906131
88 changed files with 2861 additions and 5837 deletions
@@ -32,10 +32,6 @@ import {
ProfileTimezoneConfigUpdateRequest,
} from '@fluxer/schema/src/domains/admin/ProfileTimezoneSchemas';
import {PushRelayConfigResponse, PushRelayConfigUpdateRequest} from '@fluxer/schema/src/domains/admin/PushRelaySchemas';
import {
VoiceNoiseSuppressionConfigResponse,
VoiceNoiseSuppressionConfigUpdateRequest,
} from '@fluxer/schema/src/domains/admin/VoiceNoiseSuppressionSchemas';
import {
ExperimentDeliveryConfigResponse,
ExperimentDeliveryConfigUpdateRequest,
@@ -675,7 +671,6 @@ const InstanceIntegrationsResponse = z.object({
export const InstanceConfigResponse = z.object({
sso: SsoConfigResponse,
gateway_rollout: GatewayRolloutConfigResponse,
voice_noise_suppression: VoiceNoiseSuppressionConfigResponse,
push_relay: PushRelayConfigResponse,
domain_migration: DomainMigrationConfigResponse,
altcha_captcha: AltchaCaptchaConfigResponse,
@@ -716,7 +711,6 @@ const InstancePolicyUpdateSchema = z.object({
export const InstanceConfigUpdateRequest = z.object({
gateway_rollout: GatewayRolloutConfigUpdateRequest.nullish(),
voice_noise_suppression: VoiceNoiseSuppressionConfigUpdateRequest.nullish(),
push_relay: PushRelayConfigUpdateRequest.nullish(),
domain_migration: DomainMigrationConfigUpdateRequest.nullish(),
altcha_captcha: AltchaCaptchaConfigUpdateRequest.nullish(),
@@ -1,459 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG,
INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT,
resolveVoiceNoiseSuppressionAssignment,
resolveVoiceNoiseSuppressionForCall,
VOICE_NOISE_SUPPRESSION_BACKENDS,
type VoiceNoiseSuppressionAssignmentResponse,
type VoiceNoiseSuppressionConfig,
VoiceNoiseSuppressionConfigSchema,
VoiceNoiseSuppressionConfigUpdateRequest,
} from '@fluxer/schema/src/domains/admin/VoiceNoiseSuppressionSchemas';
import {type ExperimentTargeting, experimentBucket} from '@fluxer/schema/src/domains/experiment/ExperimentBucket';
import {describe, expect, test} from 'vitest';
const NO_TARGETING: ExperimentTargeting = {memberGuildIds: new Set(), premium: false};
const TARGETED_USER_ID = '1000000000000000001';
const OTHER_USER_ID = '1000000000000000002';
const GUILD_ID = '2000000000000000001';
const OTHER_GUILD_ID = '2000000000000000002';
describe('voice noise suppression configuration', () => {
test('derives defaults from the schema with independently owned arrays', () => {
const first = VoiceNoiseSuppressionConfigSchema.parse({});
const second = VoiceNoiseSuppressionConfigSchema.parse({});
expect(first).toEqual(DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG);
first.enabled_backends.pop();
first.included_user_ids.push(TARGETED_USER_ID);
first.excluded_user_ids.push(OTHER_USER_ID);
first.guild_overrides.push({guild_id: GUILD_ID, backend: 'rnnoise'});
expect(second).toEqual(DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG);
});
test.each([{}, {enabled: false}, {enabled: undefined}, {suppression_strength: 42}])(
'keeps partial updates free of configuration defaults: %j',
(patch) => {
expect(VoiceNoiseSuppressionConfigUpdateRequest.parse(patch)).toEqual(patch);
},
);
test('does not accept a client-provided configuration version', () => {
expect(VoiceNoiseSuppressionConfigUpdateRequest.parse({config_version: 12})).toEqual({});
});
test.each([
{rollout_basis_points: -1},
{rollout_basis_points: 10001},
{suppression_strength: -1},
{suppression_strength: 101},
{rollout_salt: ' '},
{included_user_ids: ['not-an-id']},
{guild_overrides: [{guild_id: GUILD_ID, backend: 'unknown'}]},
])('applies the same validation to stored configuration and updates: %j', (value) => {
expect(VoiceNoiseSuppressionConfigSchema.safeParse(value).success).toBe(false);
expect(VoiceNoiseSuppressionConfigUpdateRequest.safeParse(value).success).toBe(false);
});
});
function createConfig(overrides: Partial<VoiceNoiseSuppressionConfig> = {}): VoiceNoiseSuppressionConfig {
return {
...DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG,
enabled_backends: [...DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG.enabled_backends],
included_user_ids: [],
excluded_user_ids: [],
guild_overrides: [],
...overrides,
};
}
function createAssignment(
overrides: Partial<VoiceNoiseSuppressionAssignmentResponse> = {},
): VoiceNoiseSuppressionAssignmentResponse {
return {
enabled: true,
config_version: 7,
user_targeted: true,
backend: 'rnnoise',
source: 'canary',
guild_overrides: [],
enabled_backends: [...VOICE_NOISE_SUPPRESSION_BACKENDS],
allow_user_override: false,
suppression_strength: 80,
...overrides,
};
}
function syntheticUserIds(count: number): Array<string> {
const ids: Array<string> = [];
for (let index = 0; index < count; index++) {
ids.push((1400000000000000000n + BigInt(index)).toString());
}
return ids;
}
function targetedUserIds(config: VoiceNoiseSuppressionConfig, userIds: ReadonlyArray<string>): Set<string> {
const targeted = new Set<string>();
for (const userId of userIds) {
if (resolveVoiceNoiseSuppressionAssignment(config, userId, NO_TARGETING).user_targeted) {
targeted.add(userId);
}
}
return targeted;
}
describe('experimentBucket', () => {
test('stays inside the basis point range for every synthetic id', () => {
for (const userId of syntheticUserIds(500)) {
const bucket = experimentBucket(userId, 'voice-ns-v1');
expect(Number.isInteger(bucket)).toBe(true);
expect(bucket).toBeGreaterThanOrEqual(0);
expect(bucket).toBeLessThan(10000);
}
});
test.each([
{userId: TARGETED_USER_ID, salt: 'voice-ns-v1', expected: 8241},
{userId: TARGETED_USER_ID, salt: 'voice-ns-v2', expected: 8500},
{userId: OTHER_USER_ID, salt: 'voice-ns-v1', expected: 5384},
{userId: OTHER_USER_ID, salt: 'voice-ns-v2', expected: 1357},
])('preserves the assignment bucket for $userId with salt $salt', ({userId, salt, expected}) => {
expect(experimentBucket(userId, salt)).toBe(expected);
});
test('changes with the salt for at least most user ids', () => {
const userIds = syntheticUserIds(200);
const changed = userIds.filter(
(userId) => experimentBucket(userId, 'voice-ns-v1') !== experimentBucket(userId, 'voice-ns-v2'),
);
expect(changed.length).toBeGreaterThan(userIds.length - 5);
});
});
describe('resolveVoiceNoiseSuppressionAssignment', () => {
test('returns the inert assignment when the master switch is off', () => {
const config = createConfig({
enabled: false,
config_version: 4,
rollout_basis_points: 10000,
included_user_ids: [TARGETED_USER_ID],
guild_overrides: [{guild_id: GUILD_ID, backend: 'rnnoise'}],
});
expect(resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING)).toEqual({
...INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT,
config_version: 4,
});
});
test('returns the inert assignment for the default config', () => {
expect(resolveVoiceNoiseSuppressionAssignment(createConfig(), TARGETED_USER_ID, NO_TARGETING)).toEqual(
INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT,
);
});
test('denylist beats allowlist', () => {
const config = createConfig({
enabled: true,
included_user_ids: [TARGETED_USER_ID],
excluded_user_ids: [TARGETED_USER_ID],
});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING);
expect(assignment.user_targeted).toBe(false);
expect(assignment.backend).toBeNull();
expect(assignment.source).toBeNull();
});
test('denylist beats canary', () => {
const config = createConfig({
enabled: true,
rollout_basis_points: 10000,
excluded_user_ids: [TARGETED_USER_ID],
});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING);
expect(assignment.user_targeted).toBe(false);
expect(assignment.source).toBeNull();
expect(resolveVoiceNoiseSuppressionAssignment(config, OTHER_USER_ID, NO_TARGETING).user_targeted).toBe(true);
});
test('denylist strips guild overrides and the client-side knobs', () => {
const config = createConfig({
enabled: true,
allow_user_override: true,
rollout_basis_points: 10000,
excluded_user_ids: [TARGETED_USER_ID],
guild_overrides: [{guild_id: GUILD_ID, backend: 'rnnoise'}],
});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING);
expect(assignment.guild_overrides).toEqual([]);
expect(assignment.enabled_backends).toEqual([]);
expect(assignment.allow_user_override).toBe(false);
});
test('allowlist gives source user_rule outside the canary', () => {
const config = createConfig({
enabled: true,
default_backend: 'gtcrn',
rollout_basis_points: 0,
included_user_ids: [TARGETED_USER_ID],
});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING);
expect(assignment.user_targeted).toBe(true);
expect(assignment.backend).toBe('gtcrn');
expect(assignment.source).toBe('user_rule');
});
test.each([
{basisPoints: 0, targeted: false},
{basisPoints: 10000, targeted: true},
])('canary at $basisPoints basis points targets $targeted', ({basisPoints, targeted}) => {
const config = createConfig({enabled: true, rollout_basis_points: basisPoints});
for (const userId of syntheticUserIds(200)) {
const assignment = resolveVoiceNoiseSuppressionAssignment(config, userId, NO_TARGETING);
expect(assignment.user_targeted).toBe(targeted);
expect(assignment.source).toBe(targeted ? 'canary' : null);
}
});
test('raising the rollout basis points only ever adds users', () => {
const userIds = syntheticUserIds(2000);
const atOneThousand = targetedUserIds(createConfig({enabled: true, rollout_basis_points: 1000}), userIds);
const atTwoThousand = targetedUserIds(createConfig({enabled: true, rollout_basis_points: 2000}), userIds);
expect(atOneThousand.size).toBeGreaterThan(0);
for (const userId of atOneThousand) {
expect(atTwoThousand.has(userId)).toBe(true);
}
expect(atTwoThousand.size).toBeGreaterThan(atOneThousand.size);
});
test('the canary set follows the salt', () => {
const userIds = syntheticUserIds(2000);
const first = targetedUserIds(
createConfig({enabled: true, rollout_basis_points: 5000, rollout_salt: 'voice-ns-v1'}),
userIds,
);
const second = targetedUserIds(
createConfig({enabled: true, rollout_basis_points: 5000, rollout_salt: 'voice-ns-v2'}),
userIds,
);
expect(first).not.toEqual(second);
});
test('a default backend missing from enabled_backends yields no backend and no source', () => {
const config = createConfig({
enabled: true,
default_backend: 'deep_filter',
enabled_backends: ['none', 'standard'],
rollout_basis_points: 10000,
included_user_ids: [OTHER_USER_ID],
});
for (const userId of [TARGETED_USER_ID, OTHER_USER_ID]) {
const assignment = resolveVoiceNoiseSuppressionAssignment(config, userId, NO_TARGETING);
expect(assignment.user_targeted).toBe(false);
expect(assignment.backend).toBeNull();
expect(assignment.source).toBeNull();
}
});
test('guild overrides naming a disabled backend are filtered out', () => {
const config = createConfig({
enabled: true,
default_backend: 'standard',
enabled_backends: ['none', 'standard', 'rnnoise'],
rollout_basis_points: 10000,
guild_overrides: [
{guild_id: GUILD_ID, backend: 'rnnoise'},
{guild_id: OTHER_GUILD_ID, backend: 'deep_filter'},
],
});
expect(resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING).guild_overrides).toEqual([
{guild_id: GUILD_ID, backend: 'rnnoise'},
]);
});
test('guild overrides survive the allowlist path too', () => {
const config = createConfig({
enabled: true,
enabled_backends: ['none', 'standard', 'rnnoise'],
included_user_ids: [TARGETED_USER_ID],
guild_overrides: [
{guild_id: GUILD_ID, backend: 'rnnoise'},
{guild_id: OTHER_GUILD_ID, backend: 'gtcrn'},
],
});
expect(resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING).guild_overrides).toEqual([
{guild_id: GUILD_ID, backend: 'rnnoise'},
]);
});
test('echoes the config version on every path', () => {
const config = createConfig({enabled: true, config_version: 11, rollout_basis_points: 10000});
expect(resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING).config_version).toBe(11);
});
});
describe('resolveVoiceNoiseSuppressionForCall', () => {
test('returns null when the master switch is off', () => {
const assignment = createAssignment({
enabled: false,
user_targeted: true,
backend: 'rnnoise',
source: 'user_rule',
allow_user_override: true,
guild_overrides: [{guild_id: GUILD_ID, backend: 'gtcrn'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, 'speex')).toBeNull();
expect(resolveVoiceNoiseSuppressionForCall(assignment, null, null)).toBeNull();
});
test('returns null when nothing targets the user', () => {
const assignment = createAssignment({user_targeted: false, backend: null, source: null});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, null)).toBeNull();
expect(resolveVoiceNoiseSuppressionForCall(assignment, null, null)).toBeNull();
});
test('user_rule beats guild_rule', () => {
const assignment = createAssignment({
backend: 'rnnoise',
source: 'user_rule',
guild_overrides: [{guild_id: GUILD_ID, backend: 'gtcrn'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, null)).toMatchObject({
backend: 'rnnoise',
source: 'user_rule',
});
});
test('guild_rule beats canary', () => {
const assignment = createAssignment({
backend: 'rnnoise',
source: 'canary',
guild_overrides: [{guild_id: GUILD_ID, backend: 'gtcrn'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, null)).toMatchObject({
backend: 'gtcrn',
source: 'guild_rule',
});
});
test('a guild override targets an otherwise untargeted user', () => {
const assignment = createAssignment({
user_targeted: false,
backend: null,
source: null,
guild_overrides: [{guild_id: GUILD_ID, backend: 'speex'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, null)).toMatchObject({
backend: 'speex',
source: 'guild_rule',
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, OTHER_GUILD_ID, null)).toBeNull();
});
test('a DM falls through to the user level result', () => {
const assignment = createAssignment({
backend: 'rnnoise',
source: 'canary',
guild_overrides: [{guild_id: GUILD_ID, backend: 'gtcrn'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, null, null)).toMatchObject({
backend: 'rnnoise',
source: 'canary',
});
});
test.each([
{allowUserOverride: true, preference: 'speex', expectedBackend: 'speex', expectedSource: 'user_override'},
{allowUserOverride: true, preference: 'gtcrn', expectedBackend: 'rnnoise', expectedSource: 'canary'},
{allowUserOverride: false, preference: 'speex', expectedBackend: 'rnnoise', expectedSource: 'canary'},
{allowUserOverride: true, preference: null, expectedBackend: 'rnnoise', expectedSource: 'canary'},
] as const)(
'allow_user_override $allowUserOverride with preference $preference resolves to $expectedSource',
({allowUserOverride, preference, expectedBackend, expectedSource}) => {
const assignment = createAssignment({
backend: 'rnnoise',
source: 'canary',
allow_user_override: allowUserOverride,
enabled_backends: ['none', 'standard', 'speex', 'rnnoise'],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, null, preference)).toMatchObject({
backend: expectedBackend,
source: expectedSource,
});
},
);
test('a user override also outranks a guild rule', () => {
const assignment = createAssignment({
user_targeted: false,
backend: null,
source: null,
allow_user_override: true,
enabled_backends: ['none', 'standard', 'speex'],
guild_overrides: [{guild_id: GUILD_ID, backend: 'standard'}],
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, GUILD_ID, 'speex')).toMatchObject({
backend: 'speex',
source: 'user_override',
});
});
test('carries the client knobs through', () => {
const assignment = createAssignment({
backend: 'rnnoise',
source: 'user_rule',
suppression_strength: 42,
config_version: 19,
});
expect(resolveVoiceNoiseSuppressionForCall(assignment, null, null)).toEqual({
backend: 'rnnoise',
source: 'user_rule',
suppressionStrength: 42,
configVersion: 19,
});
});
});
describe('resolveVoiceNoiseSuppressionAssignment guild targeting', () => {
const INCLUDED_GUILD_ID = '3000000000000000001';
const MEMBER_GUILDS: ExperimentTargeting = {
memberGuildIds: new Set(['3000000000000000009', INCLUDED_GUILD_ID]),
premium: false,
};
test('targets members of an included guild the way it targets included users', () => {
const config = createConfig({enabled: true, included_guild_ids: [INCLUDED_GUILD_ID]});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, MEMBER_GUILDS);
expect(assignment.user_targeted).toBe(true);
expect(assignment.source).toBe('user_rule');
expect(assignment.backend).toBe(config.default_backend);
expect(resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, NO_TARGETING).user_targeted).toBe(false);
});
test('keeps user exclusions ahead of guild membership', () => {
const config = createConfig({
enabled: true,
included_guild_ids: [INCLUDED_GUILD_ID],
excluded_user_ids: [TARGETED_USER_ID],
});
const assignment = resolveVoiceNoiseSuppressionAssignment(config, TARGETED_USER_ID, MEMBER_GUILDS);
expect(assignment.user_targeted).toBe(false);
expect(assignment.source).toBeNull();
});
});
describe('resolveVoiceNoiseSuppressionAssignment premium targeting', () => {
const PREMIUM: ExperimentTargeting = {memberGuildIds: new Set(), premium: true};
test('targets premium users only when the switch is on', () => {
const on = createConfig({enabled: true, include_premium_users: true});
expect(resolveVoiceNoiseSuppressionAssignment(on, TARGETED_USER_ID, PREMIUM)).toMatchObject({
user_targeted: true,
source: 'user_rule',
});
expect(resolveVoiceNoiseSuppressionAssignment(on, TARGETED_USER_ID, NO_TARGETING).user_targeted).toBe(false);
const off = createConfig({enabled: true});
expect(resolveVoiceNoiseSuppressionAssignment(off, TARGETED_USER_ID, PREMIUM).user_targeted).toBe(false);
});
});
@@ -1,221 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {
EXPERIMENT_BUCKET_RESOLUTION,
type ExperimentTargeting,
experimentAudienceIncludes,
experimentBucket,
} from '@fluxer/schema/src/domains/experiment/ExperimentBucket';
import {z} from 'zod';
export const VOICE_NOISE_SUPPRESSION_BACKENDS = [
'none',
'standard',
'gate',
'speex',
'rnnoise',
'gtcrn',
'deep_filter',
] as const;
export type VoiceNoiseSuppressionBackend = (typeof VOICE_NOISE_SUPPRESSION_BACKENDS)[number];
export const VoiceNoiseSuppressionBackendSchema = z.enum(VOICE_NOISE_SUPPRESSION_BACKENDS);
const VOICE_NOISE_SUPPRESSION_ROLLOUT_BASIS_POINTS_MAX = EXPERIMENT_BUCKET_RESOLUTION;
const VOICE_NOISE_SUPPRESSION_MAX_TARGETED_USERS = 1000;
const VOICE_NOISE_SUPPRESSION_MAX_GUILD_OVERRIDES = 200;
const DEFAULT_VOICE_NOISE_SUPPRESSION_SALT = 'voice-ns-v1';
const TargetIdSchema = z.string().regex(/^\d{1,20}$/u);
const TargetedUserIdsSchema = z.array(TargetIdSchema).max(VOICE_NOISE_SUPPRESSION_MAX_TARGETED_USERS);
const VoiceNoiseSuppressionGuildOverrideSchema = z.object({
guild_id: TargetIdSchema,
backend: VoiceNoiseSuppressionBackendSchema,
});
const voiceConfigFields = {
enabled: z.boolean(),
config_version: z.number().int().min(0),
default_backend: VoiceNoiseSuppressionBackendSchema,
enabled_backends: z.array(VoiceNoiseSuppressionBackendSchema).max(VOICE_NOISE_SUPPRESSION_BACKENDS.length),
allow_user_override: z.boolean(),
rollout_basis_points: z.number().int().min(0).max(VOICE_NOISE_SUPPRESSION_ROLLOUT_BASIS_POINTS_MAX),
rollout_salt: z.string().trim().min(1).max(64),
included_user_ids: TargetedUserIdsSchema,
included_guild_ids: TargetedUserIdsSchema,
include_premium_users: z.boolean(),
excluded_user_ids: TargetedUserIdsSchema,
guild_overrides: z.array(VoiceNoiseSuppressionGuildOverrideSchema).max(VOICE_NOISE_SUPPRESSION_MAX_GUILD_OVERRIDES),
suppression_strength: z.number().int().min(0).max(100),
};
export const VoiceNoiseSuppressionConfigSchema = z.object({
enabled: voiceConfigFields.enabled.default(false),
config_version: voiceConfigFields.config_version.default(0),
default_backend: voiceConfigFields.default_backend.default('standard'),
enabled_backends: voiceConfigFields.enabled_backends.default([...VOICE_NOISE_SUPPRESSION_BACKENDS]),
allow_user_override: voiceConfigFields.allow_user_override.default(true),
rollout_basis_points: voiceConfigFields.rollout_basis_points.default(0),
rollout_salt: voiceConfigFields.rollout_salt.default(DEFAULT_VOICE_NOISE_SUPPRESSION_SALT),
included_user_ids: voiceConfigFields.included_user_ids.default([]),
included_guild_ids: voiceConfigFields.included_guild_ids.default([]),
include_premium_users: voiceConfigFields.include_premium_users.default(false),
excluded_user_ids: voiceConfigFields.excluded_user_ids.default([]),
guild_overrides: voiceConfigFields.guild_overrides.default([]),
suppression_strength: voiceConfigFields.suppression_strength.default(80),
});
export type VoiceNoiseSuppressionConfig = z.infer<typeof VoiceNoiseSuppressionConfigSchema>;
export const DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG: VoiceNoiseSuppressionConfig =
VoiceNoiseSuppressionConfigSchema.parse({});
export const VoiceNoiseSuppressionConfigUpdateRequest = z
.object(voiceConfigFields)
.omit({config_version: true})
.partial();
export type VoiceNoiseSuppressionConfigUpdateRequest = z.infer<typeof VoiceNoiseSuppressionConfigUpdateRequest>;
export const VoiceNoiseSuppressionConfigResponse = VoiceNoiseSuppressionConfigSchema;
export type VoiceNoiseSuppressionConfigResponse = z.infer<typeof VoiceNoiseSuppressionConfigResponse>;
const VOICE_NOISE_SUPPRESSION_ASSIGNMENT_SOURCES = ['user_rule', 'canary'] as const;
const VoiceNoiseSuppressionAssignmentSourceSchema = z.enum(VOICE_NOISE_SUPPRESSION_ASSIGNMENT_SOURCES);
export const VoiceNoiseSuppressionAssignmentResponse = z.object({
enabled: voiceConfigFields.enabled,
config_version: z.number().int(),
user_targeted: z.boolean(),
backend: VoiceNoiseSuppressionBackendSchema.nullable(),
source: VoiceNoiseSuppressionAssignmentSourceSchema.nullable(),
guild_overrides: z.array(VoiceNoiseSuppressionGuildOverrideSchema),
enabled_backends: z.array(VoiceNoiseSuppressionBackendSchema),
allow_user_override: voiceConfigFields.allow_user_override,
suppression_strength: voiceConfigFields.suppression_strength,
});
export type VoiceNoiseSuppressionAssignmentResponse = z.infer<typeof VoiceNoiseSuppressionAssignmentResponse>;
export const INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT: VoiceNoiseSuppressionAssignmentResponse = {
enabled: false,
config_version: 0,
user_targeted: false,
backend: null,
source: null,
guild_overrides: [],
enabled_backends: [],
allow_user_override: false,
suppression_strength: DEFAULT_VOICE_NOISE_SUPPRESSION_CONFIG.suppression_strength,
};
export function resolveVoiceNoiseSuppressionAssignment(
config: VoiceNoiseSuppressionConfig,
userId: string,
targeting: ExperimentTargeting,
): VoiceNoiseSuppressionAssignmentResponse {
if (!config.enabled) {
return {
...INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT,
config_version: config.config_version,
};
}
const shared = {
enabled: true,
config_version: config.config_version,
enabled_backends: [...config.enabled_backends],
allow_user_override: config.allow_user_override,
suppression_strength: config.suppression_strength,
};
if (config.excluded_user_ids.includes(userId)) {
return {
...shared,
enabled_backends: [],
allow_user_override: false,
user_targeted: false,
backend: null,
source: null,
guild_overrides: [],
};
}
const backendIsUsable = config.enabled_backends.includes(config.default_backend);
const guildOverrides = config.guild_overrides.filter((override) =>
config.enabled_backends.includes(override.backend),
);
if (config.included_user_ids.includes(userId) || experimentAudienceIncludes(config, targeting)) {
return {
...shared,
user_targeted: backendIsUsable,
backend: backendIsUsable ? config.default_backend : null,
source: backendIsUsable ? 'user_rule' : null,
guild_overrides: guildOverrides,
};
}
const inCanary = experimentBucket(userId, config.rollout_salt) < config.rollout_basis_points;
return {
...shared,
user_targeted: inCanary && backendIsUsable,
backend: inCanary && backendIsUsable ? config.default_backend : null,
source: inCanary && backendIsUsable ? 'canary' : null,
guild_overrides: guildOverrides,
};
}
export const VOICE_NOISE_SUPPRESSION_RESOLUTION_SOURCES = [
'user_rule',
'guild_rule',
'canary',
'user_override',
] as const;
export type VoiceNoiseSuppressionResolutionSource = (typeof VOICE_NOISE_SUPPRESSION_RESOLUTION_SOURCES)[number];
export interface VoiceNoiseSuppressionResolution {
backend: VoiceNoiseSuppressionBackend;
source: VoiceNoiseSuppressionResolutionSource;
suppressionStrength: number;
configVersion: number;
}
function resolveVoiceNoiseSuppressionTarget(
assignment: VoiceNoiseSuppressionAssignmentResponse,
guildId: string | null,
): Pick<VoiceNoiseSuppressionResolution, 'backend' | 'source'> | null {
if (assignment.source === 'user_rule' && assignment.backend != null) {
return {backend: assignment.backend, source: 'user_rule'};
}
if (guildId != null) {
const guildOverride = assignment.guild_overrides.find((override) => override.guild_id === guildId);
if (guildOverride) return {backend: guildOverride.backend, source: 'guild_rule'};
}
if (!assignment.user_targeted) return null;
if (assignment.backend == null) return null;
if (assignment.source == null) return null;
return {backend: assignment.backend, source: assignment.source};
}
export function resolveVoiceNoiseSuppressionForCall(
assignment: VoiceNoiseSuppressionAssignmentResponse,
guildId: string | null,
userPreference: VoiceNoiseSuppressionBackend | null,
): VoiceNoiseSuppressionResolution | null {
if (!assignment.enabled) return null;
const targeted = resolveVoiceNoiseSuppressionTarget(assignment, guildId);
if (targeted == null) return null;
const shared = {
suppressionStrength: assignment.suppression_strength,
configVersion: assignment.config_version,
};
if (
assignment.allow_user_override &&
userPreference != null &&
assignment.enabled_backends.includes(userPreference)
) {
return {...shared, backend: userPreference, source: 'user_override'};
}
return {...shared, backend: targeted.backend, source: targeted.source};
}
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {experimentBucket} from '@fluxer/schema/src/domains/experiment/ExperimentBucket';
import {describe, expect, test} from 'vitest';
const TARGETED_USER_ID = '1000000000000000001';
const OTHER_USER_ID = '1000000000000000002';
function syntheticUserIds(count: number): Array<string> {
const ids: Array<string> = [];
for (let index = 0; index < count; index++) {
ids.push((1400000000000000000n + BigInt(index)).toString());
}
return ids;
}
describe('experimentBucket', () => {
test('stays inside the basis point range for every synthetic id', () => {
for (const userId of syntheticUserIds(500)) {
const bucket = experimentBucket(userId, 'voice-ns-v1');
expect(Number.isInteger(bucket)).toBe(true);
expect(bucket).toBeGreaterThanOrEqual(0);
expect(bucket).toBeLessThan(10000);
}
});
test.each([
{userId: TARGETED_USER_ID, salt: 'voice-ns-v1', expected: 8241},
{userId: TARGETED_USER_ID, salt: 'voice-ns-v2', expected: 8500},
{userId: OTHER_USER_ID, salt: 'voice-ns-v1', expected: 5384},
{userId: OTHER_USER_ID, salt: 'voice-ns-v2', expected: 1357},
])('preserves the assignment bucket for $userId with salt $salt', ({userId, salt, expected}) => {
expect(experimentBucket(userId, salt)).toBe(expected);
});
test('changes with the salt for at least most user ids', () => {
const userIds = syntheticUserIds(200);
const changed = userIds.filter(
(userId) => experimentBucket(userId, 'voice-ns-v1') !== experimentBucket(userId, 'voice-ns-v2'),
);
expect(changed.length).toBeGreaterThan(userIds.length - 5);
});
});
@@ -9,10 +9,6 @@ import {
INERT_PROFILE_TIMEZONE_ASSIGNMENT,
ProfileTimezoneAssignmentResponse,
} from '@fluxer/schema/src/domains/admin/ProfileTimezoneSchemas';
import {
INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT,
VoiceNoiseSuppressionAssignmentResponse,
} from '@fluxer/schema/src/domains/admin/VoiceNoiseSuppressionSchemas';
import {z} from 'zod';
export const EXPERIMENT_MIN_POLL_INTERVAL_SECONDS = 60;
@@ -50,7 +46,6 @@ export const ExperimentDeliveryConfigResponse = ExperimentDeliveryConfigSchema;
export type ExperimentDeliveryConfigResponse = z.infer<typeof ExperimentDeliveryConfigResponse>;
const ExperimentAssignmentsSchema = z.object({
voice_noise_suppression: VoiceNoiseSuppressionAssignmentResponse.optional(),
domain_migration: DomainMigrationAssignmentResponse.optional(),
altcha_captcha: AltchaCaptchaAssignmentResponse.optional(),
profile_timezone: ProfileTimezoneAssignmentResponse.optional(),
@@ -70,12 +65,6 @@ export const INERT_EXPERIMENT_ASSIGNMENTS_RESPONSE: ExperimentAssignmentsRespons
assignments: {},
};
export function readVoiceNoiseSuppressionAssignment(
response: ExperimentAssignmentsResponse,
): VoiceNoiseSuppressionAssignmentResponse {
return response.assignments.voice_noise_suppression ?? INERT_VOICE_NOISE_SUPPRESSION_ASSIGNMENT;
}
export function readDomainMigrationAssignment(
response: ExperimentAssignmentsResponse,
): DomainMigrationAssignmentResponse {
@@ -42,8 +42,6 @@ function sameMicrophoneOptions(
a?.echoCancellation === b?.echoCancellation &&
a?.noiseSuppression === b?.noiseSuppression &&
a?.autoGainControl === b?.autoGainControl &&
a?.deepFilter === b?.deepFilter &&
a?.deepFilterNoiseReductionLevel === b?.deepFilterNoiseReductionLevel &&
a?.maxBitrateBps === b?.maxBitrateBps
);
}
@@ -179,8 +179,6 @@ export interface VoiceEngineV2MicrophoneOptions {
echoCancellation?: boolean;
noiseSuppression?: boolean;
autoGainControl?: boolean;
deepFilter?: boolean;
deepFilterNoiseReductionLevel?: number;
maxBitrateBps?: number;
}