Skip to content

Latest commit

ย 

History

History
453 lines (344 loc) ยท 20 KB

File metadata and controls

453 lines (344 loc) ยท 20 KB

MFM - ะขะตั…ะฝะธั‡ะตัะบะธะน ะพั‚ั‡ั‘ั‚

ะžะฑะทะพั€ ะฟั€ะพะตะบั‚ะฐ

MFM (Media Moderation Framework) โ€” ัั‚ะพ ัะธัั‚ะตะผะฐ ะฝะฐ ัะทั‹ะบะต Go ะดะปั ะพะฑะฝะฐั€ัƒะถะตะฝะธั NSFW-ะบะพะฝั‚ะตะฝั‚ะฐ (Not Safe For Work) ะฒ ะผะตะดะธะฐั„ะฐะนะปะฐั… ั ะธัะฟะพะปัŒะทะพะฒะฐะฝะธะตะผ ะผะพะดะตะปะตะน ะธัะบัƒััั‚ะฒะตะฝะฝะพะณะพ ะธะฝั‚ะตะปะปะตะบั‚ะฐ ะธ ะผะฐัˆะธะฝะฝะพะณะพ ะพะฑัƒั‡ะตะฝะธั.


ะœะตั‚ะฐะธะฝั„ะพั€ะผะฐั†ะธั ะฟั€ะพะตะบั‚ะฐ

ะŸะฐั€ะฐะผะตั‚ั€ ะ—ะฝะฐั‡ะตะฝะธะต
ะœะพะดัƒะปัŒ github.com/comerc/mfm
ะ’ะตั€ัะธั Go 1.24.2
ะขะตะบัƒั‰ะฐั ะฒะตั‚ะบะฐ main
ะขะธะฟ ะปะธั†ะตะฝะทะธะธ ะะต ัƒะบะฐะทะฐะฝ

ะั€ั…ะธั‚ะตะบั‚ัƒั€ะฐ ะฟั€ะพะตะบั‚ะฐ

ะกั‚ั€ัƒะบั‚ัƒั€ะฐ ะดะธั€ะตะบั‚ะพั€ะธะน

