Update gpt/index.html
This commit is contained in:
+135
-194
@@ -379,42 +379,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/* ============================================
|
/* ============================================
|
||||||
AI CONFIGURATION
|
AI CONFIGURATION
|
||||||
============================================ */
|
============================================ */
|
||||||
let aiConfig = {
|
let aiConfig = {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
endpoint: '',
|
endpoint: '',
|
||||||
model: '',
|
model: '',
|
||||||
apiKey: '',
|
apiKey: '',
|
||||||
type: 'ollama' // ollama, openai, lmstudio, gpt4all
|
type: 'openai'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Load saved config
|
// Load saved config
|
||||||
const savedConfig = localStorage.getItem('neuralTerminalConfig');
|
const savedConfig = localStorage.getItem('neuralTerminalConfig');
|
||||||
if (savedConfig) {
|
if (savedConfig) {
|
||||||
aiConfig = JSON.parse(savedConfig);
|
aiConfig = JSON.parse(savedConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
TERMINAL STATE
|
TERMINAL STATE
|
||||||
============================================ */
|
============================================ */
|
||||||
const output = document.getElementById('output');
|
const output = document.getElementById('output');
|
||||||
const inputField = document.getElementById('inputField');
|
const inputField = document.getElementById('inputField');
|
||||||
const statusDot = document.getElementById('statusDot');
|
const statusDot = document.getElementById('statusDot');
|
||||||
const statusText = document.getElementById('statusText');
|
const statusText = document.getElementById('statusText');
|
||||||
const timeDisplay = document.getElementById('timeDisplay');
|
const timeDisplay = document.getElementById('timeDisplay');
|
||||||
const configPanel = document.getElementById('configPanel');
|
const configPanel = document.getElementById('configPanel');
|
||||||
|
|
||||||
let commandHistory = [];
|
let commandHistory = [];
|
||||||
let historyIndex = -1;
|
let historyIndex = -1;
|
||||||
let isProcessing = false;
|
let isProcessing = false;
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
UI FUNCTIONS
|
UI
|
||||||
============================================ */
|
============================================ */
|
||||||
function updateStatus() {
|
function updateStatus() {
|
||||||
if (aiConfig.enabled) {
|
if (aiConfig.enabled) {
|
||||||
statusDot.className = 'status-dot online';
|
statusDot.className = 'status-dot online';
|
||||||
statusText.textContent = 'ONLINE';
|
statusText.textContent = 'ONLINE';
|
||||||
@@ -422,42 +422,42 @@
|
|||||||
statusDot.className = 'status-dot offline';
|
statusDot.className = 'status-dot offline';
|
||||||
statusText.textContent = 'OFFLINE';
|
statusText.textContent = 'OFFLINE';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTime() {
|
function updateTime() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
timeDisplay.textContent = now.toLocaleTimeString('en-US', { hour12: false });
|
timeDisplay.textContent = now.toLocaleTimeString('en-US', { hour12: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLine(text, type = '') {
|
function addLine(text, type = '') {
|
||||||
const line = document.createElement('div');
|
const line = document.createElement('div');
|
||||||
line.className = 'output-line' + (type ? ' ' + type : '');
|
line.className = 'output-line' + (type ? ' ' + type : '');
|
||||||
line.textContent = text;
|
line.textContent = text;
|
||||||
output.appendChild(line);
|
output.appendChild(line);
|
||||||
output.scrollTop = output.scrollHeight;
|
output.scrollTop = output.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearOutput() {
|
function clearOutput() {
|
||||||
output.innerHTML = '';
|
output.innerHTML = '';
|
||||||
addLine('Screen cleared', 'system');
|
addLine('Screen cleared', 'system');
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
CONFIG PANEL
|
CONFIG PANEL
|
||||||
============================================ */
|
============================================ */
|
||||||
function openConfig() {
|
function openConfig() {
|
||||||
document.getElementById('endpointInput').value = aiConfig.endpoint;
|
document.getElementById('endpointInput').value = aiConfig.endpoint;
|
||||||
document.getElementById('modelInput').value = aiConfig.model;
|
document.getElementById('modelInput').value = aiConfig.model;
|
||||||
document.getElementById('apiKeyInput').value = aiConfig.apiKey;
|
document.getElementById('apiKeyInput').value = aiConfig.apiKey;
|
||||||
configPanel.classList.add('visible');
|
configPanel.classList.add('visible');
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeConfig() {
|
function closeConfig() {
|
||||||
configPanel.classList.remove('visible');
|
configPanel.classList.remove('visible');
|
||||||
inputField.focus();
|
inputField.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveConfig() {
|
function saveConfig() {
|
||||||
aiConfig.endpoint = document.getElementById('endpointInput').value.trim();
|
aiConfig.endpoint = document.getElementById('endpointInput').value.trim();
|
||||||
aiConfig.model = document.getElementById('modelInput').value.trim();
|
aiConfig.model = document.getElementById('modelInput').value.trim();
|
||||||
aiConfig.apiKey = document.getElementById('apiKeyInput').value.trim();
|
aiConfig.apiKey = document.getElementById('apiKeyInput').value.trim();
|
||||||
@@ -467,23 +467,28 @@
|
|||||||
updateStatus();
|
updateStatus();
|
||||||
closeConfig();
|
closeConfig();
|
||||||
addLine('Configuration saved', 'system');
|
addLine('Configuration saved', 'system');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function testConnection() {
|
/* ============================================
|
||||||
|
TEST CONNECTION (FIXED)
|
||||||
|
============================================ */
|
||||||
|
async function testConnection() {
|
||||||
const endpoint = document.getElementById('endpointInput').value.trim();
|
const endpoint = document.getElementById('endpointInput').value.trim();
|
||||||
const model = document.getElementById('modelInput').value.trim();
|
|
||||||
|
|
||||||
if (!endpoint || !model) {
|
if (!endpoint) {
|
||||||
alert('Please enter endpoint and model name');
|
alert('Please enter endpoint');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Try a simple request based on detected type
|
// ONLY OpenAI-style test
|
||||||
const testEndpoint = endpoint.replace('/chat', '/tags').replace('/completions', '/models');
|
const testUrl = endpoint.replace('/chat/completions', '/models');
|
||||||
const response = await fetch(testEndpoint, {
|
|
||||||
|
const response = await fetch(testUrl, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: aiConfig.apiKey ? { 'Authorization': 'Bearer ' + aiConfig.apiKey } : {}
|
headers: aiConfig.apiKey
|
||||||
|
? { 'Authorization': 'Bearer ' + aiConfig.apiKey }
|
||||||
|
: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
@@ -494,16 +499,28 @@
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert('Connection error: ' + e.message);
|
alert('Connection error: ' + e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preset handling
|
/* ============================================
|
||||||
document.getElementById('presetSelect').addEventListener('change', function() {
|
PRESETS (CLEAN)
|
||||||
|
============================================ */
|
||||||
|
document.getElementById('presetSelect').addEventListener('change', function () {
|
||||||
const presets = {
|
const presets = {
|
||||||
ollama: { endpoint: 'http://localhost:11434/api/chat', model: 'llama2', type: 'ollama' },
|
local: {
|
||||||
lmstudio: { endpoint: 'http://localhost:1234/v1/chat/completions', model: 'local-model', type: 'openai' },
|
endpoint: 'http://localhost:4891/v1/chat/completions',
|
||||||
gpt4all: { endpoint: 'http://localhost:4891/v1/chat/completions', model: 'gpt4all-model', type: 'openai' },
|
model: 'local-model',
|
||||||
openai: { endpoint: 'https://api.openai.com/v1/chat/completions', model: 'gpt-4o-mini', type: 'openai' },
|
type: 'openai'
|
||||||
claude: { endpoint: 'https://api.anthropic.com/v1/messages', model: 'claude-sonnet-4-20250514', type: 'claude' }
|
},
|
||||||
|
openai: {
|
||||||
|
endpoint: 'https://api.openai.com/v1/chat/completions',
|
||||||
|
model: 'gpt-4o-mini',
|
||||||
|
type: 'openai'
|
||||||
|
},
|
||||||
|
claude: {
|
||||||
|
endpoint: 'https://api.anthropic.com/v1/messages',
|
||||||
|
model: 'claude-sonnet-4-20250514',
|
||||||
|
type: 'claude'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const preset = presets[this.value];
|
const preset = presets[this.value];
|
||||||
@@ -512,109 +529,53 @@
|
|||||||
document.getElementById('modelInput').value = preset.model;
|
document.getElementById('modelInput').value = preset.model;
|
||||||
aiConfig.type = preset.type;
|
aiConfig.type = preset.type;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
HELPER FUNCTIONS
|
HELPERS
|
||||||
============================================ */
|
============================================ */
|
||||||
function getFormattedDate() {
|
function getFormattedDate() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dd = String(now.getDate()).padStart(2, '0');
|
return `${String(now.getDate()).padStart(2, '0')}/${
|
||||||
const mm = String(now.getMonth() + 1).padStart(2, '0');
|
String(now.getMonth() + 1).padStart(2, '0')
|
||||||
const yyyy = now.getFullYear();
|
}/${now.getFullYear()}:${String(now.getHours()).padStart(2, '0')}/${
|
||||||
const HH = String(now.getHours()).padStart(2, '0');
|
String(now.getMinutes()).padStart(2, '0')
|
||||||
const MM = String(now.getMinutes()).padStart(2, '0');
|
}/${String(now.getSeconds()).padStart(2, '0')}`;
|
||||||
const SS = String(now.getSeconds()).padStart(2, '0');
|
}
|
||||||
return `${dd}/${mm}/${yyyy}:${HH}/${MM}/${SS}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStatusInfo() {
|
function getStatusInfo() {
|
||||||
return `[Connection: ${aiConfig.enabled ? 'ONLINE' : 'OFFLINE'}, Endpoint: ${aiConfig.endpoint || 'none'}, Model: ${aiConfig.model || 'none'}]`;
|
return `[Connection: ${aiConfig.enabled ? 'ONLINE' : 'OFFLINE'}, Endpoint: ${aiConfig.endpoint || 'none'}, Model: ${aiConfig.model || 'none'}]`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function preprocessMessage(message) {
|
function preprocessMessage(message) {
|
||||||
// Replace "date" keyword with actual date (case insensitive, whole word)
|
return message
|
||||||
let processed = message.replace(/\bdate\b/gi, `date (${getFormattedDate()})`);
|
.replace(/\bdate\b/gi, `date (${getFormattedDate()})`)
|
||||||
// Replace "status" keyword with actual status info (case insensitive, whole word)
|
.replace(/\bstatus\b/gi, `status ${getStatusInfo()}`);
|
||||||
processed = processed.replace(/\bstatus\b/gi, `status ${getStatusInfo()}`);
|
}
|
||||||
return processed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
AI COMMUNICATION
|
AI CALL (OPENAI ONLY)
|
||||||
============================================ */
|
============================================ */
|
||||||
async function queryAI(message) {
|
async function queryAI(message) {
|
||||||
if (!aiConfig.enabled) {
|
if (!aiConfig.enabled) {
|
||||||
addLine('', '');
|
addLine('AI is offline. Use config to connect.', 'error');
|
||||||
addLine('The AI you are trying to reach is not online at the moment.', 'error');
|
|
||||||
addLine('', '');
|
|
||||||
addLine('To connect an AI backend:', 'system');
|
|
||||||
addLine(' 1. Type "config" to open settings', 'system');
|
|
||||||
addLine(' 2. Choose a preset or enter custom endpoint', 'system');
|
|
||||||
addLine(' 3. Supported: Ollama, LM Studio, GPT4All, OpenAI, Claude', 'system');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
isProcessing = true;
|
isProcessing = true;
|
||||||
|
|
||||||
// Preprocess message to replace date/status keywords
|
|
||||||
const processedMessage = preprocessMessage(message);
|
const processedMessage = preprocessMessage(message);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let response;
|
|
||||||
|
|
||||||
if (aiConfig.type === 'ollama') {
|
|
||||||
response = await fetch(aiConfig.endpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: aiConfig.model,
|
|
||||||
messages: [{ role: 'user', content: processedMessage }],
|
|
||||||
stream: false
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
if (data.message && data.message.content) {
|
|
||||||
addLine(data.message.content, 'ai');
|
|
||||||
} else if (data.error) {
|
|
||||||
addLine('Error: ' + data.error, 'error');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (aiConfig.type === 'claude') {
|
|
||||||
// Claude/Anthropic API
|
|
||||||
response = await fetch(aiConfig.endpoint, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'x-api-key': aiConfig.apiKey,
|
|
||||||
'anthropic-version': '2023-06-01',
|
|
||||||
'anthropic-dangerous-direct-browser-access': 'true'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
model: aiConfig.model,
|
|
||||||
max_tokens: 1024,
|
|
||||||
messages: [{ role: 'user', content: processedMessage }]
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
if (data.content && data.content[0]) {
|
|
||||||
addLine(data.content[0].text, 'ai');
|
|
||||||
} else if (data.error) {
|
|
||||||
addLine('Error: ' + (data.error.message || data.error), 'error');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// OpenAI-compatible API (LM Studio, GPT4All, OpenAI, etc.)
|
|
||||||
const headers = { 'Content-Type': 'application/json' };
|
const headers = { 'Content-Type': 'application/json' };
|
||||||
|
|
||||||
if (aiConfig.apiKey) {
|
if (aiConfig.apiKey) {
|
||||||
headers['Authorization'] = 'Bearer ' + aiConfig.apiKey;
|
headers['Authorization'] = 'Bearer ' + aiConfig.apiKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
response = await fetch(aiConfig.endpoint, {
|
const response = await fetch(aiConfig.endpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: headers,
|
headers,
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
model: aiConfig.model,
|
model: aiConfig.model,
|
||||||
messages: [{ role: 'user', content: processedMessage }],
|
messages: [{ role: 'user', content: processedMessage }],
|
||||||
@@ -623,25 +584,25 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.choices && data.choices[0]) {
|
if (data.choices && data.choices[0]) {
|
||||||
addLine(data.choices[0].message.content, 'ai');
|
addLine(data.choices[0].message.content, 'ai');
|
||||||
} else if (data.error) {
|
} else if (data.error) {
|
||||||
addLine('Error: ' + (data.error.message || data.error), 'error');
|
addLine('Error: ' + (data.error.message || data.error), 'error');
|
||||||
|
} else {
|
||||||
|
addLine('Invalid response from server', 'error');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
addLine('Connection error: ' + e.message, 'error');
|
addLine('Connection error: ' + e.message, 'error');
|
||||||
addLine('Make sure your AI backend is running', 'system');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isProcessing = false;
|
isProcessing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
COMMAND PROCESSING
|
COMMANDS
|
||||||
============================================ */
|
============================================ */
|
||||||
function processCommand(input) {
|
function processCommand(input) {
|
||||||
const trimmed = input.trim();
|
const trimmed = input.trim();
|
||||||
if (!trimmed) return;
|
if (!trimmed) return;
|
||||||
|
|
||||||
@@ -649,21 +610,9 @@
|
|||||||
commandHistory.unshift(trimmed);
|
commandHistory.unshift(trimmed);
|
||||||
historyIndex = -1;
|
historyIndex = -1;
|
||||||
|
|
||||||
const cmd = trimmed.toLowerCase();
|
switch (trimmed.toLowerCase()) {
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case 'help':
|
case 'help':
|
||||||
addLine('', '');
|
addLine('help | config | clear | date | status', 'system');
|
||||||
addLine('Available commands:', 'system');
|
|
||||||
addLine(' help - Show this help', 'system');
|
|
||||||
addLine(' config - Configure AI backend', 'system');
|
|
||||||
addLine(' clear - Clear screen', 'system');
|
|
||||||
addLine('', '');
|
|
||||||
addLine('Keywords (replaced in messages to AI):', 'system');
|
|
||||||
addLine(' date - Replaced with current date/time', 'system');
|
|
||||||
addLine(' status - Replaced with connection info', 'system');
|
|
||||||
addLine('', '');
|
|
||||||
addLine('Any other input is sent to the AI', 'system');
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'config':
|
case 'config':
|
||||||
@@ -674,50 +623,42 @@
|
|||||||
clearOutput();
|
clearOutput();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'date':
|
||||||
|
addLine(getFormattedDate(), 'system');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'status':
|
||||||
|
addLine(getStatusInfo(), 'system');
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
queryAI(trimmed);
|
queryAI(trimmed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
INPUT HANDLING
|
INPUT
|
||||||
============================================ */
|
============================================ */
|
||||||
inputField.addEventListener('keydown', function(e) {
|
inputField.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Enter' && !isProcessing) {
|
if (e.key === 'Enter' && !isProcessing) {
|
||||||
processCommand(this.value);
|
processCommand(inputField.value);
|
||||||
this.value = '';
|
inputField.value = '';
|
||||||
} else if (e.key === 'ArrowUp') {
|
|
||||||
e.preventDefault();
|
|
||||||
if (historyIndex < commandHistory.length - 1) {
|
|
||||||
historyIndex++;
|
|
||||||
this.value = commandHistory[historyIndex];
|
|
||||||
}
|
}
|
||||||
} else if (e.key === 'ArrowDown') {
|
});
|
||||||
e.preventDefault();
|
|
||||||
if (historyIndex > 0) {
|
|
||||||
historyIndex--;
|
|
||||||
this.value = commandHistory[historyIndex];
|
|
||||||
} else {
|
|
||||||
historyIndex = -1;
|
|
||||||
this.value = '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/* ============================================
|
/* ============================================
|
||||||
INITIALIZATION
|
INIT
|
||||||
============================================ */
|
============================================ */
|
||||||
updateStatus();
|
updateStatus();
|
||||||
updateTime();
|
updateTime();
|
||||||
setInterval(updateTime, 1000);
|
setInterval(updateTime, 1000);
|
||||||
inputField.focus();
|
inputField.focus();
|
||||||
|
|
||||||
// Click anywhere to focus input
|
document.addEventListener('click', (e) => {
|
||||||
document.addEventListener('click', function(e) {
|
|
||||||
if (!configPanel.contains(e.target)) {
|
if (!configPanel.contains(e.target)) {
|
||||||
inputField.focus();
|
inputField.focus();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user