Merge pull request #307 from Ahmed-G-ElTaher/main

feat: Add ThinkPath-Chatbot to the LLM Desktop apps with Ollama list
This commit is contained in:
Shubham Saboo 2025-08-17 21:14:06 -05:00 committed by GitHub
commit 49efda65ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 6449 additions and 0 deletions

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Ahmed El-Taher
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,884 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Guided LLM Chat</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
flex-direction: column;
}
.title-bar {
background: rgba(255, 255, 255, 0.1);
height: 40px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
color: white;
font-size: 14px;
-webkit-app-region: drag;
}
.title-text {
flex: 1;
text-align: center;
font-weight: 500;
}
.window-controls {
display: flex;
gap: 10px;
-webkit-app-region: no-drag;
}
.window-btn {
width: 12px;
height: 12px;
border-radius: 50%;
border: none;
cursor: pointer;
transition: all 0.2s ease;
}
.window-btn:hover {
transform: scale(1.2);
}
.btn-close {
background: #ff5f57;
}
.btn-close:hover {
background: #ff3b30;
}
.btn-minimize {
background: #ffbd2e;
}
.btn-minimize:hover {
background: #ff9500;
}
.btn-maximize {
background: #28ca42;
}
.btn-maximize:hover {
background: #30d158;
}
.update-paths-btn {
background: #6c757d;
color: white;
border: none;
padding: 8px 16px;
border-radius: 15px;
cursor: pointer;
font-size: 12px;
margin-bottom: 15px;
transition: all 0.2s ease;
}
.update-paths-btn:hover {
background: #5a6268;
transform: translateY(-1px);
}
.update-paths-btn:disabled {
background: #ccc;
cursor: not-allowed;
transform: none;
}
.chat-container {
flex: 1;
display: flex;
flex-direction: column;
max-width: 1000px;
margin: 0 auto;
padding: 20px;
width: 100%;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 20px;
background: rgba(255, 255, 255, 0.95);
border-radius: 15px 15px 0 0;
margin-bottom: 0;
}
.message {
margin-bottom: 20px;
padding: 15px;
border-radius: 12px;
max-width: 80%;
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.user-message {
background: #007bff;
color: white;
margin-left: auto;
}
.bot-message {
background: #f8f9fa;
color: #333;
border: 1px solid #e9ecef;
}
.loading {
background: #f8f9fa;
color: #666;
font-style: italic;
}
.thinking-paths {
background: rgba(255, 255, 255, 0.95);
padding: 20px;
border-radius: 0 0 15px 15px;
border-top: 1px solid #e9ecef;
}
.paths-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 15px;
margin-bottom: 15px;
}
.thinking-path {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 12px;
padding: 15px;
transition: all 0.2s ease;
}
.thinking-path:hover {
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
transform: translateY(-2px);
}
.path-header {
font-weight: 600;
font-size: 14px;
color: #495057;
margin-bottom: 12px;
text-align: center;
padding-bottom: 8px;
border-bottom: 1px solid #dee2e6;
}
.path-steps {
list-style: none;
padding: 0;
margin: 0;
}
.path-step {
padding: 8px 12px;
margin: 6px 0;
border-radius: 8px;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
position: relative;
}
.path-step:hover {
background: #e9ecef;
border-color: #c8b99c;
}
.path-step:before {
content: attr(data-step);
background: #c8b99c;
color: white;
border-radius: 50%;
width: 20px;
height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
margin-right: 8px;
}
.thinking-indicator {
text-align: center;
padding: 20px;
color: #666;
font-style: italic;
}
.response-with-path {
background: #e8f4f8;
border-left: 4px solid #17a2b8;
margin-bottom: 15px;
}
.path-info {
font-size: 12px;
color: #17a2b8;
font-weight: 600;
margin-bottom: 12px;
padding: 8px 12px;
background: rgba(23, 162, 184, 0.1);
border-radius: 6px;
}
.response-content {
line-height: 1.7;
font-size: 14px;
}
.response-content strong,
.response-content b {
color: #2c3e50;
font-weight: 600;
}
.response-content h3 {
color: #2c3e50;
font-size: 16px;
margin: 15px 0 10px 0;
padding-bottom: 6px;
border-bottom: 2px solid #3498db;
font-weight: 600;
}
.response-content h4 {
color: #34495e;
font-size: 15px;
margin: 14px 0 8px 0;
font-weight: 600;
}
.response-content p {
margin: 10px 0;
color: #2c3e50;
}
.response-content ul {
margin: 8px 0 12px 20px;
color: #2c3e50;
}
.response-content li {
margin: 4px 0;
line-height: 1.6;
}
.thinking-step {
background: #f8f9fa;
border-left: 4px solid #17a2b8;
padding: 15px 18px;
margin: 12px 0;
border-radius: 0 8px 8px 0;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.thinking-step h4 {
color: #17a2b8 !important;
margin-top: 0 !important;
margin-bottom: 10px !important;
font-size: 15px !important;
font-weight: 600 !important;
}
.auto-updating {
background: #fff3cd;
border: 1px solid #ffeaa7;
padding: 8px 12px;
border-radius: 6px;
font-size: 12px;
color: #856404;
margin-bottom: 10px;
text-align: center;
}
.step-executed {
background: #d4edda !important;
border-color: #c3e6cb !important;
}
.step-executed:before {
background: #28a745 !important;
}
.input-container {
display: flex;
gap: 10px;
margin-top: 15px;
}
.chat-input {
flex: 1;
padding: 12px 16px;
border: 1px solid #ddd;
border-radius: 25px;
font-size: 16px;
outline: none;
}
.chat-input:focus {
border-color: #007bff;
box-shadow: 0 0 0 3px rgba(0,123,255,0.1);
}
.send-btn {
background: #007bff;
color: white;
border: none;
padding: 12px 20px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
transition: background 0.2s ease;
}
.send-btn:hover {
background: #0056b3;
}
.send-btn:disabled {
background: #ccc;
cursor: not-allowed;
}
.category-label {
font-size: 12px;
color: #666;
margin-bottom: 8px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 1px;
}
.actions-section {
margin-bottom: 20px;
}
.status-indicator {
position: fixed;
top: 40px;
right: 20px;
padding: 8px 16px;
border-radius: 20px;
font-size: 12px;
background: rgba(255, 255, 255, 0.9);
border: 1px solid #ddd;
}
.status-connected {
color: #28a745;
}
.status-disconnected {
color: #dc3545;
}
</style>
</head>
<body>
<div class="title-bar">
<div class="window-controls">
<button class="window-btn btn-close" onclick="closeWindow()" title="Close"></button>
<button class="window-btn btn-minimize" onclick="minimizeWindow()" title="Minimize"></button>
<button class="window-btn btn-maximize" onclick="toggleMaximize()" title="Maximize"></button>
</div>
<div class="title-text">
Guided LLM Chat - Strategic Thinking Assistant
</div>
<div style="width: 70px;"></div> <!-- Spacer for centering -->
</div>
<div class="status-indicator" id="status">
<span class="status-disconnected">● Connecting...</span>
</div>
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message bot-message">
🧠 Hello! I'm your strategic thinking assistant. Ask me any question and I'll generate different thinking approaches for you to explore step-by-step. Each approach offers a unique way to tackle your problem.
</div>
</div>
<div class="thinking-paths" id="thinkingPaths">
<div class="thinking-indicator">
Ask me a question and I'll show you different thinking approaches...
</div>
<div class="input-container">
<input type="text" class="chat-input" id="chatInput" placeholder="Type your question..." onkeypress="handleKeyPress(event)">
<button class="send-btn" id="sendBtn" onclick="sendMessage()">Send</button>
</div>
</div>
</div>
<script>
const { ipcRenderer } = require('electron');
let isConnected = false;
let isLoading = false;
// Check Ollama connection on startup
checkOllamaConnection();
async function checkOllamaConnection() {
try {
const result = await ipcRenderer.invoke('send-to-llm', 'test');
isConnected = result.success;
updateStatus();
} catch (error) {
isConnected = false;
updateStatus();
}
}
function updateStatus() {
const statusEl = document.getElementById('status');
if (isConnected) {
statusEl.innerHTML = '<span class="status-connected">● Connected to Llama</span>';
} else {
statusEl.innerHTML = '<span class="status-disconnected">● Disconnected</span>';
}
}
function addMessage(content, isUser = false, isLoading = false) {
const messagesContainer = document.getElementById('chatMessages');
const messageEl = document.createElement('div');
let className = 'message ';
if (isUser) className += 'user-message';
else if (isLoading) className += 'loading';
else className += 'bot-message';
messageEl.className = className;
messageEl.textContent = content;
messagesContainer.appendChild(messageEl);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return messageEl;
}
let currentQuery = '';
let currentPaths = [];
let conversationContext = [];
let lastBotResponse = '';
// Window control functions
async function closeWindow() {
await ipcRenderer.invoke('window-close');
}
async function minimizeWindow() {
await ipcRenderer.invoke('window-minimize');
}
async function toggleMaximize() {
await ipcRenderer.invoke('window-maximize');
}
async function sendMessage() {
if (isLoading) return;
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message) return;
// Store current query
currentQuery = message;
// Add user message
addMessage(message, true);
input.value = '';
// Show thinking indicator
isLoading = true;
document.getElementById('sendBtn').disabled = true;
showThinkingIndicator('Analyzing your question and generating thinking approaches...');
try {
// Generate thinking paths
const pathsResult = await ipcRenderer.invoke('generate-thinking-paths', message);
if (pathsResult.success && pathsResult.paths.length > 0) {
currentPaths = pathsResult.paths;
displayThinkingPaths(pathsResult.paths, false); // First time, no update button
isConnected = true;
} else {
addMessage(`Error generating thinking paths: ${pathsResult.error || 'Unknown error'}`, false);
hideThinkingPaths();
isConnected = false;
}
} catch (error) {
addMessage(`Connection error: ${error.message}`, false);
hideThinkingPaths();
isConnected = false;
}
isLoading = false;
document.getElementById('sendBtn').disabled = false;
updateStatus();
}
function showThinkingIndicator(message) {
const thinkingPathsEl = document.getElementById('thinkingPaths');
thinkingPathsEl.innerHTML = `
<div class="thinking-indicator">${message}</div>
<div class="input-container">
<input type="text" class="chat-input" id="chatInput" placeholder="Type your question..." onkeypress="handleKeyPress(event)">
<button class="send-btn" id="sendBtn" onclick="sendMessage()">Send</button>
</div>
`;
}
function displayThinkingPaths(paths, showUpdateButton = false, isAutoUpdate = false) {
const thinkingPathsEl = document.getElementById('thinkingPaths');
let pathsHTML = `
<div class="thinking-indicator">Choose a thinking approach and step:</div>
`;
// Show auto-update indicator if this was automatically generated
if (isAutoUpdate) {
pathsHTML += `
<div class="auto-updating">
🔄 Approaches automatically updated based on conversation
</div>
`;
}
// Add manual update button if there's conversation context
if (showUpdateButton) {
pathsHTML += `
<button class="update-paths-btn" id="updatePathsBtn" onclick="updateThinkingPaths()">
🔄 Generate New Approaches Based on Conversation
</button>
`;
}
pathsHTML += '<div class="paths-grid">';
paths.forEach((path, pathIndex) => {
pathsHTML += `
<div class="thinking-path">
<div class="path-header">${path.name}</div>
<ul class="path-steps">
`;
path.steps.forEach((step, stepIndex) => {
pathsHTML += `
<li class="path-step"
data-step="${stepIndex + 1}"
onclick="executeThinkingPath(${pathIndex}, ${stepIndex + 1})">
${step}
</li>
`;
});
pathsHTML += '</ul></div>';
});
pathsHTML += `
</div>
<div class="input-container">
<input type="text" class="chat-input" id="chatInput" placeholder="Type your question..." onkeypress="handleKeyPress(event)">
<button class="send-btn" id="sendBtn" onclick="sendMessage()">Send</button>
</div>
`;
thinkingPathsEl.innerHTML = pathsHTML;
}
async function autoUpdateThinkingPaths(pathName, stepsExecuted) {
try {
const pathsResult = await ipcRenderer.invoke('generate-updated-paths', {
originalQuery: currentQuery,
lastResponse: lastBotResponse,
conversationContext: conversationContext,
lastPathName: pathName,
lastStepsExecuted: stepsExecuted
});
if (pathsResult.success && pathsResult.paths.length > 0) {
currentPaths = pathsResult.paths;
displayThinkingPaths(pathsResult.paths, true, true); // Show as auto-updated
}
} catch (error) {
console.error('Error auto-updating paths:', error);
}
}
async function executeThinkingPath(pathIndex, stepNumber) {
if (isLoading) return;
const path = currentPaths[pathIndex];
if (!path) return;
// Show loading
isLoading = true;
document.getElementById('sendBtn').disabled = true;
// Highlight executed steps
highlightExecutedSteps(pathIndex, stepNumber);
const loadingEl = addMessage(`Following "${path.name}" approach through step ${stepNumber}...`, false, true);
try {
const result = await ipcRenderer.invoke('execute-thinking-path', {
query: currentQuery,
pathName: path.name,
steps: path.steps,
executeUpToStep: stepNumber
});
// Remove loading message
loadingEl.remove();
if (result.success) {
// Store the response for context
lastBotResponse = result.response;
conversationContext.push({
query: currentQuery,
pathName: result.pathName,
stepsExecuted: result.stepsExecuted,
response: result.response
});
// Add response with path info
addMessageWithPath(result.response, result.pathName, result.stepsExecuted);
// Auto-generate new thinking paths based on the response
setTimeout(() => {
autoUpdateThinkingPaths(result.pathName, result.stepsExecuted);
}, 1000); // Small delay for better UX
isConnected = true;
} else {
addMessage(`Error: ${result.error}`, false);
isConnected = false;
}
} catch (error) {
loadingEl.remove();
addMessage(`Connection error: ${error.message}`, false);
isConnected = false;
}
isLoading = false;
document.getElementById('sendBtn').disabled = false;
updateStatus();
}
function highlightExecutedSteps(pathIndex, stepNumber) {
// Reset all steps
document.querySelectorAll('.path-step').forEach(step => {
step.classList.remove('step-executed');
});
// Highlight executed steps in the selected path
const pathElement = document.querySelectorAll('.thinking-path')[pathIndex];
const steps = pathElement.querySelectorAll('.path-step');
for (let i = 0; i < stepNumber; i++) {
if (steps[i]) {
steps[i].classList.add('step-executed');
}
}
}
function addMessageWithPath(content, pathName, stepsExecuted) {
const messagesContainer = document.getElementById('chatMessages');
const messageEl = document.createElement('div');
// Format the response content to highlight thinking steps
const formattedContent = formatThinkingResponse(content);
messageEl.className = 'message bot-message response-with-path';
messageEl.innerHTML = `
<div class="path-info">
🧠 Following "${pathName}" approach - Steps 1-${stepsExecuted} executed
</div>
<div class="response-content">${formattedContent}</div>
`;
messagesContainer.appendChild(messageEl);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return messageEl;
}
function formatThinkingResponse(content) {
// Convert the response to show clear thinking steps with better formatting
let formatted = content;
// Format step headers (e.g., "Step 1:", "**Step 1:**", etc.)
formatted = formatted.replace(/\*\*Step (\d+):(.*?)\*\*/g, '<div class="thinking-step"><h4>🔹 Step $1:$2</h4>');
formatted = formatted.replace(/Step (\d+):(.*?)(?=\n|Step|\*\*|$)/g, '<div class="thinking-step"><h4>🔹 Step $1:$2</h4>');
// Format bold text (**text**)
formatted = formatted.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
// Format bullet points (• or *)
formatted = formatted.replace(/^[•*]\s+(.+)$/gm, '<li>$1</li>');
formatted = formatted.replace(/(<li>.*<\/li>)/gs, '<ul>$1</ul>');
// Clean up multiple consecutive ul tags
formatted = formatted.replace(/<\/ul>\s*<ul>/g, '');
// Format numbered lists (1. 2. 3.)
formatted = formatted.replace(/^(\d+)\.\s+(.+)$/gm, '<li>$2</li>');
// Close thinking step divs (simple approach - close before next step or at summary)
formatted = formatted.replace(/(<div class="thinking-step">.*?)<div class="thinking-step">/gs, '$1</div><div class="thinking-step">');
// Handle summary section
formatted = formatted.replace(/\*\*Summary:\*\*/g, '</div><h3>📋 Summary:</h3>');
formatted = formatted.replace(/Summary:/g, '</div><h3>📋 Summary:</h3>');
// Handle approach headers
formatted = formatted.replace(/\*\*Following "(.*?)" Approach:\*\*/g, '<h3>🎯 Following "$1" Approach:</h3>');
// Format scenario headers (like "Optimistic Scenario", "Moderate Scenario", etc.)
formatted = formatted.replace(/\*\*([A-Z][a-z]+ Scenario[^*]*)\*\*/g, '<h4><strong>$1</strong></h4>');
// Format any remaining bold patterns
formatted = formatted.replace(/\*([^*]+)\*/g, '<em>$1</em>');
// Close any remaining open thinking-step divs
if (formatted.includes('<div class="thinking-step">') && !formatted.endsWith('</div>')) {
formatted += '</div>';
}
// Convert line breaks to proper HTML
formatted = formatted.replace(/\n\n/g, '</p><p>');
formatted = formatted.replace(/\n/g, '<br>');
// Wrap in paragraphs where needed
if (!formatted.includes('<p>') && !formatted.includes('<div>') && !formatted.includes('<h3>')) {
formatted = '<p>' + formatted + '</p>';
}
return formatted;
}
async function updateThinkingPaths() {
if (isLoading) return;
isLoading = true;
document.getElementById('updatePathsBtn').disabled = true;
document.getElementById('updatePathsBtn').textContent = '🔄 Generating...';
try {
const pathsResult = await ipcRenderer.invoke('generate-updated-paths', {
originalQuery: currentQuery,
lastResponse: lastBotResponse,
conversationContext: conversationContext,
lastPathName: conversationContext[conversationContext.length - 1]?.pathName || '',
lastStepsExecuted: conversationContext[conversationContext.length - 1]?.stepsExecuted || 1
});
if (pathsResult.success && pathsResult.paths.length > 0) {
currentPaths = pathsResult.paths;
displayThinkingPaths(pathsResult.paths, true, false); // Manual update, not auto
} else {
console.error('Failed to generate updated paths:', pathsResult.error);
}
} catch (error) {
console.error('Error updating paths:', error);
}
isLoading = false;
}
function hideThinkingPaths() {
const thinkingPathsEl = document.getElementById('thinkingPaths');
thinkingPathsEl.innerHTML = `
<div class="thinking-indicator">Ask me a question and I'll show you different thinking approaches...</div>
<div class="input-container">
<input type="text" class="chat-input" id="chatInput" placeholder="Type your question..." onkeypress="handleKeyPress(event)">
<button class="send-btn" id="sendBtn" onclick="sendMessage()">Send</button>
</div>
`;
}
function updateGuidedActions(actions) {
// This function is no longer used in the new thinking paths system
// Keeping it for backwards compatibility but it won't be called
}
function handleAction(actionType) {
// This function is no longer used in the new thinking paths system
// Keeping it for backwards compatibility but it won't be called
}
function sendQuickMessage(message) {
document.getElementById('chatInput').value = message;
sendMessage();
}
function handleKeyPress(event) {
if (event.key === 'Enter') {
sendMessage();
}
}
// Focus input on load and add keyboard shortcuts
window.addEventListener('load', () => {
document.getElementById('chatInput').focus();
});
// Global keyboard shortcuts
document.addEventListener('keydown', (event) => {
// Ctrl/Cmd + W to close
if ((event.ctrlKey || event.metaKey) && event.key === 'w') {
event.preventDefault();
closeWindow();
}
// Ctrl/Cmd + M to minimize
if ((event.ctrlKey || event.metaKey) && event.key === 'm') {
event.preventDefault();
minimizeWindow();
}
// F11 to toggle maximize
if (event.key === 'F11') {
event.preventDefault();
toggleMaximize();
}
// Ctrl/Cmd + R to update paths (if available)
if ((event.ctrlKey || event.metaKey) && event.key === 'r' && document.getElementById('updatePathsBtn')) {
event.preventDefault();
updateThinkingPaths();
}
});
</script>
</body>
</html>