mfm/
โ”œโ”€โ”€ cmd/server/              # ะขะพั‡ะบะฐ ะฒั…ะพะดะฐ ะฟั€ะธะปะพะถะตะฝะธั
โ”‚   โ””โ”€โ”€ main.go              # ะ“ะปะฐะฒะฝั‹ะน ั„ะฐะนะป ะฟั€ะธะปะพะถะตะฝะธั
โ”œโ”€โ”€ internal/                # ะ’ะฝัƒั‚ั€ะตะฝะฝะธะน ะบะพะด (ะฝะต ะฟะตั€ะตะธัะฟะพะปัŒะทัƒะตะผั‹ะน)
โ”‚   โ”œโ”€โ”€ repo/                # ะกะปะพะน ั€ะตะฟะพะทะธั‚ะพั€ะธะตะฒ (ะดะพัั‚ัƒะฟ ะบ ะดะฐะฝะฝั‹ะผ)
โ”‚   โ”‚   โ”œโ”€โ”€ media_reader/    # ะงั‚ะตะฝะธะต ะธ ะพะฑั€ะฐะฑะพั‚ะบะฐ ะผะตะดะธะฐั„ะฐะนะปะพะฒ
โ”‚   โ”‚   โ”œโ”€โ”€ open_runner/     # ะ—ะฐะฟัƒัะบ ะผะพะดะตะปะธ OpenNSFW2
โ”‚   โ”‚   โ””โ”€โ”€ vit_runner/      # ะ—ะฐะฟัƒัะบ ะผะพะดะตะปะธ Vision Transformer
โ”‚   โ””โ”€โ”€ service/             # ะกะปะพะน ัะตั€ะฒะธัะพะฒ (ะฑะธะทะฝะตั-ะปะพะณะธะบะฐ)
โ”‚       โ””โ”€โ”€ moderation/      # ะกะตั€ะฒะธั ะผะพะดะตั€ะฐั†ะธะธ ะบะพะฝั‚ะตะฝั‚ะฐ
โ”œโ”€โ”€ pkg/                     # ะŸะตั€ะตะธัะฟะพะปัŒะทัƒะตะผั‹ะต ะฟะฐะบะตั‚ั‹
โ”‚   โ”œโ”€โ”€ onnxinit/            # ะ˜ะฝะธั†ะธะฐะปะธะทะฐั†ะธั ONNX Runtime
โ”‚   โ””โ”€โ”€ utils/               # ะžะฑั‰ะธะต ัƒั‚ะธะปะธั‚ั‹
โ”‚       โ”œโ”€โ”€ port.go          # ะŸั€ะพะฒะตั€ะบะฐ ะดะพัั‚ัƒะฟะฝะพัั‚ะธ ะฟะพั€ั‚ะฐ
โ”‚       โ””โ”€โ”€ env.go           # ะ—ะฐะณั€ัƒะทะบะฐ ะฟะตั€ะตะผะตะฝะฝั‹ั… ะพะบั€ัƒะถะตะฝะธั
โ”œโ”€โ”€ assets/                  # ะœะตะดะธะฐ-ะฐะบั‚ะธะฒั‹ ะธ ONNX-ะผะพะดะตะปะธ
โ”‚   โ”œโ”€โ”€ onnx/                # ะคะฐะนะปั‹ ะผะพะดะตะปะตะน
โ”‚   โ”œโ”€โ”€ *.png                # ะขะตัั‚ะพะฒั‹ะต ะธะทะพะฑั€ะฐะถะตะฝะธั
โ”‚   โ””โ”€โ”€ video*.mp4           # ะขะตัั‚ะพะฒั‹ะต ะฒะธะดะตะพ
โ”œโ”€โ”€ test/                    # ะ˜ะฝั‚ะตะณั€ะฐั†ะธะพะฝะฝั‹ะต ั‚ะตัั‚ั‹
โ”œโ”€โ”€ doc/                     # ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั
โ”‚   โ””โ”€โ”€ models/              # ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั ะฟะพ ะผะพะดะตะปัะผ
โ”œโ”€โ”€ script/                  # ะ’ัะฟะพะผะพะณะฐั‚ะตะปัŒะฝั‹ะต ัะบั€ะธะฟั‚ั‹
โ”œโ”€โ”€ .env.example             # ะŸั€ะธะผะตั€ ะบะพะฝั„ะธะณัƒั€ะฐั†ะธะธ
โ”œโ”€โ”€ Taskfile.yml             # ะะฒั‚ะพะผะฐั‚ะธะทะฐั†ะธั ัะฑะพั€ะบะธ
โ”œโ”€โ”€ .golangci.yml            # ะšะพะฝั„ะธะณัƒั€ะฐั†ะธั ะปะธะฝั‚ะตั€ะฐ
โ””โ”€โ”€ .mockery.yaml            # ะšะพะฝั„ะธะณัƒั€ะฐั†ะธั ะณะตะฝะตั€ะฐั†ะธะธ ะผะพะบะพะฒ

Go-ั„ะฐะนะปั‹ ะฟั€ะพะตะบั‚ะฐ

ะคะฐะนะป ะžะฟะธัะฐะฝะธะต
cmd/server/main.go ะขะพั‡ะบะฐ ะฒั…ะพะดะฐ, ะฝะฐัั‚ั€ะพะนะบะฐ HTTP-ัะตั€ะฒะตั€ะฐ
internal/service/moderation/service.go ะžัะฝะพะฒะฝะพะน ัะตั€ะฒะธั ะผะพะดะตั€ะฐั†ะธะธ
internal/repo/media_reader/repo.go ะžะฑั€ะฐะฑะพั‚ะบะฐ ะผะตะดะธะฐั„ะฐะนะปะพะฒ
internal/repo/open_runner/open_runner.go ะ ะฐะฝะฝะตั€ OpenNSFW2
internal/repo/vit_runner/vit_runner.go ะ ะฐะฝะฝะตั€ Vision Transformer
pkg/onnxinit/onnxinit.go ะ˜ะฝะธั†ะธะฐะปะธะทะฐั†ะธั ONNX Runtime
pkg/utils/port.go ะŸั€ะพะฒะตั€ะบะฐ ะฟะพั€ั‚ะฐ
pkg/utils/env.go ะ—ะฐะณั€ัƒะทะบะฐ .env
test/moderation_test.go ะ˜ะฝั‚ะตะณั€ะฐั†ะธะพะฝะฝั‹ะต ั‚ะตัั‚ั‹

