{% extends "base.html" %} {% block title %}NordaGPT - Norda Biznes Partner{% endblock %} {% block container_class %}chat-container-override{% endblock %} {% block extra_css %} {% endblock %} {% block content %}
NordaGPT

NordaGPT

Model AI

Wybierz model dopasowany do pytania

Gemini Flash Darmowy

Szybki i skuteczny. Dla większości pytań o firmy, kontakty, wydarzenia.

Bez opłat
🧠 Gemini Pro Premium

Głęboka analiza i rozumowanie. Dla złożonych pytań, strategii, rekomendacji.

~$0.20/pytanie · limit: $2/dzień
💰 Ten miesiąc: $0.00

🤖 NordaGPT - Informacje techniczne

Dostępne modele AI

⚡ Gemini 3 Flash DARMOWY
🧠 Gemini 3 Pro PREMIUM

Najnowsza generacja modeli Google z zaawansowanym rozumowaniem (thinking mode). Flash jest szybki i darmowy. Pro oferuje głębszą analizę dla złożonych pytań.

📊 Specyfikacja techniczna

Kontekst: 1 000 000 tokenów
Max. odpowiedź: 65 536 tokenów
Reasoning: 7x lepszy ↑ vs 2.5
Thinking mode: Flash: minimalny / Pro: zaawansowany
Kodowanie (SWE-bench): 78% ↑ najlepszy w klasie
Nauka (GPQA Diamond): 90.4%

💰 Koszty i limity

⚡ Flash: $0.00 — darmowy (Free Tier Google)
🧠 Pro: ~$0.01-0.05 za odpowiedź (Tier 1)
Limit dzienny Pro: $2.00 / dzień
Limit miesięczny Pro: $20.00 / miesiąc
Limit Flash: Brak limitu

Koszt każdej odpowiedzi widoczny w badge pod wiadomością. Miesięczne zużycie w nagłówku chatu.

📜 Historia rozwoju NordaGPT

29.01.2026
Wybór modelu Flash/Pro + Koszty

Nowy dropdown wyboru modelu. Flash darmowy (Free Tier), Pro premium z limitami. Badge kosztów przy każdej odpowiedzi. Miesięczne zużycie w nagłówku.

Aktualna wersja
28.01.2026
Gemini 3 Flash (Preview)

Najnowsza generacja AI od Google. 7x lepsze rozumowanie, thinking mode, 78% na benchmarku kodowania.

Upgrade modelu
14.01.2026
Gemini 2.5 Flash-Lite

8x dłuższe odpowiedzi, pełny thinking mode, 4x większy limit dzienny.

Poprzednia wersja
13.01.2026
Historia konwersacji

Sidebar z historią rozmów - możliwość powrotu do wcześniejszych konwersacji.

Nowa funkcja
Grudzień 2025
Gemini 2.0 Flash

Pierwszy model AI w NordaGPT. Kontekst 1M tokenów.

Poprzednia wersja
Listopad 2025
Uruchomienie NordaGPT

Premiera asystenta AI dla członków Norda Biznes. Integracja z bazą {{ COMPANY_COUNT }} firm.

Premiera

✨ Co działa w NordaGPT?

  • Wybór modelu — Flash (darmowy, szybki) lub Pro (premium, głębsza analiza)
  • Baza {{ COMPANY_COUNT }} firm — pełna wiedza o członkach Izby
  • Forum i wydarzenia — dostęp do dyskusji i kalendarza
  • Linki w odpowiedziach — bezpośrednie odnośniki do profili firm i osób
  • Transparentne koszty — widoczny koszt każdej odpowiedzi i miesięczne zużycie
  • Historia rozmów — pełna historia konwersacji w sidebarze

🎬 Jak korzystać z NordaGPT?

Krótki przewodnik (40 sekund)

💡 Szybkie wskazówki

  • Znajdź firmę: "Kto oferuje usługi IT?"
  • Sprawdź prezesa: "Kto jest prezesem PIXLAB?"
  • Wydarzenia: "Kiedy następne spotkanie Norda?"
  • Rekomendacje: "Poleć drukarnie z dobrymi opiniami"
NordaGPT

NordaGPT - Asystent AI Norda Biznes

Mogę pomóc Ci znaleźć firmy, usługi, sprawdzić kalendarz wydarzeń, rekomendacje i wiele więcej.

