Score artwork comments with local signatures plus Together AI, and optionally require Turnstile before posting.
61 lines
2.7 KiB
PHP
61 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Moderation;
|
|
|
|
use App\Contracts\Moderation\CommentSpamClassifier;
|
|
use App\Data\Moderation\CommentSpamClassification;
|
|
use Illuminate\Support\Arr;
|
|
use Illuminate\Support\Facades\Http;
|
|
use RuntimeException;
|
|
|
|
final class TogetherCommentSpamClassifier implements CommentSpamClassifier
|
|
{
|
|
public function classify(string $content): CommentSpamClassification
|
|
{
|
|
$config = (array) config('comment_spam.ai', []);
|
|
$key = (string) ($config['api_key'] ?? '');
|
|
$model = (string) ($config['model'] ?? '');
|
|
if ($key === '' || $model === '') {
|
|
throw new RuntimeException('Together AI is not configured.');
|
|
}
|
|
if (! in_array($model, (array) ($config['allowed_models'] ?? []), true)) {
|
|
throw new RuntimeException('Unsupported Together AI comment spam model.');
|
|
}
|
|
|
|
$started = microtime(true);
|
|
$response = Http::timeout(min(5, max(1, (int) ($config['timeout'] ?? 5))))
|
|
->withToken($key)
|
|
->post(rtrim((string) ($config['base_url'] ?? 'https://api.together.xyz/v1'), '/').'/chat/completions', [
|
|
'model' => $model,
|
|
'temperature' => 0,
|
|
'response_format' => ['type' => 'json_object'],
|
|
'messages' => [
|
|
['role' => 'system', 'content' => 'Classify the comment as spam or not spam. Return only JSON: {"spam":true|false,"confidence":0.0,"reason":"short explanation"}. Confidence must be a number from 0 to 1. Spam includes advertising, SEO promotion, unsolicited links, scams, and bot-like promotional repetition.'],
|
|
['role' => 'user', 'content' => mb_substr($content, 0, (int) ($config['max_input_chars'] ?? 2500))],
|
|
],
|
|
]);
|
|
|
|
if ($response->failed()) {
|
|
throw new RuntimeException('Together AI request failed with status '.$response->status().'.');
|
|
}
|
|
|
|
$message = Arr::get($response->json(), 'choices.0.message.content');
|
|
$decoded = is_string($message) ? json_decode($message, true) : null;
|
|
if (! is_array($decoded) || ! is_bool($decoded['spam'] ?? null)
|
|
|| ! is_numeric($decoded['confidence'] ?? null)
|
|
|| $decoded['confidence'] < 0 || $decoded['confidence'] > 1
|
|
|| ! is_string($decoded['reason'] ?? null)) {
|
|
throw new RuntimeException('Together AI returned invalid moderation JSON.');
|
|
}
|
|
|
|
return new CommentSpamClassification(
|
|
$decoded['spam'],
|
|
(float) $decoded['confidence'],
|
|
mb_substr(trim($decoded['reason']), 0, 500),
|
|
'together',
|
|
$model,
|
|
(int) round((microtime(true) - $started) * 1000),
|
|
);
|
|
}
|
|
}
|