ะ—ะฐะฒะธัะธะผะพัั‚ะธ ะธ ั‚ะตั…ะฝะพะปะพะณะธะธ

ะžัะฝะพะฒะฝั‹ะต ะทะฐะฒะธัะธะผะพัั‚ะธ (go.mod)

ะŸะฐะบะตั‚ ะ’ะตั€ัะธั ะะฐะทะฝะฐั‡ะตะฝะธะต
github.com/joho/godotenv v1.5.1 ะ—ะฐะณั€ัƒะทะบะฐ ะฟะตั€ะตะผะตะฝะฝั‹ั… ะพะบั€ัƒะถะตะฝะธั
github.com/stretchr/testify v1.10.0 ะขะตัั‚ะธั€ะพะฒะฐะฝะธะต ะธ ะผะพะบะธ
github.com/yalue/onnxruntime_go v1.25.0 ONNX Runtime ะดะปั Go
go.uber.org/zap v1.27.1 ะกั‚ั€ัƒะบั‚ัƒั€ะธั€ะพะฒะฐะฝะฝะพะต ะปะพะณะธั€ะพะฒะฐะฝะธะต
github.com/samber/slog-zap/v2 v2.6.2 ะ˜ะฝั‚ะตะณั€ะฐั†ะธั slog ั Zap

ะ’ะฝะตัˆะฝะธะต ัะธัั‚ะตะผะฝั‹ะต ะทะฐะฒะธัะธะผะพัั‚ะธ

ะšะพะผะฟะพะฝะตะฝั‚ ะะฐะทะฝะฐั‡ะตะฝะธะต
ONNX Runtime ะ’ั‹ะฟะพะปะฝะตะฝะธะต ML-ะผะพะดะตะปะตะน
FFmpeg ะžะฑั€ะฐะฑะพั‚ะบะฐ ะฒะธะดะตะพั„ะฐะนะปะพะฒ

ะ˜ะฝัั‚ั€ัƒะผะตะฝั‚ั‹ ั€ะฐะทั€ะฐะฑะพั‚ะบะธ

ะ˜ะฝัั‚ั€ัƒะผะตะฝั‚ ะะฐะทะฝะฐั‡ะตะฝะธะต
Task ะะฒั‚ะพะผะฐั‚ะธะทะฐั†ะธั ะทะฐะดะฐั‡ (lint, test, run)
Mockery ะ“ะตะฝะตั€ะฐั†ะธั mock-ะพะฑัŠะตะบั‚ะพะฒ
GolangCI-Lint ะšะพะผะฟะปะตะบัะฝะฐั ะฟั€ะพะฒะตั€ะบะฐ ะบะพะดะฐ

ะŸะพะดั€ะพะฑะฝะพะต ะพะฟะธัะฐะฝะธะต ะบะพะผะฟะพะฝะตะฝั‚ะพะฒ

1. ะขะพั‡ะบะฐ ะฒั…ะพะดะฐ (cmd/server/main.go)

ะ“ะปะฐะฒะฝั‹ะน ั„ะฐะนะป ะฟั€ะธะปะพะถะตะฝะธั ะฒั‹ะฟะพะปะฝัะตั‚:

  1. ะ—ะฐะณั€ัƒะทะบัƒ ะฟะตั€ะตะผะตะฝะฝั‹ั… ะพะบั€ัƒะถะตะฝะธั ะธะท .env
  2. ะ˜ะฝะธั†ะธะฐะปะธะทะฐั†ะธัŽ ONNX Runtime
  3. ะะฐัั‚ั€ะพะนะบัƒ ัั‚ั€ัƒะบั‚ัƒั€ะธั€ะพะฒะฐะฝะฝะพะณะพ ะปะพะณะธั€ะพะฒะฐะฝะธั (Zap + slog)
  4. ะกะพะทะดะฐะฝะธะต ะทะฐะฒะธัะธะผะพัั‚ะตะน (media reader, model runners)
  5. ะ—ะฐะฟัƒัะบ HTTP-ัะตั€ะฒะตั€ะฐ ั ัะฝะดะฟะพะธะฝั‚ะฐะผะธ:
    • POST /moderate โ€” ะผะพะดะตั€ะฐั†ะธั ะบะพะฝั‚ะตะฝั‚ะฐ
    • GET /live โ€” health-check
  6. Graceful shutdown ะฟั€ะธ ะฟะพะปัƒั‡ะตะฝะธะธ ัะธะณะฝะฐะปะพะฒ SIGINT/SIGTERM

