Initial implementation of the HomeSysPanel CMS built with Laravel 13.0. Includes complete admin panel, content management, user authentication, modular architecture for products/services/chatbot, and marketing tools. Key features: - User authentication with email verification and password reset - Role-based access control (Admin/User with granular permissions) - Content management (pages, news, FAQ, downloads, galleries, banners) - Contact management system with subjects and responses - Marketing tools (email campaigns, visitor analytics, social sharing) - Modular architecture (Products, Services, Chatbot modules) - AI Chatbot integration with OpenRouter.ai and LlamaCpp support - Visitor tracking with geolocation - SEO optimization with meta tags and Open Graph - Responsive design with Bootstrap 5 - Dark mode support - Maintenance mode - Feature request system
176 lines
7.4 KiB
PHP
Executable File
176 lines
7.4 KiB
PHP
Executable File
<?php
|
|
|
|
namespace App\Modules\Chatbot\Services;
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
use Illuminate\Support\Facades\Log;
|
|
use App\Models\Setting;
|
|
use App\Modules\Chatbot\Models\ChatbotConversation;
|
|
use App\Modules\Chatbot\Models\ChatbotKnowledge;
|
|
use App\Modules\Chatbot\Models\ChatbotRule;
|
|
|
|
class LlamaCppService
|
|
{
|
|
/**
|
|
* Send a chat message to a local llama.cpp server and get a response.
|
|
*
|
|
* @param array $messages Array of messages with 'role' and 'content'
|
|
* @return array ['content' => string, 'tokens_used' => int|null]
|
|
*/
|
|
public function chat(array $messages, ?ChatbotConversation $conversation = null): array
|
|
{
|
|
$baseUrl = Setting::get('chatbot_llamacpp_base_url', 'http://172.17.0.1:8080');
|
|
$model = Setting::get('chatbot_llamacpp_model', '');
|
|
|
|
if (empty($baseUrl)) {
|
|
Log::warning('Chatbot: llama.cpp base URL not configured.');
|
|
return [
|
|
'content' => 'Desculpe, o assistente virtual local não está configurado corretamente. Por favor, entre em contato com a empresa.',
|
|
'tokens_used' => null,
|
|
];
|
|
}
|
|
|
|
$systemPrompt = $this->buildSystemPrompt($conversation);
|
|
|
|
$payload = [
|
|
'messages' => array_merge(
|
|
[['role' => 'system', 'content' => $systemPrompt]],
|
|
$messages
|
|
),
|
|
'max_tokens' => (int) Setting::get('chatbot_max_tokens', 500),
|
|
'temperature' => (float) Setting::get('chatbot_temperature', 0.7),
|
|
'stream' => false,
|
|
];
|
|
|
|
// Add model name if specified (some llama.cpp setups use it)
|
|
if (!empty($model)) {
|
|
$payload['model'] = $model;
|
|
}
|
|
|
|
$url = rtrim($baseUrl, '/') . '/v1/chat/completions';
|
|
|
|
try {
|
|
$response = Http::timeout(120)
|
|
->withHeaders([
|
|
'Content-Type' => 'application/json',
|
|
])
|
|
->post($url, $payload);
|
|
|
|
if ($response->successful()) {
|
|
$data = $response->json();
|
|
$tokensUsed = $data['usage']['total_tokens'] ?? null;
|
|
|
|
// Update cumulative token counter
|
|
if ($tokensUsed) {
|
|
$currentTotal = (int) Setting::get('chatbot_total_tokens_used', 0);
|
|
Setting::set('chatbot_total_tokens_used', $currentTotal + $tokensUsed, 'chatbot');
|
|
}
|
|
|
|
return [
|
|
'content' => $data['choices'][0]['message']['content'] ?? 'Desculpe, não consegui processar sua mensagem.',
|
|
'tokens_used' => $tokensUsed,
|
|
];
|
|
}
|
|
|
|
Log::error('Chatbot: llama.cpp API error', [
|
|
'status' => $response->status(),
|
|
'body' => $response->body(),
|
|
]);
|
|
|
|
return [
|
|
'content' => 'Desculpe, estou com dificuldades para responder no momento. Tente novamente em instantes.',
|
|
'tokens_used' => null,
|
|
];
|
|
|
|
} catch (\Exception $e) {
|
|
Log::error('Chatbot: Exception calling llama.cpp API', [
|
|
'message' => $e->getMessage(),
|
|
'url' => $url,
|
|
]);
|
|
|
|
return [
|
|
'content' => 'Desculpe, ocorreu um erro de conexão com o servidor local. Tente novamente em instantes.',
|
|
'tokens_used' => null,
|
|
];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the system prompt with company knowledge.
|
|
*/
|
|
protected function buildSystemPrompt(?ChatbotConversation $conversation = null): string
|
|
{
|
|
$companyName = Setting::get('company_name', 'Nossa Empresa');
|
|
$companyEmail = Setting::get('company_email');
|
|
$companyPhone = Setting::get('company_phone');
|
|
$companyAddress = Setting::get('company_address');
|
|
$companyHours = Setting::get('company_hours');
|
|
|
|
$customInstructions = Setting::get('chatbot_custom_instructions', '');
|
|
|
|
$prompt = "Você é um assistente virtual de atendimento ao cliente da empresa **{$companyName}**.\n\n";
|
|
$prompt .= "## Regras Importantes:\n";
|
|
$prompt .= "- Responda APENAS sobre a empresa {$companyName}, seus produtos, serviços e informações relacionadas.\n";
|
|
$prompt .= "- Seja sempre educado, profissional e prestativo.\n";
|
|
$prompt .= "- Responda em português do Brasil.\n";
|
|
$prompt .= "- Se não souber a resposta, diga que não possui essa informação e sugira entrar em contato pelo telefone ou e-mail da empresa.\n";
|
|
$prompt .= "- NUNCA invente informações que não foram fornecidas na base de conhecimento.\n";
|
|
$prompt .= "- NUNCA forneça informações sobre concorrentes ou outras empresas.\n";
|
|
$prompt .= "- NUNCA recomende ou sugira que o visitante faça pesquisas na internet (ex: \"pesquise no Google\", \"busque online\", \"procure no site\", \"acesse o link\", etc.). Toda informação deve ser fornecida diretamente por você com base exclusivamente no conteúdo da empresa {$companyName}.\n";
|
|
$prompt .= "- NUNCA forneça links, URLs ou endereços de sites externos, redes sociais ou buscadores. Utilize apenas os canais de contato oficiais da empresa (telefone, e-mail, WhatsApp ou endereço físico) listados abaixo.\n";
|
|
$prompt .= "- NUNCA diga frases como \"você pode pesquisar\", \"procure saber\", \"verifique na internet\", \"consulte online\" ou qualquer variação que incentive o visitante a buscar informação fora deste chat.\n";
|
|
$prompt .= "- Mantenha as respostas concisas e objetivas.\n\n";
|
|
|
|
if ($companyEmail) {
|
|
$prompt .= "- E-mail de contato: {$companyEmail}\n";
|
|
}
|
|
if ($companyPhone) {
|
|
$prompt .= "- Telefone de contato: {$companyPhone}\n";
|
|
}
|
|
if ($companyAddress) {
|
|
$prompt .= "- Endereço: {$companyAddress}\n";
|
|
}
|
|
if ($companyHours) {
|
|
$prompt .= "- Horário de funcionamento: {$companyHours}\n";
|
|
}
|
|
|
|
$prompt .= "\n";
|
|
|
|
// Add visitor info if available
|
|
if ($conversation && $conversation->visitor_name) {
|
|
$prompt .= "## Dados do Visitante:\n";
|
|
$prompt .= "- **Nome**: {$conversation->visitor_name}\n";
|
|
if ($conversation->visitor_phone) {
|
|
$prompt .= "- **Telefone**: {$conversation->visitor_phone}\n";
|
|
}
|
|
if ($conversation->visitor_city) {
|
|
$prompt .= "- **Cidade**: {$conversation->visitor_city}\n";
|
|
}
|
|
$prompt .= "\nSempre cumprimente o visitante pelo nome e personalize o atendimento.\n";
|
|
$prompt .= "\n";
|
|
}
|
|
|
|
// Add knowledge base entries
|
|
$knowledge = ChatbotKnowledge::getFormattedKnowledge();
|
|
if ($knowledge) {
|
|
$prompt .= "## Base de Conhecimento da Empresa:\n";
|
|
$prompt .= $knowledge;
|
|
}
|
|
|
|
// Add configurable AI rules (admin-defined behavior constraints)
|
|
$rules = ChatbotRule::getFormattedRules();
|
|
if ($rules) {
|
|
$prompt .= "\n## Regras Obrigatórias de Comportamento:\n";
|
|
$prompt .= "As regras abaixo foram definidas pela administração e DEVEM ser seguidas rigorosamente em TODAS as respostas:\n";
|
|
$prompt .= $rules . "\n";
|
|
}
|
|
|
|
// Add custom instructions
|
|
if ($customInstructions) {
|
|
$prompt .= "\n## Instruções Adicionais:\n{$customInstructions}\n";
|
|
}
|
|
|
|
return $prompt;
|
|
}
|
|
}
|