Spaces:
Sleeping
Sleeping
<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> | |