ะะฐัั‚ั€ะพะนะบะธ HTTP-ัะตั€ะฒะตั€ะฐ:

ReadTimeout:  15 * time.Second
WriteTimeout: 15 * time.Second
IdleTimeout:  60 * time.Second

2. ะกะตั€ะฒะธั ะผะพะดะตั€ะฐั†ะธะธ (internal/service/moderation/service.go)

ะžัะฝะพะฒะฝะพะน ะฑะธะทะฝะตั-ะปะพะณะธั‡ะตัะบะธะน ะบะพะผะฟะพะฝะตะฝั‚:

ะ˜ะฝั‚ะตั€ั„ะตะนัั‹:

type mediaReader interface {
    Read(filePath string) ([][]byte, error)
}

type modelRunner interface {
    Infer(data [][]byte) ([]float32, error)
}

ะะปะณะพั€ะธั‚ะผ ั€ะฐะฑะพั‚ั‹:

  1. ะงะธั‚ะฐะตั‚ ะผะตะดะธะฐั„ะฐะนะปั‹ ั‡ะตั€ะตะท mediaReader
  2. ะะณั€ะตะณะธั€ัƒะตั‚ ะฒัะต ั„ั€ะตะนะผั‹ ะฒ ะพะดะธะฝ ะฑะฐั‚ั‡
  3. ะ’ั‹ะฟะพะปะฝัะตั‚ ะธะฝั„ะตั€ะตะฝั ะดะปั ะฒัะตั… ะผะพะดะตะปะตะน ะฟะฐั€ะฐะปะปะตะปัŒะฝะพ
  4. ะ”ะปั ะธะทะพะฑั€ะฐะถะตะฝะธะน โ€” ะฑะตั€ั‘ั‚ ะตะดะธะฝัั‚ะฒะตะฝะฝั‹ะน ั€ะตะทัƒะปัŒั‚ะฐั‚
  5. ะ”ะปั ะฒะธะดะตะพ โ€” ะฑะตั€ั‘ั‚ ะผะฐะบัะธะผะฐะปัŒะฝั‹ะน ัั‡ั‘ั‚ ัั€ะตะดะธ ะฒัะตั… ั„ั€ะตะนะผะพะฒ
  6. ะžะฑัŠะตะดะธะฝัะตั‚ ั€ะตะทัƒะปัŒั‚ะฐั‚ั‹ ะฒัะตั… ะผะพะดะตะปะตะน (ะผะฐะบัะธะผัƒะผ)

3. Media Reader (internal/repo/media_reader/repo.go)

ะžะฑั€ะฐะฑะฐั‚ั‹ะฒะฐะตั‚ ะผะตะดะธะฐั„ะฐะนะปั‹ ะดะปั ะฟะพะดะฐั‡ะธ ะฒ ะผะพะดะตะปะธ:

ะขะธะฟั‹ ะบะพะฝั‚ะตะฝั‚ะฐ:

const (
    contentTypeUnknown ContentType = "unknown"
    contentTypeImage   ContentType = "image"
    contentTypeVideo   ContentType = "video"
)

ะžะฑั€ะฐะฑะพั‚ะบะฐ ะธะทะพะฑั€ะฐะถะตะฝะธะน:

  • ะ”ะตะบะพะดะธั€ะพะฒะฐะฝะธะต ะธะท ะปัŽะฑะพะณะพ ั„ะพั€ะผะฐั‚ะฐ
  • ะ˜ะทะผะตะฝะตะฝะธะต ั€ะฐะทะผะตั€ะฐ ะดะพ 224x224 (nearest-neighbor)
  • ะšะพะฝะฒะตั€ั‚ะฐั†ะธั ะฒ RGB24 (224 ร— 224 ร— 3 = 150,528 ะฑะฐะนั‚)

