refactor(backend): AiServiceをSensitiveMediaDetectionServiceにリネーム

NSFW推論を本体で行わなくなり、外部サービス (sensitive-detector) への
アダプタになったため、AiServiceという名前が実態と合わなくなっていた。
ログドメインも `ai` から `sensitive-media-detection` に変更する。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kakkokari-gtyih
2026-07-31 19:27:36 +09:00
parent 3ec88c35aa
commit 85c64eeb0a
7 changed files with 29 additions and 25 deletions
+2 -1
View File
@@ -7,7 +7,8 @@
-
### Server
-
- Enhance: センシティブメディア判定のログドメイン名を `ai` から `sensitive-media-detection` に変更
- `logging.domains``ai` を指定していた場合は設定の更新が必要です
## 2026.7.0
+6 -6
View File
@@ -18,7 +18,7 @@ import { FlashService } from '@/core/FlashService.js';
import { ChannelMutingService } from '@/core/ChannelMutingService.js';
import { AccountMoveService } from './AccountMoveService.js';
import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
import { SensitiveMediaDetectionService } from './SensitiveMediaDetectionService.js';
import { AnnouncementService } from './AnnouncementService.js';
import { AntennaService } from './AntennaService.js';
import { AchievementService } from './AchievementService.js';
@@ -164,7 +164,7 @@ const $AbuseReportService: Provider = { provide: 'AbuseReportService', useExisti
const $AbuseReportNotificationService: Provider = { provide: 'AbuseReportNotificationService', useExisting: AbuseReportNotificationService };
const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService };
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $SensitiveMediaDetectionService: Provider = { provide: 'SensitiveMediaDetectionService', useExisting: SensitiveMediaDetectionService };
const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
@@ -317,7 +317,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AbuseReportNotificationService,
AccountMoveService,
AccountUpdateService,
AiService,
SensitiveMediaDetectionService,
AnnouncementService,
AntennaService,
AchievementService,
@@ -468,7 +468,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AbuseReportNotificationService,
$AccountMoveService,
$AccountUpdateService,
$AiService,
$SensitiveMediaDetectionService,
$AnnouncementService,
$AntennaService,
$AchievementService,
@@ -619,7 +619,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AbuseReportNotificationService,
AccountMoveService,
AccountUpdateService,
AiService,
SensitiveMediaDetectionService,
AnnouncementService,
AntennaService,
AchievementService,
@@ -769,7 +769,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AbuseReportNotificationService,
$AccountMoveService,
$AccountUpdateService,
$AiService,
$SensitiveMediaDetectionService,
$AnnouncementService,
$AntennaService,
$AchievementService,
+5 -5
View File
@@ -16,12 +16,12 @@ import probeImageSize from 'probe-image-size';
import { sharpBmp } from '@misskey-dev/sharp-read-bmp';
import * as blurhash from 'blurhash';
import { createTempDir } from '@/misc/create-temp.js';
import { AiService } from '@/core/AiService.js';
import { SensitiveMediaDetectionService } from '@/core/SensitiveMediaDetectionService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { isMimeImage } from '@/misc/is-mime-image.js';
import type { Prediction } from '@/core/AiService.js';
import type { Prediction } from '@/core/SensitiveMediaDetectionService.js';
export type FileInfo = {
size: number;
@@ -54,7 +54,7 @@ export class FileInfoService {
private logger: Logger;
constructor(
private aiService: AiService,
private sensitiveMediaDetectionService: SensitiveMediaDetectionService,
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('file-info');
@@ -266,7 +266,7 @@ export class FileInfoService {
fs.promises.unlink(path);
}
}
const predictions = await this.aiService.detectSensitiveMany(frameBuffers);
const predictions = await this.sensitiveMediaDetectionService.detectSensitiveMany(frameBuffers);
const results = predictions.filter((x): x is Prediction[] => x != null).map(x => judgePrediction(x));
// 判定に成功したフレームが 0 件のとき(接続先未設定・通信失敗等)は、
// Math.ceil(0) との比較が 0 >= 0 で真になり全動画がセンシティブ扱いになってしまうため、
@@ -291,7 +291,7 @@ export class FileInfoService {
.flatten({ background: { r: 119, g: 119, b: 119 } }) // 透過部分を18%グレーで塗りつぶす
.png()
.toBuffer();
const result = await this.aiService.detectSensitive(png);
const result = await this.sensitiveMediaDetectionService.detectSensitive(png);
if (result) {
[sensitive, porn] = judgePrediction(result);
}
@@ -68,8 +68,11 @@ function isDetectImagesResponse(v: unknown): v is DetectImagesResponse {
// サイドカーの判定エンドポイント。baseUrl にパスプレフィックスがあっても連結できるよう先頭スラッシュは付けない。
const DETECT_IMAGES_PATH = 'v1/detect-images';
/**
* (NSFW検出) (sensitive-detector)
*/
@Injectable()
export class AiService {
export class SensitiveMediaDetectionService {
private logger: Logger;
constructor(
@@ -79,7 +82,7 @@ export class AiService {
private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('ai');
this.logger = this.loggerService.getLogger('sensitive-media-detection');
}
/**
@@ -14,7 +14,7 @@ import { mockDeep } from 'vitest-mock-extended';
import { GlobalModule } from '@/GlobalModule.js';
import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
//import { DI } from '@/di-symbols.js';
import { AiService } from '@/core/AiService.js';
import { SensitiveMediaDetectionService } from '@/core/SensitiveMediaDetectionService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing';
@@ -41,13 +41,13 @@ describe('FileInfoService', () => {
GlobalModule,
],
providers: [
AiService,
SensitiveMediaDetectionService,
LoggerService,
FileInfoService,
],
})
.useMocker((token) => {
//if (token === AiService) {
//if (token === SensitiveMediaDetectionService) {
// return { };
//}
if (typeof token === 'function') {
@@ -7,7 +7,7 @@ import { describe, test, expect, vi, beforeEach } from 'vitest';
import type { MiMeta } from '@/models/_.js';
import type { HttpRequestService } from '@/core/HttpRequestService.js';
import type { LoggerService } from '@/core/LoggerService.js';
import { AiService, type Prediction } from '@/core/AiService.js';
import { SensitiveMediaDetectionService, type Prediction } from '@/core/SensitiveMediaDetectionService.js';
const sendMock = vi.fn();
@@ -18,13 +18,13 @@ const DEFAULT_META = {
sensitiveMediaDetectionMaxImagesPerRequest: 4,
};
function makeService(metaOverrides: Partial<typeof DEFAULT_META> = {}): AiService {
function makeService(metaOverrides: Partial<typeof DEFAULT_META> = {}): SensitiveMediaDetectionService {
const meta = { ...DEFAULT_META, ...metaOverrides } as unknown as MiMeta;
const httpRequestService = { send: sendMock } as unknown as HttpRequestService;
const loggerService = {
getLogger: () => ({ warn: () => {}, error: () => {}, info: () => {} }),
} as unknown as LoggerService;
return new AiService(meta, httpRequestService, loggerService);
return new SensitiveMediaDetectionService(meta, httpRequestService, loggerService);
}
function neutral(): Prediction[] {
@@ -42,7 +42,7 @@ function okResponse(results: unknown[]) {
const buf = (s: string) => Buffer.from(s);
describe('AiService', () => {
describe('SensitiveMediaDetectionService', () => {
beforeEach(() => {
sendMock.mockReset();
});
@@ -11,7 +11,7 @@ import { describe, expect, test, beforeAll, afterAll, afterEach } from 'vitest';
import sharp from 'sharp';
import { DataSource, type Repository } from 'typeorm';
import { initTestDb, randomString } from '../../utils.js';
import type { AiService } from '@/core/AiService.js';
import type { SensitiveMediaDetectionService } from '@/core/SensitiveMediaDetectionService.js';
import { DownloadService } from '@/core/DownloadService.js';
import { FileInfoService } from '@/core/FileInfoService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
@@ -147,11 +147,11 @@ describe('FileServerService', () => {
driveFilesRepository = db.getRepository(MiDriveFile);
const loggerService = new LoggerService();
const aiService = {
const sensitiveMediaDetectionService = {
detectSensitive: async () => null,
detectSensitiveMany: async (sources: Buffer[]) => sources.map(() => null),
} as unknown as AiService;
const fileInfoService = new FileInfoService(aiService, loggerService);
} as unknown as SensitiveMediaDetectionService;
const fileInfoService = new FileInfoService(sensitiveMediaDetectionService, loggerService);
const httpRequestService = new HttpRequestService(config);
const downloadService = new DownloadService(config, httpRequestService, loggerService);
const imageProcessingService = new ImageProcessingService();