{% endblock %} {% block extra_js %} // NordaGPT Chat - State let currentConversationId = null; let conversations = []; let currentModel = 'flash'; // Default model (flash = ekonomiczny) let monthlyUsageCost = 0; // Koszt miesięczny użytkownika // ============================================ // Model Selection Toggle Functions // ============================================ const MODEL_CONFIG = { 'flash': { label: 'Flash', icon: '⚡', desc: 'Szybki' }, 'pro': { label: 'Pro', icon: '🧠', desc: 'Analiza' } }; function toggleModelDropdown() { const toggle = document.getElementById('modelToggle'); toggle.classList.toggle('open'); } function setModel(model) { currentModel = model; const config = MODEL_CONFIG[model]; // Update UI document.getElementById('modelLabel').textContent = config.label; document.getElementById('modelIcon').textContent = config.icon; // Update active state document.querySelectorAll('.thinking-option').forEach(opt => { opt.classList.toggle('active', opt.dataset.model === model); }); // Close dropdown document.getElementById('modelToggle').classList.remove('open'); // Save preference to server saveModelPreference(model); console.log('Model set to:', model); } async function saveModelPreference(model) { try { await fetch('/api/chat/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: model }) }); } catch (error) { console.error('Failed to save model preference:', error); } } function updateMonthlyCost(cost) { monthlyUsageCost += cost; const costDisplay = document.getElementById('monthlyCostDisplay'); if (costDisplay) { costDisplay.textContent = '$' + monthlyUsageCost.toFixed(2); } } // Close model dropdown when clicking outside document.addEventListener('click', function(e) { const toggle = document.getElementById('modelToggle'); if (toggle && !toggle.contains(e.target)) { toggle.classList.remove('open'); } }); // ============================================ // Model Info Modal Functions // ============================================ function openModelInfoModal() { document.getElementById('modelInfoModal').classList.add('active'); document.body.style.overflow = 'hidden'; } function closeModelInfoModal() { document.getElementById('modelInfoModal').classList.remove('active'); document.body.style.overflow = ''; } // Close modal on backdrop click document.addEventListener('click', function(e) { const modal = document.getElementById('modelInfoModal'); if (e.target === modal) { closeModelInfoModal(); } }); // Close modal on Escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closeModelInfoModal(); closeVideoHelpModal(); } }); // ============================================ // Video Help Modal Functions // ============================================ function openVideoHelpModal() { document.getElementById('videoHelpModal').classList.add('active'); document.body.style.overflow = 'hidden'; // Auto-play video when modal opens const video = document.getElementById('helpVideo'); if (video) { video.currentTime = 0; // Don't autoplay - let user control } } function closeVideoHelpModal() { document.getElementById('videoHelpModal').classList.remove('active'); document.body.style.overflow = ''; // Pause video when modal closes const video = document.getElementById('helpVideo'); if (video) { video.pause(); } } // Close video modal on backdrop click document.addEventListener('click', function(e) { const videoModal = document.getElementById('videoHelpModal'); if (e.target === videoModal) { closeVideoHelpModal(); } }); // Initialize on page load document.addEventListener('DOMContentLoaded', function() { loadConversations(); autoResizeTextarea(); loadModelSettings(); }); // Load model settings and monthly cost from server async function loadModelSettings() { try { const response = await fetch('/api/chat/settings'); const data = await response.json(); if (data.success) { // Always start with Flash (free, fast) - ignore saved preference currentModel = 'flash'; const config = MODEL_CONFIG['flash']; document.getElementById('modelLabel').textContent = config.label; document.getElementById('modelIcon').textContent = config.icon; document.querySelectorAll('.thinking-option').forEach(opt => { opt.classList.toggle('active', opt.dataset.model === 'flash'); }); // Load monthly cost if (data.monthly_cost !== undefined) { monthlyUsageCost = data.monthly_cost; document.getElementById('monthlyCostDisplay').textContent = '$' + monthlyUsageCost.toFixed(2); } } } catch (error) { console.log('Using default model:', currentModel); } } // Load conversations list async function loadConversations() { try { const response = await fetch('/api/chat/conversations'); const data = await response.json(); if (data.success) { conversations = data.conversations; renderConversationsList(); } } catch (error) { console.error('Error loading conversations:', error); document.getElementById('conversationsList').innerHTML = ''; } } // Render conversations in sidebar function renderConversationsList() { const list = document.getElementById('conversationsList'); if (conversations.length === 0) { list.innerHTML = ''; return; } list.innerHTML = conversations.map(conv => `
${escapeHtml(conv.title)}
`).join(''); } // Load a specific conversation async function loadConversation(conversationId) { currentConversationId = conversationId; // Update sidebar selection document.querySelectorAll('.conversation-item').forEach(item => { item.classList.toggle('active', parseInt(item.dataset.id) === conversationId); }); // Hide empty state const emptyState = document.getElementById('emptyState'); if (emptyState) emptyState.style.display = 'none'; // Clear messages and show loading const messagesDiv = document.getElementById('chatMessages'); messagesDiv.innerHTML = '
AI
Ładowanie historii...
'; try { const response = await fetch(`/api/chat/${conversationId}/history`); const data = await response.json(); if (data.success) { messagesDiv.innerHTML = ''; data.messages.forEach(msg => { addMessage(msg.role, msg.content, false); }); // Re-add typing indicator at the end messagesDiv.innerHTML += ` `; scrollToBottom(); } } catch (error) { console.error('Error loading conversation:', error); messagesDiv.innerHTML = '
AI
Nie udało się załadować rozmowy.
'; } // Close mobile sidebar document.getElementById('chatSidebar').classList.remove('mobile-open'); } // Start new conversation function startNewConversation() { currentConversationId = null; // Clear active state in sidebar document.querySelectorAll('.conversation-item').forEach(item => { item.classList.remove('active'); }); // Show empty state const messagesDiv = document.getElementById('chatMessages'); messagesDiv.innerHTML = `
NordaGPT

