ai = $ai; } /** * Display a listing of insights for the authenticated user. */ public function index(Request $request): View { /** @var \App\Models\User $user */ $user = Auth::user(); $subjectId = $request->input('subject_id'); /** @var \Illuminate\Pagination\LengthAwarePaginator $insights */ $insightsQuery = Insight::where('user_id', $user->id) ->with('subject'); if ($subjectId) { $insightsQuery->where('subject_id', (int) $subjectId); } $insights = $insightsQuery->orderByDesc('created_at') ->paginate(20); /** @var \Illuminate\Database\Eloquent\Collection $subjects */ $subjects = Subject::where('is_active', true)->get(); // Token usage statistics $totalInputTokens = TokenUsage::where('user_id', $user->id)->sum('input_tokens'); $totalOutputTokens = TokenUsage::where('user_id', $user->id)->sum('output_tokens'); $totalTokens = $totalInputTokens + $totalOutputTokens; return view('admin.insights.index', compact( 'insights', 'subjects', 'subjectId', 'totalInputTokens', 'totalOutputTokens', 'totalTokens' )); } /** * Generate a new AI insight for the specified subject. */ public function generate(Request $request): RedirectResponse { $validated = $request->validate([ 'subject_id' => ['required', 'exists:subjects,id'], 'custom_prompt' => ['nullable', 'string', 'max:500'], 'message_type' => ['nullable', 'string', 'max:50'], 'language' => ['nullable', 'string', 'in:pt_BR,en,es'], ]); /** @var \App\Models\User $user */ $user = Auth::user(); /** @var Subject|null $subject */ $subject = Subject::findOrFail((int) $validated['subject_id']); $messageType = MessageType::tryFrom($validated['message_type'] ?? '') ?? MessageType::AiGenerated; $language = $validated['language'] ?? app()->getLocale() ?? 'en'; // Build the prompt with subject context $prompt = $validated['custom_prompt'] ?: $this->buildDefaultPrompt($subject, $messageType); $context = ['language' => $language]; if ($subject->introduction) { $context['introduction'] = $subject->introduction; $prompt = "Context about {$subject->name}: {$subject->introduction}\n\n{$prompt}"; } // Generate message via AI based on type $result = $this->ai->generateInsight($subject->name, $prompt, $context, $messageType); if (!$result['success']) { return back()->with('error', __('Failed to generate message: :error', [ 'error' => $result['error'] ?? __('Unknown error'), ])); } // Store the generated message Insight::create([ 'user_id' => $user->id, 'subject_id' => $subject->id, 'content' => $result['content'], 'type' => $messageType->value, 'input_tokens' => $result['input_tokens'], 'output_tokens' => $result['output_tokens'], 'sent_at' => now(), ]); // Track token usage TokenUsage::create([ 'user_id' => $user->id, 'input_tokens' => $result['input_tokens'], 'output_tokens' => $result['output_tokens'], 'total_tokens' => $result['input_tokens'] + $result['output_tokens'], 'model' => config('services.openrouter.model', 'google/gemini-2.0-flash-exp:free'), 'provider' => 'openrouter', ]); return back()->with('success', __('Message generated successfully!')); } /** * Build a default prompt based on the message type. */ private function buildDefaultPrompt(Subject $subject, MessageType $messageType): string { return match ($messageType) { MessageType::LanguageLesson => __('Teach me a practical language lesson about :subject with examples, practice sentences, pronunciation, grammar, and vocabulary.', ['subject' => $subject->name]), MessageType::GameSimulator => __('Give me a strategic tip for :subject with actionable strategies, commands, and practical steps.', ['subject' => $subject->name]), MessageType::TechnicalTeaching => __('Give me a technical teaching lesson about :subject covering components, techniques, repairs, schematics, and practical exercises.', ['subject' => $subject->name]), default => __('Generate an insightful learning tip about :subject. Include practical advice and key concepts.', ['subject' => $subject->name]), }; } /** * Store a manually created insight. */ public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'subject_id' => ['required', 'exists:subjects,id'], 'content' => ['required', 'string', 'max:5000'], ]); /** @var \App\Models\User $user */ $user = Auth::user(); Insight::create([ 'user_id' => $user->id, 'subject_id' => (int) $validated['subject_id'], 'content' => $validated['content'], 'type' => 'manual', 'input_tokens' => 0, 'output_tokens' => 0, 'sent_at' => now(), ]); return back()->with('success', __('Insight created successfully.')); } /** * Remove the specified insight. */ public function destroy(Insight $insight): RedirectResponse { /** @var \App\Models\User $user */ $user = Auth::user(); if ($insight->user_id !== $user->id && !$user->hasRole('superadmin')) { return back()->with('error', __('You do not have permission to delete this insight.')); } $insight->delete(); return back()->with('success', __('Insight deleted successfully.')); } /** * Translate an insight to the user's preferred language. */ public function translate(Request $request, Insight $insight): RedirectResponse { /** @var \App\Models\User $user */ $user = Auth::user(); if ($insight->user_id !== $user->id && !$user->hasRole('superadmin')) { return back()->with('error', __('You do not have permission to translate this insight.')); } $validated = $request->validate([ 'target_language' => ['required', 'string', 'max:10'], ]); $targetLanguage = $validated['target_language']; // Get user's language preference for this subject $userSubject = \App\Models\UserSubject::where('user_id', $insight->user_id) ->where('subject_id', $insight->subject_id) ->first(); $language = $userSubject->insight_language ?? $user->locale ?? 'en'; // Don't translate if already in the target language if ($language === $targetLanguage) { return back()->with('info', __('Insight is already in the selected language.')); } $result = $this->ai->translate($insight->content, $targetLanguage, $insight->subject->name); if (!$result['success']) { return back()->with('error', __('Failed to translate message: :error', [ 'error' => $result['error'] ?? __('Unknown error'), ])); } // Create a new insight with translated content Insight::create([ 'user_id' => $insight->user_id, 'subject_id' => $insight->subject_id, 'content' => $result['content'], 'type' => $insight->type->value, 'input_tokens' => $result['input_tokens'], 'output_tokens' => $result['output_tokens'], 'sent_at' => now(), ]); // Track token usage TokenUsage::create([ 'user_id' => $insight->user_id, 'input_tokens' => $result['input_tokens'], 'output_tokens' => $result['output_tokens'], 'total_tokens' => $result['input_tokens'] + $result['output_tokens'], 'model' => config('services.openrouter.model', 'google/gemini-2.0-flash-exp:free'), 'provider' => 'openrouter', ]); return back()->with('success', __('Message translated successfully!')); } }