ะžะฑั€ะฐะฑะพั‚ะบะฐ ะฒะธะดะตะพ:

  • ะ˜ะทะฒะปะตั‡ะตะฝะธะต ะบะฐะดั€ะพะฒ ั‡ะตั€ะตะท FFmpeg (2 fps)
  • ะ˜ะทะผะตะฝะตะฝะธะต ั€ะฐะทะผะตั€ะฐ ะดะพ 224x224 ั ะพะฑั€ะตะทะบะพะน
  • ะšะพะฝะฒะตั€ั‚ะฐั†ะธั ะฒ RGB24

FFmpeg ะบะพะผะฐะฝะดะฐ:

ffmpeg -i <input> \
  -vf "fps=2,scale=224:224:force_original_aspect_ratio=increase,crop=224:224" \
  -f rawvideo \
  -pix_fmt rgb24 \
  pipe:1

4. Model Runners

OpenNSFW2 Runner (internal/repo/open_runner/open_runner.go)

ะœะพะดะตะปัŒ: OpenNSFW2

  • ะ’ั…ะพะด: [batch_size, 224, 224, 3] (float32, ะฝะพั€ะผะฐะปะธะทะพะฒะฐะฝะพ 0-1)
  • ะ’ั‹ั…ะพะด: [batch_size, 1] (float32, ะฒะตั€ะพัั‚ะฝะพัั‚ัŒ NSFW)

ViT Runner (internal/repo/vit_runner/vit_runner.go)

ะœะพะดะตะปัŒ: Vision Transformer

  • ะ’ั…ะพะด: [batch_size, 224, 224, 3] (float32, ะฝะพั€ะผะฐะปะธะทะพะฒะฐะฝะพ 0-1)
  • ะ’ั‹ั…ะพะด: [batch_size, 2] (float32, ะฟะฐั€ะฐ: normal, nsfw)

ะšะพะฝั„ะธะณัƒั€ะฐั†ะธั

ะŸะตั€ะตะผะตะฝะฝั‹ะต ะพะบั€ัƒะถะตะฝะธั (.env)

ะŸะตั€ะตะผะตะฝะฝะฐั ะžะฟะธัะฐะฝะธะต
HTTP_PORT ะŸะพั€ั‚ HTTP-ัะตั€ะฒะตั€ะฐ (ะฟะพ ัƒะผะพะปั‡ะฐะฝะธัŽ 7171)
MODEL_OPEN ะะฑัะพะปัŽั‚ะฝั‹ะน ะฟัƒั‚ัŒ ะบ ะผะพะดะตะปะธ OpenNSFW2
MODEL_VIT ะะฑัะพะปัŽั‚ะฝั‹ะน ะฟัƒั‚ัŒ ะบ ะผะพะดะตะปะธ ViT

ะŸั€ะธะผะตั€ ั„ะฐะนะปะฐ (.env.example)

# Port for the HTTP server
HTTP_PORT=7171

# Model paths (absolute paths required)
MODEL_OPEN=/absolute/path/to/assets/onnx/opennsfw2.onnx
MODEL_VIT=/absolute/path/to/assets/onnx/vit_nsfw.onnx

API

ะญะฝะดะฟะพะธะฝั‚ั‹

POST /moderate

ะœะพะดะตั€ะฐั†ะธั ะผะตะดะธะฐั„ะฐะนะปะพะฒ.

ะขะตะปะพ ะทะฐะฟั€ะพัะฐ: (ะพะถะธะดะฐะตั‚ัั JSON ั ะฟัƒั‚ัะผะธ ะบ ั„ะฐะนะปะฐะผ)

ะžั‚ะฒะตั‚:

{
  "status": "moderation service ready"
}

ะŸั€ะธะผะตั‡ะฐะฝะธะต: ะŸะพะปะฝะฐั ั€ะตะฐะปะธะทะฐั†ะธั ัะฝะดะฟะพะธะฝั‚ะฐ ะฝะฐั…ะพะดะธั‚ัั ะฒ ั€ะฐะทั€ะฐะฑะพั‚ะบะต (TODO ะฒ ะบะพะดะต).

