-
Notifications
You must be signed in to change notification settings - Fork 1
Add AI chat history, conversation search, and per-user personalization #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -28,19 +28,23 @@ def modTokens(user: str, user_input: str) -> None: | |
| """Backward-compatible alias for mod_tokens.""" | ||
| return mod_tokens(user, user_input) | ||
|
|
||
| def send(user_input: str) -> str: | ||
| def send(user_input: str, history: list = None, user_name: str = None) -> str: | ||
| name_part = f" You are talking to {user_name}." if user_name else "" | ||
| system_message = { | ||
| 'role': 'system', | ||
| 'content': f'You are {name}. {sys_prompt} Version: {version}. You are only allowed to speak with markdown formatting. Begin normal messages with ` and end them with `' | ||
| 'content': f'You are {name}. {sys_prompt}{name_part} Version: {version}. You are only allowed to speak with markdown formatting. Begin normal messages with ` and end them with `' | ||
| } | ||
|
Comment on lines
+31
to
36
|
||
|
|
||
| user_message = { | ||
| 'role': 'user', | ||
| 'content': user_input | ||
| } | ||
|
|
||
| messages = [system_message, user_message] | ||
|
|
||
|
|
||
| messages = [system_message] | ||
|
|
||
| # Include prior conversation turns so the AI has memory of the chat | ||
| if history: | ||
| for msg in history: | ||
| role = 'user' if msg.get('message_type') == 'user' else 'assistant' | ||
| messages.append({'role': role, 'content': msg['content']}) | ||
|
|
||
| messages.append({'role': 'user', 'content': user_input}) | ||
|
Comment on lines
+38
to
+46
|
||
|
|
||
| try: | ||
| response = ollama.chat(model=ai_model, messages=messages) | ||
| return response['message']['content'] | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -371,6 +371,55 @@ | |||||
| color: white; | ||||||
| } | ||||||
|
|
||||||
| /* Search Input Styles */ | ||||||
| .sidebar-search { | ||||||
| margin-bottom: 16px; | ||||||
| } | ||||||
|
|
||||||
| .sidebar-search-input { | ||||||
| width: 100%; | ||||||
| background: rgba(255, 255, 255, 0.1); | ||||||
| border: 1px solid rgba(255, 255, 255, 0.2); | ||||||
| border-radius: 10px; | ||||||
| padding: 8px 36px 8px 12px; | ||||||
| color: white; | ||||||
| font-size: 0.875rem; | ||||||
| outline: none; | ||||||
| transition: all 0.3s ease; | ||||||
| } | ||||||
|
|
||||||
| .sidebar-search-input::placeholder { | ||||||
| color: rgba(255, 255, 255, 0.5); | ||||||
| } | ||||||
|
|
||||||
| .sidebar-search-input:focus { | ||||||
| background: rgba(255, 255, 255, 0.15); | ||||||
| border-color: rgba(255, 255, 255, 0.4); | ||||||
| } | ||||||
|
|
||||||
| .sidebar-search-wrapper { | ||||||
| position: relative; | ||||||
| } | ||||||
|
|
||||||
| .sidebar-search-icon { | ||||||
| position: absolute; | ||||||
| right: 10px; | ||||||
| top: 50%; | ||||||
| transform: translateY(-50%); | ||||||
| color: rgba(255, 255, 255, 0.5); | ||||||
| font-size: 0.8rem; | ||||||
| pointer-events: none; | ||||||
| } | ||||||
|
|
||||||
| .search-results-section { | ||||||
| margin-left: 16px; | ||||||
| border-left: 2px solid rgba(255, 255, 255, 0.1); | ||||||
| padding-left: 16px; | ||||||
| margin-top: 4px; | ||||||
| max-height: 240px; | ||||||
| overflow-y: auto; | ||||||
| } | ||||||
|
|
||||||
| /* Chat History Sub-section Styles */ | ||||||
| .chat-history-section { | ||||||
| margin-left: 16px; | ||||||
|
|
@@ -586,6 +635,22 @@ | |||||
| <div class="sidebar-brand"> | ||||||
| <h5><i class="fas fa-robot me-2"></i>Komli</h5> | ||||||
| </div> | ||||||
|
|
||||||
| {% if user %} | ||||||
| <!-- Search --> | ||||||
| <div class="sidebar-search"> | ||||||
| <div class="sidebar-search-wrapper"> | ||||||
| <input type="text" class="sidebar-search-input" id="sidebarSearch" | ||||||
| placeholder="Search conversations..." | ||||||
| oninput="handleSearchInput(this.value)"> | ||||||
| <i class="fas fa-search sidebar-search-icon"></i> | ||||||
| </div> | ||||||
| <!-- Search Results --> | ||||||
| <div class="search-results-section" id="searchResultsSection" style="display: none;"> | ||||||
| <div id="searchResultsList"></div> | ||||||
| </div> | ||||||
| </div> | ||||||
| {% endif %} | ||||||
|
|
||||||
| <nav class="nav flex-column"> | ||||||
| <a href="#" class="nav-link active" onclick="startNewChat(event)"> | ||||||
|
|
@@ -701,6 +766,8 @@ <h1 id="chatTitle"><i class="fas fa-robot me-3"></i>Komli</h1> | |||||
| <script> | ||||||
| // Global variables | ||||||
| const userLoggedIn = {% if user %}true{% else %}false{% endif %}; | ||||||
| const SEARCH_DEBOUNCE_MS = 300; | ||||||
| const MAX_SEARCH_RESULT_TITLE_LENGTH = 22; | ||||||
|
|
||||||
| function toggleSidebar() { | ||||||
| const sidebar = document.getElementById('sidebar'); | ||||||
|
|
@@ -1071,6 +1138,59 @@ <h1 id="chatTitle"><i class="fas fa-robot me-3"></i>Komli</h1> | |||||
| toggleSidebar(); | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| // Search functionality | ||||||
| let searchTimeout = null; | ||||||
|
|
||||||
| function handleSearchInput(query) { | ||||||
| const resultsSection = document.getElementById('searchResultsSection'); | ||||||
| clearTimeout(searchTimeout); | ||||||
|
|
||||||
| if (!query.trim()) { | ||||||
| resultsSection.style.display = 'none'; | ||||||
| return; | ||||||
| } | ||||||
|
|
||||||
| // Debounce: wait SEARCH_DEBOUNCE_MS after the user stops typing | ||||||
| searchTimeout = setTimeout(() => performSearch(query.trim()), SEARCH_DEBOUNCE_MS); | ||||||
| } | ||||||
|
|
||||||
| function performSearch(query) { | ||||||
| const resultsSection = document.getElementById('searchResultsSection'); | ||||||
| const resultsList = document.getElementById('searchResultsList'); | ||||||
|
|
||||||
| resultsSection.style.display = 'block'; | ||||||
| resultsList.innerHTML = '<div class="text-center py-2"><i class="fas fa-spinner fa-spin"></i></div>'; | ||||||
|
|
||||||
| fetch('/api/search?q=' + encodeURIComponent(query)) | ||||||
|
||||||
| fetch('/api/search?q=' + encodeURIComponent(query)) | |
| fetch('api/search?q=' + encodeURIComponent(query)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The history-fetch/conversation-resolution block swallows all exceptions and then proceeds, which can silently disable both history and DB logging for logged-in users (e.g., non-integer
conversation_id, DB errors). Since this code is critical to the new “AI has memory” behavior, consider handling expected errors (likeValueError) with a fallback to the active conversation and logging unexpected exceptions so failures are visible.