NathMen12 commited on
Commit
57beef4
·
verified ·
1 Parent(s): 39172fd

Upload index.html

Browse files
Files changed (1) hide show
  1. static/index.html +33 -7
static/index.html CHANGED
@@ -140,6 +140,7 @@
140
  .typing-indicator span { display: inline-block; animation: blink 1.4s infinite both; font-size: 1.5rem; line-height: 1; margin: 0 2px;}
141
  .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
142
  .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
 
143
  @keyframes blink { 0% { opacity: 0.2; transform: scale(0.8); } 20% { opacity: 1; transform: scale(1.2); } 100% { opacity: 0.2; transform: scale(0.8); } }
144
 
145
  /* Modals & Toggles */
@@ -315,8 +316,8 @@
315
 
316
  <div class="toggle-group">
317
  <div>
318
- <strong>Recherche Web</strong>
319
- <div style="font-size: 0.8rem; color: #a6adc8;">Autorise l'IA à "chercher" sur internet.</div>
320
  </div>
321
  <label class="switch"><input type="checkbox" id="toggleWebSearch"><span class="slider"></span></label>
322
  </div>
@@ -603,6 +604,16 @@
603
  if (m.isThinking && fullText) m.isThinking = false;
604
  this.updateMessageDOM(aiIndex);
605
  },
 
 
 
 
 
 
 
 
 
 
606
  finishSmoothStream(aiIndex) {
607
  const s = this._smoothStreams[aiIndex];
608
  const chat = this.getActiveChat();
@@ -1058,7 +1069,9 @@
1058
  let contentHTML = '';
1059
 
1060
  if (m.isThinking) {
1061
- contentHTML = `<div class="typing-indicator"><span>.</span><span>.</span><span>.</span></div>`;
 
 
1062
  } else if (m.isImageGenerated) {
1063
  contentHTML = `<div class="img-gen-wrapper" style="min-height: auto; max-width: 350px;">
1064
  <img src="${m.content}" class="loaded" style="max-width: 100%; border-radius: 8px;" alt="Image Générée">
@@ -1156,7 +1169,9 @@
1156
 
1157
  const bodyEl = wrapper.querySelector('.message');
1158
  if (m.isThinking) {
1159
- bodyEl.innerHTML = `<div class="typing-indicator"><span>.</span><span>.</span><span>.</span></div>`;
 
 
1160
  } else if (m.isImageGenerated) {
1161
  bodyEl.innerHTML = `<div class="img-gen-wrapper" style="min-height: auto; max-width: 350px;">
1162
  <img src="${m.content}" class="loaded" style="max-width: 100%; border-radius: 8px;" alt="Image Générée">
@@ -1623,7 +1638,9 @@ Structure actuelle du workspace :
1623
  ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1624
  }
1625
 
1626
- if (this.state.settings.webSearch) { systemMsg += "\nTu es autorisé à simuler des recherches Web si nécessaire."; }
 
 
1627
 
1628
  let history = [];
1629
  if (provider !== 'gemini') { history.push({ role: 'system', content: systemMsg }); }
@@ -1753,7 +1770,8 @@ ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1753
  Structure actuelle du workspace :
1754
  ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1755
  }
1756
- if (this.state.settings.webSearch) { systemMsg += "\nTu es autorisé à simuler des recherches Web si nécessaire."; }
 
1757
 
1758
  const messages = chat.messages.slice(0, aiIndex).map(m => {
1759
  return { role: m.role, content: this.buildMessageContentForAI(m) };
@@ -1783,7 +1801,8 @@ ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1783
  body: JSON.stringify({
1784
  chat_id: chat.id, chat_title: chat.title, message_id: msg.id, provider, model,
1785
  apiKey: creds.apiKey || '', baseUrl: creds.baseUrl || '',
1786
- system, messages, recent_messages: recentMessages
 
1787
  }),
1788
  signal: this.abortController.signal
1789
  });
@@ -1849,14 +1868,21 @@ ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1849
  if (eventName === 'sync') {
1850
  this.syncSmoothContent(aiIndex, payload.content || '');
1851
  if (payload.tokens) chat.messages[aiIndex].tokens = payload.tokens;
 
 
 
 
1852
  } else if (eventName === 'delta') {
 
1853
  this.pushSmoothChunk(aiIndex, payload.text || '');
1854
  } else if (eventName === 'done') {
 
1855
  if (payload.tokens) chat.messages[aiIndex].tokens = payload.tokens;
1856
  if (payload.promptTokens) chat.messages[aiIndex].promptTokens = payload.promptTokens;
1857
  if (payload.completionTokens) chat.messages[aiIndex].completionTokens = payload.completionTokens;
1858
  this.finishSmoothStream(aiIndex);
1859
  } else if (eventName === 'error') {
 
1860
  this.pushSmoothChunk(aiIndex, "\n**Erreur API:** " + (payload.message || 'inconnue'));
1861
  this.finishSmoothStream(aiIndex);
1862
  }
 
140
  .typing-indicator span { display: inline-block; animation: blink 1.4s infinite both; font-size: 1.5rem; line-height: 1; margin: 0 2px;}
141
  .typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
142
  .typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
143
+ .search-status { display: flex; align-items: center; gap: 8px; font-size: 0.9rem; opacity: 0.85; }
144
  @keyframes blink { 0% { opacity: 0.2; transform: scale(0.8); } 20% { opacity: 1; transform: scale(1.2); } 100% { opacity: 0.2; transform: scale(0.8); } }
145
 
146
  /* Modals & Toggles */
 
316
 
317
  <div class="toggle-group">
318
  <div>
