Wire Afia into Frontdesk and sync the app launcher catalog.
Deploy Ladill Frontdesk / deploy (push) Successful in 34s
Deploy Ladill Frontdesk / deploy (push) Successful in 34s
Add the AI assistant button, chat endpoint, and Frontdesk-specific prompts, and include Frontdesk in the shared launcher config used by the app switcher. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Afia;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Afia — Ladill in-app AI assistant scoped to Frontdesk visitor management.
|
||||
*/
|
||||
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 <<<PROMPT
|
||||
You are Afia, the assistant inside Ladill Frontdesk (frontdesk.ladill.com).
|
||||
Help reception teams, security staff, and admins manage visitors, check-in, badges, hosts, and devices. Be concise, friendly, and actionable: short numbered steps and name the screen or section to use.
|
||||
|
||||
What Ladill Frontdesk does and where things live:
|
||||
- Dashboard: today's visitors, people currently inside, expected arrivals, and devices online.
|
||||
- Visits: check visitors in/out, approve visits, print badges, schedule future visits, and view visit history.
|
||||
- Visitors: search the visitor directory, view past visits, and manage watchlist flags.
|
||||
- Hosts: the people visitors come to see — set availability and link Ladill users where needed.
|
||||
- Host portal (/host): hosts approve pending visits and schedule expected guests.
|
||||
- Kiosk: self-service check-in on a tablet — register a Visitor Kiosk device, then open its kiosk URL on the hardware.
|
||||
- Devices: register kiosks, printers, and scanners; kiosks get a device token for unattended access.
|
||||
- Branches → Buildings → Reception desks: set up your site hierarchy before assigning devices.
|
||||
- Security: verify badges, run evacuation reports, and check visitors out at the gate.
|
||||
- Reports & integrations: exports, webhooks, and calendar feeds.
|
||||
|
||||
Rules:
|
||||
- Only answer questions about Ladill Frontdesk — visitors, visits, hosts, kiosks, badges, devices, security, and reception setup.
|
||||
- If asked about CRM, payments, events ticketing, or email mailboxes, briefly say those live in other Ladill apps and suggest the app launcher.
|
||||
- Never invent counts or visitor names — use the user context below or tell them where to check.
|
||||
|
||||
Current user context:
|
||||
{$ctx}
|
||||
PROMPT;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user