Spaces:
Sleeping
Sleeping
File size: 11,512 Bytes
14f00fb 21974f1 d224495 21974f1 14f00fb 21974f1 14f00fb 21974f1 b7f3059 50cadbb b7f3059 50cadbb 21974f1 b7f3059 21974f1 b7f3059 50cadbb b7f3059 50cadbb b7f3059 21974f1 b7f3059 d224495 21974f1 d224495 14f00fb 7d5dfa0 14f00fb 21974f1 14f00fb 21974f1 786de4e 32c354e 83089c7 d224495 21974f1 d224495 789b8b7 d224495 ed04063 7183bac ed04063 d224495 21974f1 d224495 50cadbb 32c354e 21974f1 c7ab2fb 21974f1 d224495 786de4e 14f00fb 21974f1 14f00fb 7d5dfa0 14f00fb 21974f1 14f00fb 21974f1 7d5dfa0 14f00fb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Client WebSocket Simple</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
margin: 40px;
background-color: #f0f2f5;
}
.container {
max-width: 600px;
margin: auto;
padding: 20px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
#status {
padding: 10px;
border-radius: 5px;
font-weight: bold;
margin-bottom: 15px;
}
.connected {
background-color: #e6ffed;
color: #2f6f43;
}
.disconnected {
background-color: #ffeef0;
color: #c53030;
}
#logs {
list-style-type: none;
padding: 0;
margin-top: 20px;
background-color: #f7f7f7;
border: 1px solid #ddd;
border-radius: 5px;
height: 200px;
overflow-y: scroll;
padding: 10px;
}
#logs li {
padding: 5px;
border-bottom: 1px solid #eee;
}
</style>
</head>
<body>
<div class="container">
<h2>Client WebSocket pour API Mock</h2>
<div id="status" class="disconnected">Déconnecté</div>
<div style="margin-bottom: 20px;">
<label for="apiKey" style="display: block; margin-bottom: 5px; font-weight: bold;">Clé API :</label>
<input type="password" id="apiKey" placeholder="Entrez votre clé API..." style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
<div style="margin-bottom: 20px;">
<label for="defaultModel" style="display: block; margin-bottom: 5px; font-weight: bold;">Modèle par défaut :</label>
<select id="defaultModel" style="width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-size: 14px;">
<option value="">Chargement des modèles...</option>
</select>
<small style="color: #666; font-size: 12px;">Modèle utilisé si non spécifié par Python.</small>
</div>
<small style="color: #666; font-size: 12px;">La clé est stockée uniquement dans votre navigateur pour cette session.</small>
</div>
<h3>Logs de Communication :</h3>
<ul id="logs">
<li>En attente de connexion...</li>
</ul>
</div>
<script>
const statusDiv = document.getElementById('status');
const logsList = document.getElementById('logs');
let ws;
function addLog(message) {
const li = document.createElement('li');
li.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logsList.appendChild(li);
logsList.scrollTop = logsList.scrollHeight; // Auto-scroll
}
// OpenAI API Call for private mode
async function callOpenAI(prompt, model = 'gemini-2.5-pro') {
console.log('prompt:', prompt)
console.log('model:', model)
// Récupérer la clé API depuis l'input
const apiKey = document.getElementById('apiKey').value.trim();
if (!apiKey) {
throw new Error('Clé API manquante. Veuillez la saisir dans le champ prévu.');
}
try {
const response = await fetch('https://llm.synapse.thalescloud.io/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`
},
body: JSON.stringify({
model: model,
messages: [
{
role: 'user',
content: prompt
}
]
})
});
console.log('response:', response)
if (!response.ok) {
throw new Error(`OpenAI API error: ${response.status}`);
}
const data = await response.json();
console.log('data:', data)
return data.choices[0].message.content.trim();
} catch (error) {
console.error('Error calling OpenAI:', error);
throw error;
}
}
// Fonction pour récupérer la liste des modèles
async function loadAvailableModels() {
const apiKey = document.getElementById('apiKey').value.trim();
const modelSelect = document.getElementById('defaultModel');
if (!apiKey) {
modelSelect.innerHTML = '<option value="gemini-2.5-pro">gemini-2.5-pro (défaut)</option>';
addLog('Saisissez votre clé API pour charger les modèles disponibles');
return;
}
try {
addLog('Chargement des modèles disponibles...');
const response = await fetch('https://llm.synapse.thalescloud.io/v1/models', {
method: 'GET',
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
if (!response.ok) {
throw new Error(`Erreur API: ${response.status}`);
}
const data = await response.json();
// Vider le select et ajouter les options
modelSelect.innerHTML = '';
if (data.data && Array.isArray(data.data)) {
data.data.forEach(model => {
const option = document.createElement('option');
option.value = model.id;
option.textContent = model.id;
if (model.id === 'gemini-2.5-pro') {
option.selected = true; // Sélectionner par défaut
}
modelSelect.appendChild(option);
});
addLog(`${data.data.length} modèles chargés`);
} else {
// Fallback si la structure de réponse est différente
modelSelect.innerHTML = '<option value="gemini-2.5-pro" selected>gemini-2.5-pro (défaut)</option>';
}
} catch (error) {
console.error('Erreur lors du chargement des modèles:', error);
addLog(`Erreur chargement modèles: ${error.message}`);
// Ajouter une option par défaut en cas d'erreur
modelSelect.innerHTML = '<option value="gemini-2.5-pro" selected>gemini-2.5-pro (défaut)</option>';
}
}
// Écouter les changements sur le champ API Key pour recharger les modèles
document.getElementById('apiKey').addEventListener('input', debounce(loadAvailableModels, 1000));
// Fonction debounce pour éviter trop d'appels
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function connect() {
// Adapte le protocole (ws ou wss pour le sécurisé)
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/ws`;
ws = new WebSocket(wsUrl);
ws.onopen = function() {
statusDiv.textContent = 'Connecté';
statusDiv.className = 'connected';
addLog('Connexion WebSocket établie.');
};
ws.onmessage = async function(event) {
try {
const startTime = performance.now();
let prompt, model;
// Essayer de parser comme JSON (nouveau format)
try {
const messageData = JSON.parse(event.data);
// const messageData = event.data;
prompt = messageData.prompt;
model = messageData.model;
} catch (parseError) {
// Si ce n'est pas du JSON, traiter comme ancien format (string simple)
// prompt = event.data;
// model = null;
console.error('parseError :', parseError);
addLog(`Erreur : ${error.message}`);
}
// Si aucun modèle spécifié, utiliser celui sélectionné dans l'UI
if (!model) {
model = document.getElementById('defaultModel').value || 'gemini-2.5-pro';
}
console.log(`Message reçu - Prompt: "${prompt}", Modèle: "${model}"`);
addLog(`Message reçu - Prompt: "${prompt}", Modèle: "${model}"`);
const responsePhrase = await callOpenAI(prompt, model);
const endTime = performance.now();
const duration = (endTime - startTime) / 1000
// Vérifier à nouveau que la connexion est ouverte
if (ws.readyState === WebSocket.OPEN) {
addLog(`Envoi de la réponse automatique : "${responsePhrase}", durée : "${duration.toFixed(2)}"`);
ws.send(responsePhrase);
} else {
addLog('Erreur: WebSocket fermé pendant l\'appel API');
}
} catch (error) {
console.error('Erreur traitement message:', error);
addLog(`Erreur traitement message: ${error.message}`);
}
};
ws.onclose = function() {
statusDiv.textContent = 'Déconnecté';
statusDiv.className = 'disconnected';
addLog('Connexion WebSocket fermée. Tentative de reconnexion dans 3 secondes...');
// Tente de se reconnecter après 3 secondes
setTimeout(connect, 3000);
};
ws.onerror = function(error) {
addLog('Erreur WebSocket.');
console.error('WebSocket Error:', error);
ws.close();
};
}
// Lance la connexion au chargement de la page
connect();
</script>
</body>
</html> |