GET /live

Health-check endpoint.

ะกั‚ะฐั‚ัƒั: 200 OK


ะขะตัั‚ะธั€ะพะฒะฐะฝะธะต

ะŸะพะบั€ั‹ั‚ะธะต ั‚ะตัั‚ะฐะผะธ

ะขะธะฟ ั‚ะตัั‚ะพะฒ ะคะฐะนะปั‹
ะฎะฝะธั‚-ั‚ะตัั‚ั‹ *_test.go ะฒ ะบะฐะถะดะพะผ ะฟะฐะบะตั‚ะต
ะ˜ะฝั‚ะตะณั€ะฐั†ะธะพะฝะฝั‹ะต test/moderation_test.go
ะœะพะบะธ internal/service/moderation/mocks/
ะ‘ะตะฝั‡ะผะฐั€ะบะธ ะ˜ะฝั‚ะตะณั€ะฐั†ะธะพะฝะฝั‹ะต ั‚ะตัั‚ั‹ ั ะฑะฐั‚ั‡ะฐะผะธ

Testify

ะ˜ัะฟะพะปัŒะทัƒะตั‚ัั ะดะปั ัƒั‚ะฒะตั€ะถะดะตะฝะธะน ะธ ะผะพะบะธั€ะพะฒะฐะฝะธั:

import "github.com/stretchr/testify"

Mockery

ะ“ะตะฝะตั€ะฐั†ะธั ะผะพะบะพะฒ ะดะปั ะธะฝั‚ะตั€ั„ะตะนัะพะฒ (ะฝะฐัั‚ั€ะพะตะฝะฐ ั‡ะตั€ะตะท .mockery.yaml)


ะกะฑะพั€ะบะฐ ะธ ั€ะฐะทั€ะฐะฑะพั‚ะบะฐ

Taskfile ะทะฐะดะฐั‡ะธ

task lint          # ะ—ะฐะฟัƒัะบ golangci-lint
task test          # ะ—ะฐะฟัƒัะบ ั‚ะตัั‚ะพะฒ
task run           # ะ—ะฐะฟัƒัะบ ัะตั€ะฒะตั€ะฐ

ะ›ะธะฝั‚ะธะฝะณ (.golangci.yml)

ะšะพะฝั„ะธะณัƒั€ะฐั†ะธั ะฒะบะปัŽั‡ะฐะตั‚ ะฟั€ะฐะฒะธะปะฐ revive ะดะปั ะฟั€ะพะฒะตั€ะบะธ ะบะฐั‡ะตัั‚ะฒะฐ ะบะพะดะฐ.


ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั

ะคะฐะนะป ะกะพะดะตั€ะถะฐะฝะธะต
README.md ะ˜ะฝัั‚ั€ัƒะบั†ะธั ะฟะพ ัƒัั‚ะฐะฝะพะฒะบะต
doc/models/OpenNSFW2.md ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั ะผะพะดะตะปะธ OpenNSFW2
doc/models/ViT.md ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั Vision Transformer
internal/service/moderation/TESTING.md ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธั ะฟะพ ั‚ะตัั‚ะธั€ะพะฒะฐะฝะธัŽ
AGENTS.md ะšะพะฝั„ะธะณัƒั€ะฐั†ะธั KiloCode ะฐะณะตะฝั‚ะพะฒ

ะฃัั‚ะฐะฝะพะฒะบะฐ

1. ONNX Runtime

macOS:

brew install onnxruntime

ะ”ั€ัƒะณะธะต ะฟะปะฐั‚ั„ะพั€ะผั‹: ัะผ. ONNX Runtime installation guide

2. FFmpeg

macOS:

brew install ffmpeg

3. ะ˜ะฝัั‚ั€ัƒะผะตะฝั‚ั‹ ั€ะฐะทั€ะฐะฑะพั‚ะบะธ

go install github.com/go-task/task/v3/cmd/task@latest
go install github.com/vektra/mockery/v2@v2.53.3
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest

4. ะะฐัั‚ั€ะพะนะบะฐ ะพะบั€ัƒะถะตะฝะธั