View file

@ -0,0 +1,348 @@
const { app, BrowserWindow, ipcMain } = require('electron');
const path = require('path');
const axios = require('axios');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
titleBarStyle: 'hidden',
frame: false,
minWidth: 800,
minHeight: 600,
icon: path.join(__dirname, 'assets/icon.png') // Optional: add an icon
});
mainWindow.loadFile('index.html');
// Open DevTools in development
if (process.env.NODE_ENV === 'development') {
mainWindow.webContents.openDevTools();
}
}
// Window control handlers
ipcMain.handle('window-minimize', () => {
if (mainWindow) mainWindow.minimize();
});
ipcMain.handle('window-maximize', () => {
if (mainWindow) {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
}
});
ipcMain.handle('window-close', () => {
if (mainWindow) mainWindow.close();
});
ipcMain.handle('window-is-maximized', () => {
return mainWindow ? mainWindow.isMaximized() : false;
});
// Ollama API communication
ipcMain.handle('send-to-llm', async (event, message) => {
try {
const response = await axios.post('http://localhost:11434/api/generate', {
model: 'gemma3:1b',
prompt: message,
stream: false
});
return {
success: true,
response: response.data.response
};
} catch (error) {
return {
success: false,
error: error.message
};
}
});
// Generate updated thinking paths based on the current conversation context
ipcMain.handle('generate-updated-paths', async (event, { originalQuery, lastResponse, conversationContext, lastPathName, lastStepsExecuted }) => {
try {
const prompt = `Based on this conversation context:
Original Question: "${originalQuery}"
Last Approach Used: "${lastPathName}" (executed ${lastStepsExecuted} steps)
Latest Response: "${lastResponse.substring(0, 500)}..."
Generate 4 NEW thinking approaches that logically continue from where we left off. These should be:
1. Paths that build on the insights already gained
2. Different perspectives or deeper exploration
3. Next logical steps in the thinking process
4. Alternative directions to explore
For each path, provide:
1. A clear approach name (2-4 words)
2. Exactly 3 specific thinking steps for that approach
3. Make steps actionable and build on current progress
Format your response as JSON:
{
"paths": [
{
"name": "Continue Deep",
"steps": ["Build on current insights", "Explore specific implications", "Develop concrete recommendations"]
}
]
}
Focus on continuation and progression rather than starting over.`;
const response = await axios.post('http://localhost:11434/api/generate', {
model: 'gemma3:1b',
prompt: prompt,
stream: false
});
// Try to parse JSON from response
let parsedPaths;
try {
const jsonMatch = response.data.response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
parsedPaths = JSON.parse(jsonMatch[0]);
} else {
throw new Error('No JSON found');
}
} catch (parseError) {
// Fallback: generate contextual paths
parsedPaths = generateContinuationPaths(lastPathName, lastStepsExecuted);
}
return {
success: true,
paths: parsedPaths.paths || []
};
} catch (error) {
return {
success: false,
paths: generateContinuationPaths(lastPathName, lastStepsExecuted),
error: error.message
};
}
});
function generateContinuationPaths(lastPathName, lastStepsExecuted) {
// Generate continuation paths based on what was just executed
return {
paths: [
{
name: "Continue Deep",
steps: ["Build on current insights", "Explore specific implications", "Develop concrete recommendations"]
},
{
name: "New Angle",
steps: ["Approach from different perspective", "Challenge current assumptions", "Synthesize alternative view"]
},
{
name: "Apply Practical",
steps: ["Focus on implementation", "Address real-world constraints", "Create actionable plan"]
},
{
name: "Expand Context",
steps: ["Broaden the scope", "Connect to related domains", "Explore wider implications"]
}
]
};
}
ipcMain.handle('generate-thinking-paths', async (event, query) => {
try {
const prompt = `You are a strategic thinking assistant. For the following query: "${query}"
Generate 4 different thinking approaches/paths to solve this. For each path, provide:
1. A clear approach name (2-4 words)
2. Exactly 3 specific thinking steps for that approach
3. Each step should be a concrete action or analysis
Format your response as JSON:
{
"paths": [
{
"name": "Approach Name",
"steps": ["Step 1 description", "Step 2 description", "Step 3 description"]
}
]
}
Make the paths genuinely different approaches, not just variations. Think like a consultant presenting multiple strategies.`;
const response = await axios.post('http://localhost:11434/api/generate', {
model: 'gemma3:1b',
prompt: prompt,
stream: false
});
// Try to parse JSON from response
let parsedPaths;
try {
// Extract JSON from the response (model might add extra text)
const jsonMatch = response.data.response.match(/\{[\s\S]*\}/);
if (jsonMatch) {
parsedPaths = JSON.parse(jsonMatch[0]);
} else {
throw new Error('No JSON found');
}
} catch (parseError) {
// Fallback: generate default paths
parsedPaths = generateFallbackPaths(query);
}
return {
success: true,
paths: parsedPaths.paths || []
};
} catch (error) {
return {
success: false,
paths: generateFallbackPaths(query),
error: error.message
};
}
});
// Execute a specific thinking path up to a certain step
ipcMain.handle('execute-thinking-path', async (event, { query, pathName, steps, executeUpToStep }) => {
try {
let prompt = `Original question: "${query}"\n\nI'm following the "${pathName}" approach. I will execute these steps and structure my response to show my thinking process:\n\n`;
for (let i = 0; i < executeUpToStep; i++) {
prompt += `Step ${i + 1}: ${steps[i]}\n`;
}
prompt += `\nIMPORTANT: You must think through and execute ONLY the ${executeUpToStep} step${executeUpToStep > 1 ? 's' : ''} listed above. Do NOT go beyond these steps or provide a complete solution.
Structure your response exactly like this format:
**Following "${pathName}" Approach:**
**Step 1: ${steps[0] || 'First Step'}**
[Think through and execute this specific step. Provide your actual reasoning, analysis, and findings for JUST this step. Be detailed but focused only on this step's scope.]
${executeUpToStep > 1 ? `**Step 2: ${steps[1] || 'Second Step'}**
[Now execute step 2, building on step 1. Show your thinking process for this specific step. What new insights emerge? How does this advance from step 1?]` : ''}
${executeUpToStep > 2 ? `**Step 3: ${steps[2] || 'Third Step'}**
[Execute the final step. Complete your analysis up to this point. What conclusions can you draw from steps 1-3?]` : ''}
**Current Progress:**
[Summarize what you've accomplished in these ${executeUpToStep} step${executeUpToStep > 1 ? 's' : ''}. Note what still needs to be explored in future steps.]
REMEMBER: Only execute the steps you're asked to. Don't provide a complete answer - just show your thinking for the specified steps. Use **bold text** for important terms and bullet points for clarity.`;
const response = await axios.post('http://localhost:11434/api/generate', {
model: 'gemma3:1b',
prompt: prompt,
stream: false
});
return {
success: true,
response: response.data.response,
pathName: pathName,
stepsExecuted: executeUpToStep
};
} catch (error) {
return {
success: false,
error: error.message
};
}
});
function generateFallbackPaths(query) {
const isCodeRelated = query.toLowerCase().includes('code') || query.toLowerCase().includes('program') || query.toLowerCase().includes('function');
const isAnalysisRelated = query.toLowerCase().includes('analyz') || query.toLowerCase().includes('data') || query.toLowerCase().includes('research');
if (isCodeRelated) {
return {
paths: [
{
name: "Step by Step",
steps: ["Break down requirements", "Design the algorithm", "Implement and test"]
},
{
name: "Best Practices",
steps: ["Research existing solutions", "Apply design patterns", "Optimize for performance"]
},
{
name: "Quick Prototype",
steps: ["Create minimal version", "Test core functionality", "Iterate and improve"]
},
{
name: "Comprehensive",
steps: ["Plan architecture", "Implement with documentation", "Add error handling"]
}
]
};
} else if (isAnalysisRelated) {
return {
paths: [
{
name: "Data Driven",
steps: ["Gather relevant data", "Analyze patterns", "Draw conclusions"]
},
{
name: "Comparative",
steps: ["Identify alternatives", "Compare pros and cons", "Recommend best option"]
},
{
name: "Root Cause",
steps: ["Identify the problem", "Trace underlying causes", "Propose solutions"]
},
{
name: "Strategic",
steps: ["Define objectives", "Evaluate resources", "Create action plan"]
}
]
};
} else {
return {
paths: [
{
name: "Analytical",
steps: ["Break down the question", "Examine each component", "Synthesize insights"]
},
{
name: "Creative",
steps: ["Brainstorm possibilities", "Explore unconventional ideas", "Refine the best concepts"]
},
{
name: "Practical",
steps: ["Focus on implementation", "Consider real constraints", "Provide actionable steps"]
},
{
name: "Comprehensive",
steps: ["Research thoroughly", "Consider multiple perspectives", "Provide detailed analysis"]
}
]
};
}
}
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,20 @@
{
"name": "guided-llm-chat",
"version": "1.0.0",
"description": "Local LLM chat with guided responses",
"main": "main.js",
"scripts": {
"start": "electron .",
"dev": "concurrently \"npm run start\" \"npm run watch\"",
"watch": "nodemon --exec npm run start",
"build": "electron-builder"
},
"devDependencies": {
"electron": "^27.0.0",
"concurrently": "^8.2.0",
"nodemon": "^3.0.0"
},
"dependencies": {
"axios": "^1.6.0"
}
}

