feat(ai): AiService (Anthropic adapter), GenerateSlug action

Implement AI domain with Anthropic API integration for slug generation.
Adds GenerateSlug action for URL-to-slug transformation and GenerateAbVariants
for A/B test variants. Includes test coverage for GenerateSlug action with
mocked AiService.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
boban 2026-05-16 08:06:11 +02:00
parent 79da7502cd
commit c193e482ac
5 changed files with 94 additions and 0 deletions

View File

@ -0,0 +1,26 @@
<?php
namespace App\Domains\Ai\Actions;
use App\Domains\Ai\Services\AiService;
class GenerateAbVariants
{
public function __construct(private AiService $ai) {}
public function handle(string $targetUrl, int $count = 3): array
{
$prompt = "Generate {$count} A/B test URL slug variants (3-8 chars each, lowercase, hyphens only, comma-separated) for: {$targetUrl}. Reply with ONLY the comma-separated slugs.";
$result = $this->ai->complete($prompt);
if (empty($result)) {
return [];
}
return array_map(
fn($s) => preg_replace('/[^a-z0-9-]/', '', strtolower(trim($s))),
explode(',', $result)
);
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Domains\Ai\Actions;
use App\Domains\Ai\Services\AiService;
class GenerateSlug
{
public function __construct(private AiService $ai) {}
public function handle(string $targetUrl): string
{
$prompt = "Generate a short, memorable URL slug (3-8 chars, lowercase, hyphens only) for this URL: {$targetUrl}. Reply with ONLY the slug, no explanation.";
$slug = $this->ai->complete($prompt);
return preg_replace('/[^a-z0-9-]/', '', strtolower($slug));
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Domains\Ai\Services;
use Illuminate\Support\Facades\Http;
class AiService
{
public function complete(string $prompt, string $model = 'claude-sonnet-4-6'): string
{
$apiKey = config('services.anthropic.api_key');
if (! $apiKey) {
return '';
}
$response = Http::withHeaders([
'x-api-key' => $apiKey,
'anthropic-version' => '2023-06-01',
'content-type' => 'application/json',
])->post('https://api.anthropic.com/v1/messages', [
'model' => $model,
'max_tokens' => 256,
'messages' => [['role' => 'user', 'content' => $prompt]],
]);
return trim($response->json('content.0.text') ?? '');
}
}

View File

@ -35,4 +35,8 @@ return [
],
],
'anthropic' => [
'api_key' => env('ANTHROPIC_API_KEY'),
],
];

View File

@ -0,0 +1,16 @@
<?php
use App\Domains\Ai\Actions\GenerateSlug;
use App\Domains\Ai\Services\AiService;
it('generates slug from URL via AI service', function () {
$mockAi = Mockery::mock(AiService::class);
$mockAi->shouldReceive('complete')
->once()
->andReturn('best-promo');
$action = new GenerateSlug($mockAi);
$slug = $action->handle('https://example.com/summer-sale-2026');
expect($slug)->toBe('best-promo');
});