feat: add app and database modules
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
class AiAssistantService
|
||||
{
|
||||
public function __construct(
|
||||
protected AiService $aiService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Answer questions about the system.
|
||||
*/
|
||||
public function answer(string $question): string
|
||||
{
|
||||
if (! get_setting('ai_enabled', false)) {
|
||||
return 'AI Assistant is currently disabled.';
|
||||
}
|
||||
|
||||
$systemPrompt = "You are the BIIProject Intelligent Assistant.
|
||||
You help administrators manage the system.
|
||||
The system is built with Laravel, PostgreSQL, and Redis.
|
||||
Key features: RBAC, System Monitoring, AI Log Analysis, Backup/Restore.
|
||||
|
||||
Guidelines:
|
||||
- Be professional, helpful, and concise.
|
||||
- If asked about technical details, provide accurate Laravel-based advice.
|
||||
- If you don't know the specific configuration of this instance, advise checking 'System Config'.
|
||||
- Security is priority. Never advise actions that compromise security.";
|
||||
|
||||
try {
|
||||
$result = $this->aiService->provider()->generate($question, [
|
||||
'system_instruction' => $systemPrompt,
|
||||
]);
|
||||
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
return $result['response'];
|
||||
}
|
||||
|
||||
return 'Sorry, I encountered an error: '.($result['error'] ?? 'Unknown error');
|
||||
} catch (\Exception $e) {
|
||||
return 'Error: '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
interface AiProviderInterface
|
||||
{
|
||||
/**
|
||||
* Generate response from the AI provider.
|
||||
*
|
||||
* @return array [success => bool, response => string, usage => array, error => string]
|
||||
*/
|
||||
public function generate(string $prompt, array $options = []): array;
|
||||
|
||||
/**
|
||||
* Get the provider identifier.
|
||||
*/
|
||||
public function getIdentifier(): string;
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*
|
||||
* Unauthorized copying, modification, distribution, or use
|
||||
* of this file is strictly prohibited without prior written
|
||||
* permission from the author.
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use App\Services\SystemConfig\SystemConfigService;
|
||||
|
||||
class AiService
|
||||
{
|
||||
protected $systemConfig;
|
||||
|
||||
protected $providers = [];
|
||||
|
||||
public function __construct(SystemConfigService $systemConfig)
|
||||
{
|
||||
$this->systemConfig = $systemConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a provider instance.
|
||||
*/
|
||||
public function provider(?string $identifier = null): AiProviderInterface
|
||||
{
|
||||
$identifier = $identifier ?: $this->systemConfig->get('ai_provider', 'gpt');
|
||||
|
||||
if (isset($this->providers[$identifier])) {
|
||||
return $this->providers[$identifier];
|
||||
}
|
||||
|
||||
$config = $this->getProviderConfig($identifier);
|
||||
|
||||
$instance = match ($identifier) {
|
||||
'gpt' => new GptProvider($config),
|
||||
'gemini' => new GeminiProvider($config),
|
||||
'claude' => new ClaudeProvider($config),
|
||||
'deepseek' => new OpenAiCompatibleProvider('deepseek', 'https://api.deepseek.com/chat/completions', $config),
|
||||
'grok' => new OpenAiCompatibleProvider('grok', 'https://api.x.ai/v1/chat/completions', $config),
|
||||
'mistral' => new OpenAiCompatibleProvider('mistral', 'https://api.mistral.ai/v1/chat/completions', $config),
|
||||
'openrouter' => new OpenAiCompatibleProvider('openrouter', 'https://openrouter.ai/api/v1/chat/completions', $config),
|
||||
'ollama' => new OllamaProvider($config),
|
||||
default => throw new \Exception("Unsupported AI provider: {$identifier}"),
|
||||
};
|
||||
|
||||
return $this->providers[$identifier] = $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get configuration for a specific provider from system settings.
|
||||
*/
|
||||
protected function getProviderConfig(string $identifier): array
|
||||
{
|
||||
return [
|
||||
'key' => $this->systemConfig->get("ai_{$identifier}_key"),
|
||||
'base_url' => $this->systemConfig->get("ai_{$identifier}_base_url"),
|
||||
'default_model' => $this->systemConfig->get('ai_default_model'),
|
||||
'instruction' => $this->systemConfig->get('ai_system_instruction'),
|
||||
'temperature' => $this->systemConfig->get('ai_temperature', 0.7),
|
||||
'max_tokens' => $this->systemConfig->get('ai_max_tokens', 2000),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Static method to get supported models for each provider.
|
||||
*/
|
||||
public static function getSupportedModels(): array
|
||||
{
|
||||
return [
|
||||
'gpt' => [
|
||||
['id' => 'gpt-4o', 'name' => 'GPT-4o (Newest)'],
|
||||
['id' => 'gpt-4-turbo', 'name' => 'GPT-4 Turbo'],
|
||||
['id' => 'gpt-3.5-turbo', 'name' => 'GPT-3.5 Turbo'],
|
||||
],
|
||||
'gemini' => [
|
||||
['id' => 'gemini-1.5-pro', 'name' => 'Gemini 1.5 Pro'],
|
||||
['id' => 'gemini-1.5-flash', 'name' => 'Gemini 1.5 Flash'],
|
||||
['id' => 'gemini-1.0-pro', 'name' => 'Gemini 1.0 Pro'],
|
||||
],
|
||||
'claude' => [
|
||||
['id' => 'claude-3-5-sonnet-20240620', 'name' => 'Claude 3.5 Sonnet'],
|
||||
['id' => 'claude-3-opus-20240229', 'name' => 'Claude 3 Opus'],
|
||||
['id' => 'claude-3-sonnet-20240229', 'name' => 'Claude 3 Sonnet'],
|
||||
['id' => 'claude-3-haiku-20240307', 'name' => 'Claude 3 Haiku'],
|
||||
],
|
||||
'deepseek' => [
|
||||
['id' => 'deepseek-chat', 'name' => 'DeepSeek Chat'],
|
||||
['id' => 'deepseek-coder', 'name' => 'DeepSeek Coder'],
|
||||
],
|
||||
'grok' => [
|
||||
['id' => 'grok-1', 'name' => 'Grok-1'],
|
||||
],
|
||||
'mistral' => [
|
||||
['id' => 'mistral-large-latest', 'name' => 'Mistral Large'],
|
||||
['id' => 'mistral-medium-latest', 'name' => 'Mistral Medium'],
|
||||
['id' => 'mistral-small-latest', 'name' => 'Mistral Small'],
|
||||
],
|
||||
'openrouter' => [
|
||||
['id' => 'google/gemini-pro-1.5', 'name' => 'Gemini Pro 1.5'],
|
||||
['id' => 'anthropic/claude-3.5-sonnet', 'name' => 'Claude 3.5 Sonnet'],
|
||||
['id' => 'meta-llama/llama-3-70b-instruct', 'name' => 'Llama 3 70B'],
|
||||
],
|
||||
'ollama' => [
|
||||
['id' => 'llama3', 'name' => 'Llama 3'],
|
||||
['id' => 'mistral', 'name' => 'Mistral'],
|
||||
['id' => 'phi3', 'name' => 'Phi-3'],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use App\Models\AI\AiUsageLog;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
abstract class BaseAiProvider implements AiProviderInterface
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the AI usage to the database.
|
||||
*/
|
||||
protected function logUsage(array $data): void
|
||||
{
|
||||
try {
|
||||
AiUsageLog::create([
|
||||
'user_id' => Auth::id(),
|
||||
'provider' => $this->getIdentifier(),
|
||||
'model' => $data['model'] ?? 'unknown',
|
||||
'prompt' => $data['prompt'] ?? null,
|
||||
'response' => $data['response'] ?? null,
|
||||
'prompt_tokens' => $data['usage']['prompt_tokens'] ?? 0,
|
||||
'completion_tokens' => $data['usage']['completion_tokens'] ?? 0,
|
||||
'total_tokens' => $data['usage']['total_tokens'] ?? 0,
|
||||
'estimated_cost' => $this->calculateCost($data),
|
||||
'status' => $data['status'] ?? 'success',
|
||||
'error_message' => $data['error'] ?? null,
|
||||
'metadata' => $data['metadata'] ?? null,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
\Log::error('Failed to log AI usage: '.$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract cost calculation, to be implemented by each provider.
|
||||
*/
|
||||
protected function calculateCost(array $data): float
|
||||
{
|
||||
// Default implementation, can be overridden
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class ClaudeProvider extends BaseAiProvider
|
||||
{
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'claude';
|
||||
}
|
||||
|
||||
public function generate(string $prompt, array $options = []): array
|
||||
{
|
||||
$key = $options['key'] ?? $this->config['key'] ?? null;
|
||||
if (! $key) {
|
||||
return ['success' => false, 'error' => 'API Key not configured.'];
|
||||
}
|
||||
|
||||
$model = $options['model'] ?? $this->config['default_model'] ?? 'claude-3-5-sonnet-20240620';
|
||||
$instruction = $options['instruction'] ?? $this->config['instruction'] ?? '';
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$res = Http::timeout(60)->withHeaders([
|
||||
'x-api-key' => $key,
|
||||
'anthropic-version' => '2023-06-01',
|
||||
'content-type' => 'application/json',
|
||||
])->post('https://api.anthropic.com/v1/messages', [
|
||||
'model' => $model,
|
||||
'max_tokens' => (int) ($options['max_tokens'] ?? $this->config['max_tokens'] ?? 2000),
|
||||
'system' => $instruction,
|
||||
'messages' => [
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
'temperature' => (float) ($options['temperature'] ?? $this->config['temperature'] ?? 0.7),
|
||||
]);
|
||||
|
||||
if ($res->failed()) {
|
||||
$error = $res->json()['error']['message'] ?? 'Claude Error';
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
]);
|
||||
|
||||
return ['success' => false, 'error' => $error];
|
||||
}
|
||||
|
||||
$data = $res->json();
|
||||
$response = $data['content'][0]['text'];
|
||||
$usage = [
|
||||
'prompt_tokens' => $data['usage']['input_tokens'] ?? 0,
|
||||
'completion_tokens' => $data['usage']['output_tokens'] ?? 0,
|
||||
'total_tokens' => ($data['usage']['input_tokens'] ?? 0) + ($data['usage']['output_tokens'] ?? 0),
|
||||
];
|
||||
|
||||
$result = [
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'model' => $model,
|
||||
'latency' => microtime(true) - $startTime,
|
||||
];
|
||||
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'status' => 'success',
|
||||
'metadata' => ['latency' => $result['latency']],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class GeminiProvider extends BaseAiProvider
|
||||
{
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'gemini';
|
||||
}
|
||||
|
||||
public function generate(string $prompt, array $options = []): array
|
||||
{
|
||||
$key = $options['key'] ?? $this->config['key'] ?? null;
|
||||
if (! $key) {
|
||||
return ['success' => false, 'error' => 'API Key not configured.'];
|
||||
}
|
||||
|
||||
$model = $options['model'] ?? $this->config['default_model'] ?? 'gemini-1.5-flash';
|
||||
$instruction = $options['instruction'] ?? $this->config['instruction'] ?? '';
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$res = Http::timeout(60)->post("https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$key}", [
|
||||
'contents' => [
|
||||
['parts' => [['text' => $instruction."\n\n".$prompt]]],
|
||||
],
|
||||
'generationConfig' => [
|
||||
'temperature' => (float) ($options['temperature'] ?? $this->config['temperature'] ?? 0.7),
|
||||
'maxOutputTokens' => (int) ($options['max_tokens'] ?? $this->config['max_tokens'] ?? 2000),
|
||||
],
|
||||
]);
|
||||
|
||||
if ($res->failed()) {
|
||||
$error = $res->json()['error']['message'] ?? 'Gemini Error';
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
]);
|
||||
|
||||
return ['success' => false, 'error' => $error];
|
||||
}
|
||||
|
||||
$data = $res->json();
|
||||
$response = $data['candidates'][0]['content']['parts'][0]['text'] ?? 'No response';
|
||||
|
||||
// Gemini doesn't always return token count in the same way, simplified for now
|
||||
$usage = [
|
||||
'prompt_tokens' => 0,
|
||||
'completion_tokens' => 0,
|
||||
'total_tokens' => 0,
|
||||
];
|
||||
|
||||
$result = [
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'model' => $model,
|
||||
'latency' => microtime(true) - $startTime,
|
||||
];
|
||||
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'status' => 'success',
|
||||
'metadata' => ['latency' => $result['latency']],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class GptProvider extends BaseAiProvider
|
||||
{
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'gpt';
|
||||
}
|
||||
|
||||
public function generate(string $prompt, array $options = []): array
|
||||
{
|
||||
$key = $options['key'] ?? $this->config['key'] ?? null;
|
||||
if (! $key) {
|
||||
return ['success' => false, 'error' => 'API Key not configured.'];
|
||||
}
|
||||
|
||||
$model = $options['model'] ?? $this->config['default_model'] ?? 'gpt-4o';
|
||||
$instruction = $options['instruction'] ?? $this->config['instruction'] ?? '';
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$res = Http::withToken($key)
|
||||
->timeout(60)
|
||||
->post('https://api.openai.com/v1/chat/completions', [
|
||||
'model' => $model,
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => $instruction],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
'temperature' => (float) ($options['temperature'] ?? $this->config['temperature'] ?? 0.7),
|
||||
'max_tokens' => (int) ($options['max_tokens'] ?? $this->config['max_tokens'] ?? 2000),
|
||||
]);
|
||||
|
||||
if ($res->failed()) {
|
||||
$error = $res->json()['error']['message'] ?? 'OpenAI Error';
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
]);
|
||||
|
||||
return ['success' => false, 'error' => $error];
|
||||
}
|
||||
|
||||
$data = $res->json();
|
||||
$response = $data['choices'][0]['message']['content'];
|
||||
$usage = $data['usage'] ?? [];
|
||||
|
||||
$result = [
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'model' => $model,
|
||||
'latency' => microtime(true) - $startTime,
|
||||
];
|
||||
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'status' => 'success',
|
||||
'metadata' => ['latency' => $result['latency']],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
protected function calculateCost(array $data): float
|
||||
{
|
||||
$model = $data['model'] ?? '';
|
||||
$promptTokens = $data['usage']['prompt_tokens'] ?? 0;
|
||||
$completionTokens = $data['usage']['completion_tokens'] ?? 0;
|
||||
|
||||
// Simplified estimation
|
||||
$rates = [
|
||||
'gpt-4o' => ['prompt' => 0.005 / 1000, 'completion' => 0.015 / 1000],
|
||||
'gpt-4-turbo' => ['prompt' => 0.01 / 1000, 'completion' => 0.03 / 1000],
|
||||
'gpt-3.5-turbo' => ['prompt' => 0.0005 / 1000, 'completion' => 0.0015 / 1000],
|
||||
];
|
||||
|
||||
$rate = $rates[$model] ?? $rates['gpt-4o'];
|
||||
|
||||
return ($promptTokens * $rate['prompt']) + ($completionTokens * $rate['completion']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Spatie\Activitylog\Models\Activity;
|
||||
|
||||
class LogAnalysisService
|
||||
{
|
||||
public function __construct(
|
||||
protected AiService $aiService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Analyze recent activity logs using AI.
|
||||
*/
|
||||
public function analyzeRecentLogs(int $limit = 50): string
|
||||
{
|
||||
if (! get_setting('ai_enabled', false)) {
|
||||
return 'AI Service is currently disabled in system settings.';
|
||||
}
|
||||
|
||||
$logs = Activity::with('causer')
|
||||
->latest()
|
||||
->take($limit)
|
||||
->get()
|
||||
->map(function ($log) {
|
||||
return [
|
||||
'time' => $log->created_at->toDateTimeString(),
|
||||
'user' => $log->causer ? $log->causer->name : 'System',
|
||||
'action' => $log->description,
|
||||
'subject' => $log->subject_type ? class_basename($log->subject_type) : 'N/A',
|
||||
];
|
||||
})
|
||||
->toArray();
|
||||
|
||||
if (empty($logs)) {
|
||||
return 'No activity logs found to analyze.';
|
||||
}
|
||||
|
||||
$prompt = 'As a Security and Operational Auditor, analyze the following system activity logs and provide:
|
||||
1. A brief summary of recent operations.
|
||||
2. Security insights (detect any suspicious patterns or potential privilege abuse).
|
||||
3. Operational health status.
|
||||
4. Recommendations (if any).
|
||||
|
||||
FORMAT: Use Markdown with bold headers. Be concise and professional.
|
||||
|
||||
LOGS DATA:
|
||||
'.json_encode($logs, JSON_PRETTY_PRINT);
|
||||
|
||||
try {
|
||||
return Cache::remember('ai_log_analysis_result', 3600, function () use ($prompt) {
|
||||
$result = $this->aiService->provider()->generate($prompt);
|
||||
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
return $result['response'] ?? 'AI failed to generate analysis.';
|
||||
}
|
||||
|
||||
return 'AI Provider Error: '.($result['error'] ?? 'Unknown error');
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
return 'Error during AI analysis: '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class OllamaProvider extends BaseAiProvider
|
||||
{
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return 'ollama';
|
||||
}
|
||||
|
||||
public function generate(string $prompt, array $options = []): array
|
||||
{
|
||||
$baseUrl = $options['base_url'] ?? $this->config['base_url'] ?? 'http://localhost:11434';
|
||||
$model = $options['model'] ?? $this->config['default_model'] ?? 'llama3';
|
||||
$instruction = $options['instruction'] ?? $this->config['instruction'] ?? '';
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$res = Http::timeout(120)->post("{$baseUrl}/api/generate", [
|
||||
'model' => $model,
|
||||
'prompt' => $instruction."\n\n".$prompt,
|
||||
'stream' => false,
|
||||
'options' => [
|
||||
'temperature' => (float) ($options['temperature'] ?? $this->config['temperature'] ?? 0.7),
|
||||
'num_predict' => (int) ($options['max_tokens'] ?? $this->config['max_tokens'] ?? 2000),
|
||||
],
|
||||
]);
|
||||
|
||||
if ($res->failed()) {
|
||||
$error = 'Ollama Error: Make sure the server is running and the model is pulled.';
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
]);
|
||||
|
||||
return ['success' => false, 'error' => $error];
|
||||
}
|
||||
|
||||
$data = $res->json();
|
||||
$response = $data['response'];
|
||||
|
||||
$usage = [
|
||||
'prompt_tokens' => $data['prompt_eval_count'] ?? 0,
|
||||
'completion_tokens' => $data['eval_count'] ?? 0,
|
||||
'total_tokens' => ($data['prompt_eval_count'] ?? 0) + ($data['eval_count'] ?? 0),
|
||||
];
|
||||
|
||||
$result = [
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'model' => $model,
|
||||
'latency' => microtime(true) - $startTime,
|
||||
];
|
||||
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'status' => 'success',
|
||||
'metadata' => ['latency' => $result['latency']],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
|
||||
class OpenAiCompatibleProvider extends BaseAiProvider
|
||||
{
|
||||
protected $identifier;
|
||||
|
||||
protected $baseUrl;
|
||||
|
||||
public function __construct(string $identifier, string $baseUrl, array $config = [])
|
||||
{
|
||||
parent::__construct($config);
|
||||
$this->identifier = $identifier;
|
||||
$this->baseUrl = $baseUrl;
|
||||
}
|
||||
|
||||
public function getIdentifier(): string
|
||||
{
|
||||
return $this->identifier;
|
||||
}
|
||||
|
||||
public function generate(string $prompt, array $options = []): array
|
||||
{
|
||||
$key = $options['key'] ?? $this->config['key'] ?? null;
|
||||
if (! $key) {
|
||||
return ['success' => false, 'error' => ucfirst($this->identifier).' API Key not configured.'];
|
||||
}
|
||||
|
||||
$model = $options['model'] ?? $this->config['default_model'] ?? null;
|
||||
$instruction = $options['instruction'] ?? $this->config['instruction'] ?? '';
|
||||
|
||||
$startTime = microtime(true);
|
||||
|
||||
try {
|
||||
$res = Http::timeout(60)->withToken($key)
|
||||
->post($this->baseUrl, [
|
||||
'model' => $model,
|
||||
'messages' => [
|
||||
['role' => 'system', 'content' => $instruction],
|
||||
['role' => 'user', 'content' => $prompt],
|
||||
],
|
||||
'temperature' => (float) ($options['temperature'] ?? $this->config['temperature'] ?? 0.7),
|
||||
'max_tokens' => (int) ($options['max_tokens'] ?? $this->config['max_tokens'] ?? 2000),
|
||||
]);
|
||||
|
||||
if ($res->failed()) {
|
||||
$error = $res->json()['error']['message'] ?? $res->json()['message'] ?? ucfirst($this->identifier).' Error';
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'status' => 'failed',
|
||||
'error' => $error,
|
||||
]);
|
||||
|
||||
return ['success' => false, 'error' => $error];
|
||||
}
|
||||
|
||||
$data = $res->json();
|
||||
$response = $data['choices'][0]['message']['content'];
|
||||
$usage = $data['usage'] ?? [
|
||||
'prompt_tokens' => 0,
|
||||
'completion_tokens' => 0,
|
||||
'total_tokens' => 0,
|
||||
];
|
||||
|
||||
$result = [
|
||||
'success' => true,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'model' => $model,
|
||||
'latency' => microtime(true) - $startTime,
|
||||
];
|
||||
|
||||
$this->logUsage([
|
||||
'model' => $model,
|
||||
'prompt' => $prompt,
|
||||
'response' => $response,
|
||||
'usage' => $usage,
|
||||
'status' => 'success',
|
||||
'metadata' => ['latency' => $result['latency']],
|
||||
]);
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return ['success' => false, 'error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ============================================================
|
||||
*
|
||||
* @project biiproject
|
||||
*
|
||||
* @author Andika Debi Putra
|
||||
*
|
||||
* @email andikadebiputra@gmail.com
|
||||
*
|
||||
* @website https://biiproject.com
|
||||
*
|
||||
* @copyright Copyright (c) 2026 Andika Debi Putra
|
||||
* @license Proprietary - All Rights Reserved
|
||||
*
|
||||
* @version 1.0.0
|
||||
*
|
||||
* @created 2026-05-01
|
||||
* ============================================================
|
||||
*/
|
||||
|
||||
namespace App\Services\AI;
|
||||
|
||||
use App\Services\SystemConfig\SystemConfigService;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class SecurityHardeningService
|
||||
{
|
||||
public function __construct(
|
||||
protected AiService $aiService,
|
||||
protected SystemConfigService $configService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Audit system security settings and get AI recommendations.
|
||||
*/
|
||||
public function auditSecurity(): array
|
||||
{
|
||||
if (! get_setting('ai_enabled', false)) {
|
||||
return ['error' => 'AI Service disabled.'];
|
||||
}
|
||||
|
||||
// Collect relevant security settings
|
||||
$settings = [
|
||||
'force_https' => get_setting('force_https'),
|
||||
'hsts_enabled' => get_setting('hsts_enabled'),
|
||||
'two_factor_auth' => get_setting('two_factor_auth'),
|
||||
'password_min_length' => get_setting('password_min_length'),
|
||||
'login_max_attempts' => get_setting('login_max_attempts'),
|
||||
'session_lifetime' => get_setting('session_lifetime'),
|
||||
'ip_whitelist_admin' => ! empty(get_setting('ip_whitelist_admin')),
|
||||
'backup_db_encrypt' => get_setting('backup_db_encrypt'),
|
||||
'maintenance_mode' => get_setting('maintenance_mode_enabled'),
|
||||
'environment' => app()->environment(),
|
||||
'debug_mode' => config('app.debug'),
|
||||
];
|
||||
|
||||
$prompt = 'As a Cyber Security Expert, audit the following Laravel system security configurations and provide:
|
||||
1. A Security Score (0-100).
|
||||
2. Critical Vulnerabilities (if any).
|
||||
3. Hardening Recommendations.
|
||||
4. A JSON object summary at the end.
|
||||
|
||||
CONFIGURATIONS:
|
||||
'.json_encode($settings, JSON_PRETTY_PRINT);
|
||||
|
||||
try {
|
||||
return Cache::remember('security_audit_result', 86400, function () use ($prompt) {
|
||||
$result = $this->aiService->provider()->generate($prompt);
|
||||
|
||||
if (isset($result['success']) && $result['success']) {
|
||||
return [
|
||||
'analysis' => $result['response'],
|
||||
'score' => $this->extractScore($result['response']),
|
||||
'timestamp' => now()->toDateTimeString(),
|
||||
];
|
||||
}
|
||||
|
||||
return ['error' => $result['error'] ?? 'Unknown error'];
|
||||
});
|
||||
} catch (\Exception $e) {
|
||||
return ['error' => $e->getMessage()];
|
||||
}
|
||||
}
|
||||
|
||||
private function extractScore(string $text): int
|
||||
{
|
||||
preg_match('/Score:?\s*(\d+)/i', $text, $matches);
|
||||
|
||||
return isset($matches[1]) ? (int) $matches[1] : 70;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user