Skip to content

Commit 8c8e40a

Browse files
authored
Create 2023-08-20-ai-image-moderation-with-laravel-workflow.md
1 parent 1d78dbe commit 8c8e40a

File tree

1 file changed

+203
-0
lines changed

1 file changed

+203
-0
lines changed
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
---
2+
slug: ai-image-moderation-with-laravel-workflow
3+
title: "AI Image Moderation with Laravel Workflow"
4+
authors:
5+
name: Richard
6+
title: Core Team
7+
url: https://github.com/rmcdaniel
8+
image_url: https://github.com/rmcdaniel.png
9+
tags: [ai, image-moderation, laravel-workflow, automation]
10+
---
11+
12+
![captionless image](https://miro.medium.com/v2/resize:fit:1400/format:webp/1*Sz-f9McEdB5UIlr55GOjyw.png)
13+
14+
## Introduction
15+
16+
Before we begin, let’s understand the scenario. We are building an image moderation system where:
17+
18+
1. Every image undergoes an initial AI check to determine if it’s safe.
19+
2. If the AI deems the image unsafe, it’s automatically logged and deleted.
20+
3. If it’s potentially safe, a human moderator is alerted to further review the image. They have the option to approve or reject the image.
21+
4. Approved images are moved to a public location, whereas rejected images are deleted.
22+
23+
## Laravel Workflow
24+
25+
Laravel Workflow is designed to streamline and organize complex processes in applications. It allows developers to define, manage, and execute workflows seamlessly. You can find installation instructions [here](https://github.com/laravel-workflow/laravel-workflow).
26+
27+
## ClarifAI API
28+
29+
ClarifAI provides AI-powered moderation tools for analyzing visual content. They offer a [free plan](https://www.clarifai.com/pricing) with up to 1,000 actions per month.
30+
31+
### 1. Store your credentials in `.env`.
32+
```ini
33+
CLARIFAI_API_KEY=key
34+
CLARIFAI_APP=my-application
35+
CLARIFAI_WORKFLOW=my-workflow
36+
CLARIFAI_USER=username
37+
```
38+
39+
### 2. Add the service to `config/services.php`.
40+
```php
41+
'clarifai' => [
42+
'api_key' => env('CLARIFAI_API_KEY'),
43+
'app' => env('CLARIFAI_APP'),
44+
'workflow' => env('CLARIFAI_WORKFLOW'),
45+
'user' => env('CLARIFAI_USER'),
46+
],
47+
```
48+
49+
### 3. Create a service at `app/Services/ClarifAI.php`.
50+
```php
51+
namespace App\Services;
52+
use Illuminate\Support\Facades\Http;
53+
class ClarifAI
54+
{
55+
private $apiKey;
56+
private $apiUrl;
57+
public function __construct()
58+
{
59+
$app = config('services.clarifai.app');
60+
$workflow = config('services.clarifai.workflow');
61+
$user = config('services.clarifai.user');
62+
$this->apiKey = config('services.clarifai.api_key');
63+
$this->apiUrl = "https://api.clarifai.com/v2/users/{$user}/apps/{$app}/workflows/{$workflow}/results/";
64+
}
65+
public function checkImage(string $image): bool
66+
{
67+
$response = Http::withToken($this->apiKey, 'Key')
68+
->post($this->apiUrl, ['inputs' => [
69+
['data' => ['image' => ['base64' => base64_encode($image)]]],
70+
]]);
71+
return collect($response->json('results.0.outputs.0.data.concepts', []))
72+
->filter(fn ($value) => $value['name'] === 'safe')
73+
->map(fn ($value) => round((float) $value['value']) > 0)
74+
->first() ?? false;
75+
}
76+
}
77+
```
78+
79+
## Creating the Workflow
80+
81+
```php
82+
namespace App\Workflows;
83+
use Workflow\ActivityStub;
84+
use Workflow\SignalMethod;
85+
use Workflow\WorkflowStub;
86+
use Workflow\Workflow;
87+
88+
class ImageModerationWorkflow extends Workflow
89+
{
90+
private bool $approved = false;
91+
private bool $rejected = false;
92+
93+
#[SignalMethod]
94+
public function approve()
95+
{
96+
$this->approved = true;
97+
}
98+
99+
#[SignalMethod]
100+
public function reject()
101+
{
102+
$this->rejected = true;
103+
}
104+
105+
public function execute($imagePath)
106+
{
107+
$safe = yield from $this->check($imagePath);
108+
if (! $safe) {
109+
yield from $this->unsafe($imagePath);
110+
return 'unsafe';
111+
}
112+
yield from $this->moderate($imagePath);
113+
return $this->approved ? 'approved' : 'rejected';
114+
}
115+
116+
private function check($imagePath)
117+
{
118+
return yield ActivityStub::make(AutomatedImageCheckActivity::class, $imagePath);
119+
}
120+
121+
private function unsafe($imagePath)
122+
{
123+
yield ActivityStub::all([
124+
ActivityStub::make(LogUnsafeImageActivity::class, $imagePath),
125+
ActivityStub::make(DeleteImageActivity::class, $imagePath),
126+
]);
127+
}
128+
129+
private function moderate($imagePath)
130+
{
131+
while (true) {
132+
yield ActivityStub::make(NotifyImageModeratorActivity::class, $imagePath);
133+
$signaled = yield WorkflowStub::awaitWithTimeout('24 hours', fn () => $this->approved || $this->rejected);
134+
if ($signaled) break;
135+
}
136+
}
137+
}
138+
```
139+
140+
## Activities
141+
142+
### Automated Image Check
143+
```php
144+
namespace App\Workflows;
145+
use App\Services\ClarifAI;
146+
use Illuminate\Support\Facades\Storage;
147+
use Workflow\Activity;
148+
class AutomatedImageCheckActivity extends Activity
149+
{
150+
public function execute($imagePath)
151+
{
152+
return app(ClarifAI::class)
153+
->checkImage(Storage::get($imagePath));
154+
}
155+
}
156+
```
157+
158+
### Logging Unsafe Images
159+
```php
160+
namespace App\Workflows;
161+
use Illuminate\Support\Facades\Log;
162+
use Workflow\Activity;
163+
class LogUnsafeImageActivity extends Activity
164+
{
165+
public function execute($imagePath)
166+
{
167+
Log::info('Unsafe image detected at: ' . $imagePath);
168+
}
169+
}
170+
```
171+
172+
### Deleting Images
173+
```php
174+
namespace App\Workflows;
175+
use Illuminate\Support\Facades\Storage;
176+
use Workflow\Activity;
177+
class DeleteImageActivity extends Activity
178+
{
179+
public function execute($imagePath)
180+
{
181+
Storage::delete($imagePath);
182+
}
183+
}
184+
```
185+
186+
## Starting and Signaling the Workflow
187+
```php
188+
$workflow = WorkflowStub::make(ImageModerationWorkflow::class);
189+
$workflow->start('tmp/good.jpg');
190+
```
191+
192+
For approvals or rejections:
193+
```php
194+
$workflow = WorkflowStub::load($id);
195+
$workflow->approve();
196+
// or
197+
$workflow->reject();
198+
```
199+
200+
## Conclusion
201+
202+
[Laravel Workflow](https://github.com/laravel-workflow/laravel-workflow) provides a structured approach to handle complex processes like image moderation. It supports asynchronous processing, external API integrations, and modular design for scalability. Thanks for reading!
203+

0 commit comments

Comments
 (0)