Files
ladill-events/app/Services/Afia/AfiaService.php
T
isaaccladandClaude Opus 4.8 86e40bfbd2
Deploy Ladill Events / deploy (push) Successful in 53s
Afia: rebrand assistant from QR Plus to Ladill Events
Events shipped the QR Plus Afia (prompt, greeting, suggestions, and 'QR Plus
assistant' subtitle). Rewrite all for Ladill Events — events, tickets,
attendees, check-in, badges, programmes, payouts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 07:34:26 +00:00

119 lines
4.7 KiB
PHP

<?php
namespace App\Services\Afia;
use Illuminate\Support\Facades\Http;
use RuntimeException;
/**
* Afia — Ladill in-app AI assistant scoped to a product (QR Plus).
*/
class AfiaService
{
public function enabled(): bool
{
return (bool) config('afia.enabled', true) && (string) config('afia.api_key', '') !== '';
}
/**
* @param array<int,array{role:string,text:string}> $history
* @param array<string,mixed> $context
*/
public function chat(string $message, array $history, array $context): string
{
if (! $this->enabled()) {
throw new RuntimeException('Afia is not configured.');
}
$provider = (string) config('afia.provider', 'openai');
$model = (string) config('afia.model', 'gpt-4o-mini');
$apiKey = (string) config('afia.api_key');
$messages = [['role' => 'system', 'content' => $this->systemPrompt($context)]];
foreach (array_slice($history, -8) as $turn) {
$role = ($turn['role'] ?? 'user') === 'assistant' ? 'assistant' : 'user';
$text = trim((string) ($turn['text'] ?? ''));
if ($text !== '') {
$messages[] = ['role' => $role, 'content' => $text];
}
}
$messages[] = ['role' => 'user', 'content' => $message];
return $provider === 'anthropic'
? $this->viaAnthropic($model, $apiKey, $messages)
: $this->viaOpenAi($model, $apiKey, $messages);
}
private function viaOpenAi(string $model, string $apiKey, array $messages): string
{
$res = Http::withToken($apiKey)->acceptJson()->timeout(45)
->post('https://api.openai.com/v1/chat/completions', [
'model' => $model,
'temperature' => 0.3,
'max_tokens' => 600,
'messages' => $messages,
]);
if ($res->failed()) {
throw new RuntimeException('OpenAI request failed: '.$res->status());
}
return trim((string) $res->json('choices.0.message.content', ''));
}
private function viaAnthropic(string $model, string $apiKey, array $messages): string
{
$system = $messages[0]['content'];
$turns = array_values(array_filter($messages, fn ($m) => $m['role'] !== 'system'));
$res = Http::withHeaders([
'x-api-key' => $apiKey,
'anthropic-version' => '2023-06-01',
])->acceptJson()->timeout(45)->post('https://api.anthropic.com/v1/messages', [
'model' => $model,
'max_tokens' => 600,
'system' => $system,
'messages' => array_map(fn ($m) => ['role' => $m['role'], 'content' => $m['content']], $turns),
]);
if ($res->failed()) {
throw new RuntimeException('Anthropic request failed: '.$res->status());
}
return trim((string) $res->json('content.0.text', ''));
}
/** @param array<string,mixed> $context */
private function systemPrompt(array $context): string
{
$ctx = collect($context)->map(fn ($v, $k) => "- {$k}: {$v}")->implode("\n");
return $this->eventsSystemPrompt($ctx);
}
private function eventsSystemPrompt(string $ctx): string
{
return <<<PROMPT
You are Afia, the assistant inside Ladill Events (events.ladill.com).
Help users create events, sell tickets, and manage attendees. Be concise, friendly, and actionable: short numbered steps and name the screen or section to use.
What Ladill Events does and where things live:
- Overview (Dashboard): upcoming events, ticket sales, recent attendees, and wallet balance.
- Events: create and manage events — set details, date, venue, and ticket types/prices, then publish and share the event link or QR.
- Attendees: people holding tickets — view them and check them in (scan their ticket QR at the door).
- Badges: design and print attendee badges.
- Programmes: build the event schedule/agenda (sessions, times, speakers).
- Payouts: ticket revenue lands in your Ladill wallet; request a withdrawal to your bank or mobile money from Payouts.
- Account, Wallet & Team: manage billing, balance, and teammates from the account area.
Rules:
- Only answer questions about Ladill Events — events, tickets, attendees, check-in, badges, programmes, and payouts.
- If asked about domains, hosting, email, plain QR codes, storefronts, donations, or payments, briefly say those live in other Ladill apps and suggest the app launcher.
- Never invent prices, ticket numbers, payout amounts, or wallet balances — use the user context below or tell them where to check.
Current user context:
{$ctx}
PROMPT;
}
}