319
+ <strong>Recherche Web (agent)</strong>
320
+ <div style="font-size: 0.8rem; color: #a6adc8;">L'IA décide si une recherche est utile, interroge plusieurs sites via le serveur MCP de victor (HuggingFace), puis répond avec les résultats. Nécessite le mode connecté (compte + serveur).</div>
321
  </div>
322
  <label class="switch"><input type="checkbox" id="toggleWebSearch"><span class="slider"></span></label>
323
  </div>
 
604
  if (m.isThinking && fullText) m.isThinking = false;
605
  this.updateMessageDOM(aiIndex);
606
  },
607
+ // Affiche/efface un statut court pendant une étape agentique invisible
608
+ // (ex: "Je vais rechercher sur le web...") à la place des "...", tant que le
609
+ // message est encore en isThinking. Jamais utilisé pour montrer le détail des tool calls.
610
+ setSearchStatus(aiIndex, text) {
611
+ const chat = this.getActiveChat();
612
+ const m = chat && chat.messages[aiIndex];
613
+ if (!m) return;
614
+ m.searchStatus = text || undefined;
615
+ if (m.isThinking) this.updateMessageDOM(aiIndex);
616
+ },
617
  finishSmoothStream(aiIndex) {
618
  const s = this._smoothStreams[aiIndex];
619
  const chat = this.getActiveChat();
 
1069
  let contentHTML = '';
1070
 
1071
  if (m.isThinking) {
1072
+ contentHTML = m.searchStatus
1073
+ ? `<div class="search-status"><i class="fas fa-search fa-spin"></i> ${this.escapeHtml(m.searchStatus)}</div>`
1074
+ : `<div class="typing-indicator"><span>.</span><span>.</span><span>.</span></div>`;
1075
  } else if (m.isImageGenerated) {
1076
  contentHTML = `<div class="img-gen-wrapper" style="min-height: auto; max-width: 350px;">
1077
  <img src="${m.content}" class="loaded" style="max-width: 100%; border-radius: 8px;" alt="Image Générée">
 
1169
 
1170
  const bodyEl = wrapper.querySelector('.message');
1171
  if (m.isThinking) {
1172
+ bodyEl.innerHTML = m.searchStatus
1173
+ ? `<div class="search-status"><i class="fas fa-search fa-spin"></i> ${this.escapeHtml(m.searchStatus)}</div>`
1174
+ : `<div class="typing-indicator"><span>.</span><span>.</span><span>.</span></div>`;
1175
  } else if (m.isImageGenerated) {
1176
  bodyEl.innerHTML = `<div class="img-gen-wrapper" style="min-height: auto; max-width: 350px;">
1177
  <img src="${m.content}" class="loaded" style="max-width: 100%; border-radius: 8px;" alt="Image Générée">
 
1638
  ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1639
  }
1640
 
1641
+ // Note : en mode local (sans serveur), la recherche web réelle n'est pas
1642
+ // disponible — pas d'instruction de "simulation" pour éviter les réponses
1643
+ // inventées ; ce toggle n'a d'effet réel qu'en mode connecté (voir triggerAiResponseCloud).
1644
 
1645
  let history = [];
1646
  if (provider !== 'gemini') { history.push({ role: 'system', content: systemMsg }); }
 
1770
  Structure actuelle du workspace :
1771
  ${JSON.stringify(Object.keys(this.state.virtualFiles))}`;
1772
  }
1773
+ // La recherche web réelle (agent + MCP victor/websearch) est décidée et exécutée
1774
+ // côté serveur dans run_job() si webSearchEnabled est transmis — voir plus bas.
1775
 
1776
  const messages = chat.messages.slice(0, aiIndex).map(m => {
1777
  return { role: m.role, content: this.buildMessageContentForAI(m) };
 
1801
  body: JSON.stringify({
1802
  chat_id: chat.id, chat_title: chat.title, message_id: msg.id, provider, model,
1803
  apiKey: creds.apiKey || '', baseUrl: creds.baseUrl || '',
1804
+ system, messages, recent_messages: recentMessages,
1805
+ webSearchEnabled: !!this.state.settings.webSearch
1806
  }),
1807
  signal: this.abortController.signal
1808
  });
 
1868
  if (eventName === 'sync') {
1869
  this.syncSmoothContent(aiIndex, payload.content || '');
1870
  if (payload.tokens) chat.messages[aiIndex].tokens = payload.tokens;
1871
+ } else if (eventName === 'status') {
1872
+ // Étape agentique invisible (ex: décision + exécution de la recherche web) :
1873
+ // affiche un statut court à la place des "...", sans jamais montrer le détail des tool calls.
1874
+ this.setSearchStatus(aiIndex, payload.text || '');
1875
  } else if (eventName === 'delta') {
1876
+ this.setSearchStatus(aiIndex, null);
1877
  this.pushSmoothChunk(aiIndex, payload.text || '');
1878
  } else if (eventName === 'done') {
1879
+ this.setSearchStatus(aiIndex, null);
1880
  if (payload.tokens) chat.messages[aiIndex].tokens = payload.tokens;
1881
  if (payload.promptTokens) chat.messages[aiIndex].promptTokens = payload.promptTokens;
1882
  if (payload.completionTokens) chat.messages[aiIndex].completionTokens = payload.completionTokens;
1883
  this.finishSmoothStream(aiIndex);
1884
  } else if (eventName === 'error') {
1885
+ this.setSearchStatus(aiIndex, null);
1886
  this.pushSmoothChunk(aiIndex, "\n**Erreur API:** " + (payload.message || 'inconnue'));
1887
  this.finishSmoothStream(aiIndex);
1888
  }