View file

@ -0,0 +1,261 @@
# ThinkPath Chatbot 🧠
*Strategic Thinking Assistant with Local LLM Integration*
*Guided Responses Chatbot*
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Node.js](https://img.shields.io/badge/Node.js-18+-green.svg)](https://nodejs.org/)
[![Electron](https://img.shields.io/badge/Electron-27+-blue.svg)](https://electronjs.org/)
[![Ollama](https://img.shields.io/badge/Ollama-Compatible-orange.svg)](https://ollama.ai/)
> **Stop over-generating. Start thinking strategically.**
ThinkPath AI revolutionizes how you interact with language models by introducing **guided thinking paths** - letting you control exactly how deep the AI goes into any topic, step by step.
![ThinkPath AI Demo](demo.gif)
<video width="100%" controls>
<source src="https://github.com/Ahmed-G-ElTaher/ThinkPath-Chatbot/blob/main/github%20thinkpath%20video.mp4" type="video/mp4">
Your browser does not support the video tag.
</video>
## 🎯 **The Problem We Solve**
### Before ThinkPath AI:
- ❌ **Token Waste**: AI generates full responses when you only need part of the analysis
- ❌ **Over-Information**: Getting overwhelmed with details you didn't ask for
- ❌ **No Control**: Can't pause AI mid-thought to explore different directions
- ❌ **Linear Thinking**: Stuck with one approach, can't easily switch perspectives
- ❌ **High Costs**: Paying for tokens you don't need or want
### With ThinkPath AI:
- ✅ **Precision Control**: Get exactly the depth of analysis you need
- ✅ **Cost Efficiency**: Pay only for the thinking steps you choose
- ✅ **Strategic Flexibility**: Switch between different approaches dynamically
- ✅ **Incremental Discovery**: Build understanding step-by-step
- ✅ **Complete Privacy**: Everything runs locally on your machine
## 🚀 **Key Features**
### 🧭 **Guided Thinking Paths**
- **Dynamic Path Generation**: AI creates 4 different thinking approaches for each question
- **Step-by-Step Execution**: Click any step to execute that approach up to that point
- **Cumulative Logic**: Step 3 = Steps 1 + 2 + 3 executed together
- **Visual Progress**: See exactly which steps have been completed
### 🔄 **Adaptive Conversation**
- **Auto-Path Updates**: New thinking approaches generated after each response
- **Context Awareness**: Paths build on conversation history
- **Continuation Focus**: Next steps always relevant to current progress
### 🎨 **Professional Interface**
- **Modern Design**: Clean, intuitive interface inspired by professional tools
- **Window Controls**: Native minimize, maximize, close buttons
- **Structured Responses**: Bold text, bullet points, professional formatting
- **Keyboard Shortcuts**: Fast navigation and control
### 🔒 **Complete Privacy**
- **Local Processing**: All AI runs on your machine via Ollama
- **No Data Sharing**: Conversations never leave your computer
- **Offline Capable**: Works without internet connection
- **Model Choice**: Use any Ollama-compatible model (Llama, Gemma, etc.)
## 📊 **Cost Comparison**
| Scenario | Traditional Chat | ThinkPath AI | Savings |
|----------|-----------------|--------------|---------|
| Quick clarification | 500 tokens | 150 tokens | **70%** |
| Partial analysis | 1200 tokens | 400 tokens | **67%** |
| Exploring options | 2000 tokens | 600 tokens | **70%** |
| Complex strategy | 3500 tokens | 1000 tokens | **71%** |
*Based on typical usage patterns where users only need partial analysis*
## 🛠 **Installation**
### Prerequisites
- [Node.js](https://nodejs.org/) (v18 or higher)
- [Ollama](https://ollama.ai/) installed and running
- At least one language model downloaded
### Quick Start
1. **Clone the repository**
```bash
git clone https://github.com/Ahmed-G-ElTaher/ThinkPath-Chatbot.git
cd thinkpath-ai
```
2. **Install dependencies**
```bash
npm install
```
3. **Setup Ollama and download a model**
```bash
# Install Ollama (if not already installed)
# Visit https://ollama.ai/download
# Download a fast model
ollama pull gemma3:1b
# Or a more capable model
ollama pull llama3.1:8b
```
4. **Configure the model** (if needed)
```bash
# Edit main.js line 45 to match your model
model: 'gemma3:1b' # Change to your preferred model
```
5. **Run the application**
```bash
npm start
```
## 💡 **How It Works**
### 1. **Ask Any Question**
Type your question and ThinkPath AI generates 4 different thinking approaches:
- Analytical, Creative, Practical, Comprehensive
- Or context-specific paths like "Technical Deep Dive", "Business Impact", etc.
### 2. **Choose Your Path & Step**
Each approach has 3 steps. Click any step to execute that path up to that point:
- Step 1: Execute just the first step
- Step 2: Execute steps 1 and 2
- Step 3: Execute all three steps
### 3. **Get Structured Responses**
AI provides detailed analysis with:
- Clear step-by-step breakdown
- Bold key terms and concepts
- Bullet points for clarity
- Progress summary
### 4. **Continue Exploring**
After each response, new thinking paths automatically appear, building on your conversation context.
## 🎯 **Use Cases**
### 💻 **Software Development & Debugging**
- Model debugging with controllable depth of analysis
- Architecture planning with multiple technical approaches
- Code review with focused, step-by-step examination
- Performance optimization with systematic investigation
### 🤖 **Machine Learning & AI**
- Training issue diagnosis without information overflow
- Hyperparameter tuning with guided experimentation
- Model architecture exploration step by step
- Data pipeline debugging with structured approaches
### 📊 **Data Science**
- Exploratory data analysis with multiple perspectives
- Feature engineering with incremental discovery
- Statistical analysis with controlled complexity
- Visualization planning with step-by-step breakdown
### 💼 **Technical Leadership**
- System architecture decisions with guided analysis
- Technology stack evaluation with structured comparison
- Technical debt assessment with focused investigation
- Team problem-solving with methodical approaches
## ⚙️ **Configuration**
### Model Selection
Edit `main.js` to use different models:
```javascript
// Line 45: Change the model name
model: 'llama3.1:8b' // or 'gemma3:1b', 'mistral:7b', etc.
```
### UI Customization
Modify `index.html` CSS for:
- Color schemes
- Typography
- Layout preferences
- Window styling
### Keyboard Shortcuts
- `Ctrl/Cmd + W` - Close window
- `Ctrl/Cmd + M` - Minimize window
- `F11` - Toggle maximize
- `Ctrl/Cmd + R` - Refresh thinking paths
## 🔮 **Future Development**
### 🎯 **Planned Features**
- [ ] **Multi-Model Support**: Run multiple models simultaneously for different perspectives
- [ ] **Custom Thinking Templates**: Create and save your own thinking approaches
- [ ] **Conversation Export**: Save thinking sessions as structured documents
- [ ] **Voice Integration**: Speech-to-text for natural interaction
- [ ] **Team Collaboration**: Share thinking sessions with team members
- [ ] **Analytics Dashboard**: Track thinking patterns and productivity
- [ ] **Plugin System**: Extend functionality with custom tools
- [ ] **Mobile App**: iOS/Android versions with cloud sync
### 🏗 **Potential Applications**
#### 🎓 **Education Sector**
- **Socratic Learning Platform**: Guide students through step-by-step problem solving
- **Research Assistant**: Help students explore topics with structured thinking
- **Thesis Planning**: Break down complex research into manageable steps
#### 🏥 **Healthcare**
- **Diagnostic Support**: Multi-approach medical analysis (symptoms → differential → testing)
- **Treatment Planning**: Step-by-step care plan development
- **Medical Education**: Case-based learning with guided analysis
#### ⚖️ **Legal**
- **Case Analysis**: Multiple legal approaches to complex cases
- **Contract Review**: Systematic document analysis
- **Legal Research**: Structured exploration of legal precedents
#### 🏭 **Enterprise**
- **Decision Support**: Strategic planning with guided thinking
- **Risk Assessment**: Multi-perspective risk analysis
- **Training Programs**: Skill development with structured learning
#### 🔬 **Research & Development**
- **Scientific Method**: Hypothesis → Experiment → Analysis workflows
- **Innovation Labs**: Systematic ideation and validation
- **Patent Analysis**: Multi-angle IP research
## 🤝 **Contributing**
We welcome contributions! Here's how you can help:
1. **Fork the repository**
2. **Create a feature branch**: `git checkout -b feature/amazing-feature`
3. **Commit changes**: `git commit -m 'Add amazing feature'`
4. **Push to branch**: `git push origin feature/amazing-feature`
5. **Open a Pull Request**
### Development Areas
- **UI/UX Improvements**: Better visual design and user experience
- **Model Integration**: Support for new LLM providers
- **Performance**: Optimization for faster response times
- **Features**: New thinking methodologies and tools
- **Documentation**: Tutorials, guides, and examples
## 📝 **License**
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## 🙏 **Acknowledgments**
- **Ollama**: For making local LLM deployment accessible
- **Electron**: For cross-platform desktop app framework
- **AI Community**: For advancing open-source language models
- **Strategic Thinking**: Inspired by consulting methodologies and structured problem-solving
---
**Built with ❤️ for strategic thinkers who value precision, privacy, and control.**
*Stop over-generating. Start thinking strategically with ThinkPath AI.*
**Developed in collaboration with Claude AI** - demonstrating that the future of software development lies in thoughtful human-AI partnership, where AI amplifies human creativity and strategic thinking rather than replacing it. 🤖🤝👨‍💻