MCPSynapseChat / index.html
Omar ID EL MOUMEN
Final version
8227e25
<!DOCTYPE html>
<html>
<head>
<title>Chat System with LLM Proxy</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
}
.container {
display: flex;
gap: 20px;
height: 90vh;
}
.panel {
flex: 1;
border: 1px solid #ddd;
border-radius: 8px;
padding: 15px;
display: flex;
flex-direction: column;
}
h2 {
margin-top: 0;
border-bottom: 1px solid #eee;
padding-bottom: 10px;
}
.chat-container {
height: 300px;
overflow-y: scroll;
border: 1px solid #eee;
padding: 10px;
margin-bottom: 10px;
flex: 1;
}
.input-container {
display: flex;
gap: 10px;
}
input[type="text"],
input[type="password"] {
flex: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
button {
padding: 8px 15px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
.message {
margin-bottom: 10px;
padding: 8px;
border-radius: 8px;
}
.user-message {
background-color: #e1f5fe;
align-self: flex-end;
}
.assistant-message {
background-color: #f1f1f1;
}
.connection-status {
color: #666;
font-size: 0.9em;
margin-top: 10px;
}
.message-entry {
margin: 5px 0;
padding: 8px;
border-radius: 8px;
background: white;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
font-family: monospace;
}
.incoming {
border-left: 4px solid #4CAF50;
}
.outgoing {
border-left: 4px solid #2196F3;
}
.system {
border-left: 4px solid #9C27B0;
}
.error {
border-left: 4px solid #F44336;
}
.message-header {
display: flex;
justify-content: space-between;
font-size: 0.8em;
color: #666;
margin-bottom: 4px;
}
.tabs {
display: flex;
margin-bottom: 15px;
}
.tab {
padding: 10px 20px;
cursor: pointer;
border: 1px solid #ddd;
border-radius: 4px 4px 0 0;
margin-right: 5px;
}
.tab.active {
background-color: #f1f1f1;
border-bottom: none;
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
flex: 1;
display: flex;
flex-direction: column;
}
</style>
</head>
<body>
<div class="tabs">
<div class="tab active" onclick="switchTab('chat')">Chat Client</div>
<div class="tab" onclick="switchTab('proxy')">Proxy Configuration</div>
</div>
<div class="container">
<!-- Chat Client Panel -->
<div id="chat-tab" class="tab-content active panel">
<h2>Chat Client</h2>
<div id="chat" class="chat-container"></div>
<div class="input-container">
<input id="msg" type="text" placeholder="Type your message here...">
<button onclick="sendMessage()">Send</button>
</div>
<div id="client-status" class="connection-status">Connecting...</div>
</div>
<!-- Proxy Configuration Panel -->
<div id="proxy-tab" class="tab-content panel">
<h2>LLM Proxy Configuration</h2>
<div style="margin-bottom: 20px;">
<input type="password" id="apiKey" placeholder="Enter API Key" style="width: 100%;">
<button onclick="initializeClient()" style="margin-top: 10px;">Fetch Models</button>
</div>
<select id="modelSelect" style="width: 100%; margin-bottom: 20px;"></select>
<div id="systemStatus" class="connection-status"></div>
<h3>Message Flow</h3>
<div id="messageFlow"
style="flex: 1; border: 1px solid #eee; padding: 10px; overflow-y: auto; background: #f9f9f9;">
<div style="text-align: center; color: #999; margin-bottom: 10px;">Message Flow</div>
</div>
<div id="detailedStatus" class="connection-status"></div>
</div>
</div>
<script>
function showStatus(message, type = 'info') {
const statusDiv = document.getElementById('systemStatus');
statusDiv.innerHTML = `<div style="color: ${type === 'error' ? '#F44336' : '#4CAF50'}">${message}</div>`;
addMessageEntry('system', 'system', 'proxy', message);
}
// Tab switching functionality
function switchTab(tabName) {
document.querySelectorAll('.tab').forEach(tab => tab.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(content => content.classList.remove('active'));
document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active');
document.getElementById(`${tabName}-tab`).classList.add('active');
}
// Client WebSocket
const clientWs = new WebSocket('wss://' + window.location.host + '/ws');
clientWs.onopen = () => {
clientWs.send(JSON.stringify({
source: 'user'
}));
document.getElementById('client-status').textContent = 'Connected';
};
clientWs.onclose = () => {
document.getElementById('client-status').textContent = 'Disconnected';
};
clientWs.onmessage = e => {
const msg = JSON.parse(e.data);
const chatDiv = document.getElementById('chat');
chatDiv.innerHTML += `<div class="message assistant-message">${msg.content}</div>`;
chatDiv.scrollTop = chatDiv.scrollHeight;
};
function sendMessage() {
const input = document.getElementById('msg');
const content = input.value.trim();
if (content) {
const message = {
content: content,
source: 'user',
destination: 'proxy',
request_id: generateUUID()
};
clientWs.send(JSON.stringify(message));
const chatDiv = document.getElementById('chat');
chatDiv.innerHTML += `<div class="message user-message">${content}</div>`;
chatDiv.scrollTop = chatDiv.scrollHeight;
input.value = '';
}
}
document.getElementById('msg').addEventListener('keypress', function (e) {
if (e.key === 'Enter') {
sendMessage();
}
});
// Proxy WebSocket
let proxyWs = new WebSocket('wss://' + window.location.host + '/ws');
proxyWs.onopen = () => {
proxyWs.send(JSON.stringify({
source: 'proxy'
}));
showStatus('Connected to server');
};
proxyWs.onclose = () => {
showStatus('Disconnected from server', 'error');
};
proxyWs.onmessage = async e => {
const msg = JSON.parse(e.data);
// Display incoming messages
if (msg.destination === 'proxy') {
let tools = null
addMessageEntry('incoming', msg.source, 'proxy', msg.content);
document.getElementById('detailedStatus').textContent = `Processing ${msg.source} request...`;
try {
const response = await fetch("/list-tools");
tools = await response.json();
} catch (error) {
console.log(`Failed to fetch tools : ${error}`);
tools = null;
}
try {
if (!agentClient) {
throw new Error(
"LLM client not initialized. Please enter API key and fetch models first.");
}
if (!currentModel) {
throw new Error("No model selected. Please select a model first.");
}
let llmResponse = await agentClient.call(
currentModel,
msg.content,
conversationHistory,
tools
);
conversationHistory = llmResponse.history
// Display outgoing response
addMessageEntry('outgoing', 'proxy', msg.source, llmResponse.response);
if(llmResponse.response.tool_calls != null){
try {
console.log("Calling ....")
const toolCalls = await fetch("/call-tools", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({tool_calls: llmResponse.response.tool_calls})
})
const toolCallsJson = await toolCalls.json()
console.log("Succeed !")
for(const toolCall of toolCallsJson){
conversationHistory.push(toolCall)
}
llmResponse = await agentClient.call(
currentModel,
null,
conversationHistory,
null
)
conversationHistory = llmResponse.history
} catch (error) {
throw new Error("Error on calling tools " + error)
}
}
const responseMsg = {
request_id: msg.request_id,
content: llmResponse.response.content,
source: 'proxy',
destination: msg.source
};
proxyWs.send(JSON.stringify(responseMsg));
document.getElementById('detailedStatus').textContent = `Response sent to ${msg.source}`;
} catch (error) {
addMessageEntry('error', 'system', 'proxy', `Error: ${error.message}`);
const errorResponse = {
request_id: msg.request_id,
content: `Error: ${error.message}`,
source: 'proxy',
destination: msg.source
};
proxyWs.send(JSON.stringify(errorResponse));
}
}
};
function generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
</script>
<script src="/static/proxy_llm.js"></script>
</body>
</html>