Deploy Ladill Link / deploy (push) Successful in 1m26s
Let accounts organise links by promo or launch, attach or detach links, and view combined click totals from the sidebar.
124 lines
5.2 KiB
PHP
124 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Afia;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use RuntimeException;
|
|
|
|
/**
|
|
* Afia — Ladill in-app AI assistant scoped to Ladill Link.
|
|
*/
|
|
class AfiaService
|
|
{
|
|
public function __construct(private PlatformAiConfig $ai) {}
|
|
|
|
public function enabled(): bool
|
|
{
|
|
return $this->ai->enabled();
|
|
}
|
|
|
|
/**
|
|
* @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 = $this->ai->provider();
|
|
$model = $this->ai->model();
|
|
$apiKey = $this->ai->apiKey();
|
|
|
|
$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->linkSystemPrompt($ctx);
|
|
}
|
|
|
|
private function linkSystemPrompt(string $ctx): string
|
|
{
|
|
return <<<PROMPT
|
|
You are Afia, the assistant inside Ladill Link (link.ladill.com).
|
|
Help users create and manage short links, track clicks, use custom domains, campaigns, and understand wallet billing. Be concise, friendly, and actionable: short numbered steps and name the screen or section to use.
|
|
|
|
What Ladill Link does and where things live:
|
|
- Overview (Dashboard): link count, recent clicks, and wallet balance.
|
|
- My Links: create, edit, enable/disable, and delete short links. Each new link debits a small fee from the Ladill wallet (see Settings or the create screen for the current price).
|
|
- Campaigns: group short links for a promo or launch, attach/detach links, and see combined click totals.
|
|
- Public URLs: default domain is configured (usually ladl.link/{slug}) and always resolved live — custom domains from Settings replace it when set as default.
|
|
- Analytics: click totals and trends across links.
|
|
- Custom Domains: connect a domain you own, verify DNS, and set a default domain for new links.
|
|
- Settings: default domain preference and account shortcuts.
|
|
- Unified catalog: links created in other Ladill apps (Merchant, Give, Events, QR Plus, etc.) also appear in My Links — some are managed in their home app, others here.
|
|
- Wallet: link creation is paid from the Ladill wallet; top up at account.ladill.com/wallet.
|
|
|
|
Rules:
|
|
- Only answer questions about Ladill Link — short links, slugs, destinations, campaigns, clicks, custom domains, billing, and the unified link list.
|
|
- If asked about QR code design, donations, events, or storefronts, briefly say those live in other Ladill apps and suggest the app launcher.
|
|
- Never invent slugs, click counts, or wallet balances — use the user context below or tell them where to check.
|
|
- For DNS/custom domain help: mention verifying the TXT/CNAME record in Custom Domains before the domain goes live.
|
|
|
|
Current user context:
|
|
{$ctx}
|
|
PROMPT;
|
|
}
|
|
}
|