cp .env.example .env
# ะžั‚ั€ะตะดะฐะบั‚ะธั€ัƒะนั‚ะต .env ั ะฟัƒั‚ัะผะธ ะบ ะผะพะดะตะปัะผ

ะั€ั…ะธั‚ะตะบั‚ัƒั€ะฝั‹ะต ะพัะพะฑะตะฝะฝะพัั‚ะธ

Clean Architecture

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    HTTP Layer                       โ”‚
โ”‚                  (cmd/server)                       โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                  Service Layer                      โ”‚
โ”‚           (internal/service/moderation)             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                 Repository Layer                    โ”‚
โ”‚    (internal/repo/{media_reader,open_runner,vit})   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚               External Dependencies                 โ”‚
โ”‚              (ONNX Runtime, FFmpeg)                 โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Dependency Injection

ะ’ัะต ะบะพะผะฟะพะฝะตะฝั‚ั‹ ัะพะทะดะฐัŽั‚ัั ั‡ะตั€ะตะท ะบะพะฝัั‚ั€ัƒะบั‚ะพั€ั‹ ะธ ะฒะฝะตะดั€ััŽั‚ัั ะทะฐะฒะธัะธะผะพัั‚ะธ:

mediaReader := mediareader.New()
openRunner := openrunner.New()
vitRunner := vitrunner.New()
moderationService := moderation.New(mediaReader, openRunner, vitRunner)

Graceful Shutdown

ะŸั€ะฐะฒะธะปัŒะฝะพะต ะพัะฒะพะฑะพะถะดะตะฝะธะต ั€ะตััƒั€ัะพะฒ:

  • ะ—ะฐะบั€ั‹ั‚ะธะต ONNX-ัะตััะธะน ั‡ะตั€ะตะท defer
  • ะขะฐะนะผะฐัƒั‚ shutdown: 30 ัะตะบัƒะฝะด
  • ะžะฑั€ะฐะฑะพั‚ะบะฐ SIGINT/SIGTERM

ะŸะพั‚ะพะบ ะพะฑั€ะฐะฑะพั‚ะบะธ ะดะฐะฝะฝั‹ั…

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                         Media File(s)                               โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Media Reader                                   โ”‚
โ”‚  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”                           โ”‚
โ”‚  โ”‚  Image Process  โ”‚  โ”‚  Video Process  โ”‚                           โ”‚
โ”‚  โ”‚  - Decode       โ”‚  โ”‚  - FFmpeg       โ”‚                           โ”‚
โ”‚  โ”‚  - Resize 224ยฒ  โ”‚  โ”‚  - 2 fps        โ”‚                           โ”‚
โ”‚  โ”‚  - RGB24        โ”‚  โ”‚  - RGB24        โ”‚                           โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Batch All Frames                               โ”‚
โ”‚              [frame1, frame2, ..., frameN]                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
                โ–ผ                               โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”       โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚      OpenNSFW2 Runner     โ”‚       โ”‚       ViT Runner          โ”‚
โ”‚   [batch, 224, 224, 3]    โ”‚       โ”‚   [batch, 224, 224, 3]    โ”‚
โ”‚           โ†“               โ”‚       โ”‚           โ†“               โ”‚
โ”‚   [batch, 1] (NSFW prob)  โ”‚       โ”‚   [batch, 2] (N, NSFW)    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
              โ”‚                                   โ”‚
              โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                      Aggregate Results                              โ”‚
โ”‚  - Images: single score per file                                    โ”‚
โ”‚  - Videos: max score across all frames                              โ”‚
โ”‚  - Models: max score across all models                              โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                                โ”‚
                                โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    Final NSFW Scores                                โ”‚
โ”‚                 [file1, file2, ..., fileM]                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

ะŸะพั‚ะตะฝั†ะธะฐะปัŒะฝั‹ะต ัƒะปัƒั‡ัˆะตะฝะธั