NordaGPT - Asystent AI Norda Biznes

Mogę pomóc Ci znaleźć firmy, usługi, sprawdzić kalendarz wydarzeń, rekomendacje i wiele więcej.

`; document.getElementById('messageInput').focus(); // Close mobile sidebar document.getElementById('chatSidebar').classList.remove('mobile-open'); } // Delete conversation async function deleteConversation(conversationId) { if (!confirm('Czy na pewno chcesz usunąć tę rozmowę?')) return; try { const response = await fetch(`/api/chat/${conversationId}/delete`, { method: 'DELETE' }); const data = await response.json(); if (data.success) { // If deleted current conversation, start new if (currentConversationId === conversationId) { startNewConversation(); } // Reload conversations list loadConversations(); } } catch (error) { console.error('Error deleting conversation:', error); alert('Nie udało się usunąć rozmowy'); } } // Send message async function sendMessage() { const input = document.getElementById('messageInput'); const sendBtn = document.getElementById('sendBtn'); const message = input.value.trim(); if (!message) return; // Disable input input.value = ''; input.disabled = true; sendBtn.disabled = true; // Hide empty state const emptyState = document.getElementById('emptyState'); if (emptyState) emptyState.style.display = 'none'; // Add user message addMessage('user', message); // Show typing indicator document.getElementById('typingIndicator').style.display = 'flex'; scrollToBottom(); try { // Start new conversation if needed if (!currentConversationId) { const startResponse = await fetch('/api/chat/start', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: message.substring(0, 50) + (message.length > 50 ? '...' : '') }) }); const startData = await startResponse.json(); if (startData.success) { currentConversationId = startData.conversation_id; } else { throw new Error(startData.error || 'Failed to start conversation'); } } // Send message with model selection const response = await fetch(`/api/chat/${currentConversationId}/message`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message, model: currentModel }) }); const data = await response.json(); // Hide typing indicator document.getElementById('typingIndicator').style.display = 'none'; if (data.success) { addMessage('assistant', data.message, true, data.tech_info); // Reload conversations to update list loadConversations(); } else { addMessage('assistant', 'Przepraszam, wystąpił błąd: ' + (data.error || 'Nieznany błąd')); } } catch (error) { console.error('Error sending message:', error); document.getElementById('typingIndicator').style.display = 'none'; addMessage('assistant', 'Przepraszam, nie mogę teraz odpowiedzieć. Spróbuj ponownie później.'); } // Re-enable input input.disabled = false; sendBtn.disabled = false; input.focus(); } // Send suggestion function sendSuggestion(text) { document.getElementById('messageInput').value = text; sendMessage(); } // Add message to chat function addMessage(role, content, animate = true, techInfo = null) { const messagesDiv = document.getElementById('chatMessages'); const typingIndicator = document.getElementById('typingIndicator'); const messageDiv = document.createElement('div'); messageDiv.className = 'message ' + role; if (!animate) messageDiv.style.animation = 'none'; const avatar = document.createElement('div'); avatar.className = 'message-avatar'; avatar.textContent = role === 'assistant' ? 'AI' : '{{ current_user.name[:1].upper() if current_user else "U" }}'; const contentDiv = document.createElement('div'); contentDiv.className = 'message-content'; contentDiv.innerHTML = formatMessage(content); // Add response info badge for AI responses (model, time, cost) if (role === 'assistant' && techInfo) { const infoBadge = document.createElement('div'); infoBadge.className = 'thinking-info-badge'; const modelName = techInfo.model || 'flash'; const latencyMs = parseInt(techInfo.latency_ms) || 0; const latencySec = (latencyMs / 1000).toFixed(1); const costUsd = parseFloat(techInfo.cost_usd) || 0; // Model labels const modelLabels = { 'flash': '⚡ Flash', 'pro': '🧠 Pro', 'gemini-3-flash-preview': '⚡ Flash', 'gemini-3-pro-preview': '🧠 Pro' }; const modelLabel = modelLabels[modelName] || modelName; // Format cost const costStr = costUsd > 0 ? `$${costUsd.toFixed(4)}` : '$0.00'; infoBadge.innerHTML = `${modelLabel} · ${latencySec}s · ${costStr}`; contentDiv.appendChild(infoBadge); // Update monthly cost if cost provided if (costUsd > 0) { updateMonthlyCost(costUsd); } } messageDiv.appendChild(avatar); messageDiv.appendChild(contentDiv); // Insert before typing indicator if (typingIndicator) { messagesDiv.insertBefore(messageDiv, typingIndicator); } else { messagesDiv.appendChild(messageDiv); } scrollToBottom(); } // Format message content (links, lists, etc.) function formatMessage(text) { if (!text) return ''; // Escape HTML first text = escapeHtml(text); // Convert markdown links [text](url) to tags with appropriate class // Links: company (orange), person (green), forum (purple), news (green), b2b (yellow), external (blue) // First: Handle internal links starting with / text = text.replace(/\[([^\]]+)\]\((\/[^)]+)\)/g, function(match, linkText, url) { let linkClass = 'external-link'; if (url.startsWith('/company/')) { linkClass = 'company-link'; } else if (url.startsWith('/forum/')) { linkClass = 'forum-link'; } else if (url.startsWith('/news/') || url.startsWith('/aktualnosci/')) { linkClass = 'news-link'; } else if (url.startsWith('/b2b/') || url.startsWith('/ogloszenia/')) { linkClass = 'b2b-link'; } else if (url.startsWith('/osoba/')) { linkClass = 'person-link'; } return '' + linkText + ''; }); // Then: Handle full URLs (https://) text = text.replace(/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g, function(match, linkText, url) { let linkClass = 'external-link'; if (url.includes('nordabiznes.pl/company/')) { linkClass = 'company-link'; } else if (url.includes('nordabiznes.pl/osoba/')) { linkClass = 'person-link'; } else if (url.includes('nordabiznes.pl/forum/')) { linkClass = 'forum-link'; } else if (url.includes('nordabiznes.pl/news/') || url.includes('nordabiznes.pl/aktualnosci/')) { linkClass = 'news-link'; } else if (url.includes('nordabiznes.pl/b2b/') || url.includes('nordabiznes.pl/ogloszenia/')) { linkClass = 'b2b-link'; } return '' + linkText + ''; }); // Convert raw URLs to links (only those not already in tags) text = text.replace(/(?)(https?:\/\/[^\s<]+)(?![^<]*<\/a>)/g, function(match, url) { let linkClass = 'external-link'; if (url.includes('nordabiznes.pl/company/')) { linkClass = 'company-link'; } else if (url.includes('nordabiznes.pl/osoba/')) { linkClass = 'person-link'; } return '' + url + ''; }); // Convert **bold** to text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); // Convert newlines to
text = text.replace(/\n/g, '
'); return text; } // Escape HTML function escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Scroll to bottom function scrollToBottom() { const messagesDiv = document.getElementById('chatMessages'); // Prosty i niezawodny scroll do dołu kontenera wiadomości requestAnimationFrame(() => { messagesDiv.scrollTop = messagesDiv.scrollHeight; }); } // Auto-resize textarea function autoResizeTextarea() { const textarea = document.getElementById('messageInput'); textarea.addEventListener('input', function() { this.style.height = 'auto'; this.style.height = Math.min(this.scrollHeight, 150) + 'px'; }); } // Toggle mobile sidebar function toggleSidebar() { document.getElementById('chatSidebar').classList.toggle('mobile-open'); } {% endblock %}