ะะฐ ะพัะฝะพะฒะต ะฐะฝะฐะปะธะทะฐ ะบะพะดะฐ:

  1. ะ ะตะฐะปะธะทะฐั†ะธั ัะฝะดะฟะพะธะฝั‚ะฐ /moderate โ€” ะฒ ะฝะฐัั‚ะพัั‰ะตะต ะฒั€ะตะผั ะฒะพะทะฒั€ะฐั‰ะฐะตั‚ ะทะฐะณะปัƒัˆะบัƒ (TODO ะฒ ะบะพะดะต)
  2. ะ”ะพะฑะฐะฒะปะตะฝะธะต ะฒะฐะปะธะดะฐั†ะธะธ ะฒั…ะพะดะฝั‹ั… ะดะฐะฝะฝั‹ั… ะดะปั ัะฝะดะฟะพะธะฝั‚ะฐ ะผะพะดะตั€ะฐั†ะธะธ
  3. ะ ะฐััˆะธั€ะตะฝะธะต ั„ะพั€ะผะฐั‚ะพะฒ ะฒั‹ะฒะพะดะฐ (JSON ั ะดะตั‚ะฐะปัŒะฝั‹ะผะธ ั€ะตะทัƒะปัŒั‚ะฐั‚ะฐะผะธ)
  4. ะ”ะพะฑะฐะฒะปะตะฝะธะต ะผะตั‚ั€ะธะบ (prometheus) ะดะปั ะผะพะฝะธั‚ะพั€ะธะฝะณะฐ ะฟั€ะพะธะทะฒะพะดะธั‚ะตะปัŒะฝะพัั‚ะธ
  5. ะšะตัˆะธั€ะพะฒะฐะฝะธะต ั€ะตะทัƒะปัŒั‚ะฐั‚ะพะฒ ะดะปั ะฟะพะฒั‚ะพั€ะฝั‹ั… ะทะฐะฟั€ะพัะพะฒ
  6. ะัะธะฝั…ั€ะพะฝะฝะฐั ะพะฑั€ะฐะฑะพั‚ะบะฐ ะดะปั ะฑะพะปัŒัˆะธั… ะฑะฐั‚ั‡ะตะน ั„ะฐะนะปะพะฒ

ะ—ะฐะบะปัŽั‡ะตะฝะธะต

MFM โ€” ัั‚ะพ ั…ะพั€ะพัˆะพ ัั‚ั€ัƒะบั‚ัƒั€ะธั€ะพะฒะฐะฝะฝั‹ะน, production-ready ะฟั€ะพะตะบั‚ ะดะปั ะผะพะดะตั€ะฐั†ะธะธ ะผะตะดะธะฐะบะพะฝั‚ะตะฝั‚ะฐ. ะšะพะด ัะปะตะดัƒะตั‚ ะฟั€ะธะฝั†ะธะฟะฐะผ Clean Architecture ั ั‡ั‘ั‚ะบะธะผ ั€ะฐะทะดะตะปะตะฝะธะตะผ ะฝะฐ ัะปะพะธ. ะŸั€ะพะตะบั‚ ะธะผะตะตั‚:

  • โœ… ะงั‘ั‚ะบัƒัŽ ะฐั€ั…ะธั‚ะตะบั‚ัƒั€ัƒ ั ั€ะฐะทะดะตะปะตะฝะธะตะผ ะพั‚ะฒะตั‚ัั‚ะฒะตะฝะฝะพัั‚ะธ
  • โœ… Comprehensive ะปะพะณะธั€ะพะฒะฐะฝะธะต ั ะบะพะฝั‚ะตะบัั‚ะพะผ
  • โœ… Graceful shutdown ะธ ัƒะฟั€ะฐะฒะปะตะฝะธะต ั€ะตััƒั€ัะฐะผะธ
  • โœ… ะŸะพะบั€ั‹ั‚ะธะต ั‚ะตัั‚ะฐะผะธ
  • โœ… ะ”ะพะบัƒะผะตะฝั‚ะฐั†ะธัŽ ะฟะพ ัƒัั‚ะฐะฝะพะฒะบะต ะธ ะผะพะดะตะปัะผ
  • โœ… ะ˜ะฝัั‚ั€ัƒะผะตะฝั‚ั‹ ะดะปั ั€ะฐะทั€ะฐะฑะพั‚ะบะธ (Taskfile, linting)

ะ”ะฐั‚ะฐ: 2026-02-01 ะ’ะตั€ัะธั ะพั‚ั‡ั‘ั‚ะฐ: 1.0