expAge commited on
Commit
fb0d1fb
·
1 Parent(s): 866e0b2

feat(ui): case 'Afficher le raisonnement' (decoche par defaut) dans Jarvis + Chatbot

Browse files

Le 'thought summary' Gemini 3.x (include_thoughts=True, thinking_level=low)
ralentit visiblement la 1ere reponse. On le rend opt-in via une checkbox
visible dans :
- le bandeau Jarvis (commun aux 5 sous-onglets)
- l'onglet LLM Chatbot

Decoche par defaut. Comportement fonctionnel strictement identique quand
decoche : memes outils, memes sorties, seule la narration interne du
raisonnement n'est pas demandee a l'API => premiere reponse plus rapide.

Plomberie :
- _build_llm(model, api_key, *, use_thinking=True) propage a
_build_gemini_native qui omet include_thoughts/thinking_level si False
- run_jarvis_flow gagne use_thinking, passe a build_llm_fn
- chat_with_agent gagne use_thinking (4e additional_input)
- exemples du ChatInterface : 4 valeurs au lieu de 3

Files changed (2) hide show
  1. app.py +72 -21
  2. jarvis.py +2 -1
app.py CHANGED
@@ -296,7 +296,7 @@ ALL_MODELS = {
296
  }
297
 
298
 
299
- def _build_llm(model: str, api_key: str):
300
  """Instancie le ChatModel selon le modèle choisi.
301
 
302
  - claude-* → Anthropic via clé visiteur (BYOK, sk-ant-...)
@@ -306,6 +306,12 @@ def _build_llm(model: str, api_key: str):
306
  pour son architecture (Cerebras pour Qwen, Together pour
307
  Mistral) via le suffixe :provider du nom de modèle.
308
 
 
 
 
 
 
 
309
  Lève ValueError avec message utilisateur explicite si la clé manque.
310
  """
311
  if model.startswith("claude-"):
@@ -340,7 +346,7 @@ def _build_llm(model: str, api_key: str):
340
  # 3.x preview → SDK natif Google (préserve thought_signature).
341
  # 2.x stables → endpoint OpenAI-compat (déjà éprouvé, plus simple).
342
  if model in GEMINI_NATIVE_REQUIRED:
343
- return _build_gemini_native(model)
344
  return _build_openai_compat(
345
  model_id=model, label="Google Gemini",
346
  env_var="GOOGLE_API_KEY",
@@ -388,7 +394,7 @@ def _build_openai_compat(*, model_id: str, label: str, env_var: str,
388
  )
389
 
390
 
391
- def _build_gemini_native(model_id: str):
392
  """Builder spécifique pour les Gemini 3.x preview via SDK natif Google.
393
 
394
  Le SDK `langchain-google-genai` (qui enveloppe le SDK Python officiel
@@ -438,6 +444,14 @@ def _build_gemini_native(model_id: str):
438
  "google_api_key": token,
439
  "temperature": 1.0,
440
  }
 
 
 
 
 
 
 
 
441
  try:
442
  return ChatGoogleGenerativeAI(
443
  **base_kwargs,
@@ -485,7 +499,8 @@ def _history_to_lc(history: list[dict], current_user_message: str) -> list:
485
  return lc
486
 
487
 
488
- def chat_with_agent(message: str, history: list[dict], api_key: str, model: str):
 
489
  """Générateur de streaming pour ChatInterface.
490
 
491
  Yields la trace progressive (thinking + appels d'outils + résultats)
@@ -503,7 +518,7 @@ def chat_with_agent(message: str, history: list[dict], api_key: str, model: str)
503
  yield "Pose une question sur la langue française.", _NOOP_FILE
504
  return
505
  try:
506
- llm = _build_llm(model, api_key)
507
  except ValueError as e:
508
  yield f"⚠️ {e}", _NOOP_FILE
509
  return
@@ -1646,6 +1661,16 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1646
  label="Modèle",
1647
  scale=2,
1648
  )
 
 
 
 
 
 
 
 
 
 
1649
 
1650
  # Toggle dynamique : la clé API n'est saisissable que pour les
1651
  # modèles BYOK (Claude / GPT). Sur un modèle Gemini hébergé,
@@ -1683,7 +1708,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1683
  )
1684
  chat = gr.ChatInterface(
1685
  fn=chat_with_agent,
1686
- additional_inputs=[key_in, model_in],
1687
  additional_outputs=[viz_html_out],
1688
  # Chatbot agrandi : 780 px de haut (+30 % vs 600).
1689
  # Tentative d'HTML/<details> abandonnée — gr.Chatbot v5
@@ -1708,11 +1733,12 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1708
  # Tous les exemples passent par Gemini 3.1 Flash Lite
1709
  # (quota le plus large : 500 req/jour, le plus rapide).
1710
  # En cas d'épuisement, BYOK Claude / GPT.
1711
- ["Quels sont les synonymes de voiture ?", "", "gemini-3.1-flash-lite"],
1712
- ["Le saumon est-il un mammifère selon JDM ?", "", "gemini-3.1-flash-lite"],
1713
- ["Pour le sens juridique de 'avocat', donne-moi 5 synonymes.", "", "gemini-3.1-flash-lite"],
1714
- ["Que peut faire un chat ?", "", "gemini-3.1-flash-lite"],
1715
- ["Quelles sont les composantes typiques d'un smartphone ?", "", "gemini-3.1-flash-lite"],
 
1716
  ],
1717
  cache_examples=False,
1718
  type="messages",
@@ -1768,6 +1794,17 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1768
  label="Budget d'appels d'outils",
1769
  scale=1,
1770
  )
 
 
 
 
 
 
 
 
 
 
 
1771
 
1772
  # ====== Sous-onglets ======
1773
  with gr.Tabs() as jarvis_tabs:
@@ -1848,7 +1885,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1848
  )
1849
 
1850
  def _run_enrich(term, relations, target_n, vary, iterate, upload,
1851
- drops_key, model, budget_label):
1852
  """Wrapper Gradio : construit le prompt, lance le flow Jarvis.
1853
  Yield (chatbot, file_update, preview_update)."""
1854
  from jarvis import build_enrich_prompt, run_jarvis_flow
@@ -1877,6 +1914,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1877
  build_llm_fn=_build_llm,
1878
  build_agent_fn=build_jdm_agent,
1879
  get_client_fn=get_client,
 
1880
  ):
1881
  yield (
1882
  messages,
@@ -1888,7 +1926,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1888
  _run_enrich,
1889
  inputs=[je_term, je_relation, je_target_n, je_vary,
1890
  je_iterate, je_upload,
1891
- jarvis_drops_key, jarvis_model, jarvis_budget],
 
1892
  outputs=[je_chat, je_file, je_preview],
1893
  )
1894
 
@@ -1988,7 +2027,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
1988
  visible=False,
1989
  )
1990
 
1991
- def _run_audit(term, relations, upload, drops_key, model, budget_label):
 
1992
  from jarvis import build_audit_prompt, run_jarvis_flow
1993
  from jdm_agent.tools.jdm_agent import build_jdm_agent
1994
  prompt = build_audit_prompt(
@@ -2009,6 +2049,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2009
  build_llm_fn=_build_llm,
2010
  build_agent_fn=build_jdm_agent,
2011
  get_client_fn=get_client,
 
2012
  ):
2013
  yield (
2014
  messages,
@@ -2019,7 +2060,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2019
  ja_launch.click(
2020
  _run_audit,
2021
  inputs=[ja_term, ja_relation, ja_upload,
2022
- jarvis_drops_key, jarvis_model, jarvis_budget],
 
2023
  outputs=[ja_chat, ja_file, ja_preview],
2024
  )
2025
 
@@ -2100,7 +2142,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2100
  height=400,
2101
  )
2102
 
2103
- def _run_gap_detection(term, relations, min_pos, drops_key, model, budget_label):
 
2104
  """Détecte les gaps DIRECTEMENT (rapide, déterministe)
2105
  puis lance l'agent pour la synthèse narrative."""
2106
  from jarvis import build_gap_prompt, run_jarvis_flow
@@ -2179,13 +2222,15 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2179
  build_llm_fn=_build_llm,
2180
  build_agent_fn=build_jdm_agent,
2181
  get_client_fn=get_client,
 
2182
  ):
2183
  yield (gr.update(), gr.update(), chat_msgs)
2184
 
2185
  jg_launch.click(
2186
  _run_gap_detection,
2187
  inputs=[jg_term, jg_relations, jg_min_pos,
2188
- jarvis_drops_key, jarvis_model, jarvis_budget],
 
2189
  outputs=[jg_gaps_table, jg_gap_dropdown, jg_chat],
2190
  )
2191
 
@@ -2289,7 +2334,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2289
  visible=False,
2290
  )
2291
 
2292
- def _run_signalement(term, relations, upload, drops_key, model, budget_label):
 
2293
  from jarvis import build_signalement_prompt, run_jarvis_flow
2294
  from jdm_agent.tools.jdm_agent import build_jdm_agent
2295
  prompt = build_signalement_prompt(
@@ -2310,6 +2356,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2310
  build_llm_fn=_build_llm,
2311
  build_agent_fn=build_jdm_agent,
2312
  get_client_fn=get_client,
 
2313
  ):
2314
  yield (
2315
  messages,
@@ -2320,7 +2367,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2320
  js_launch.click(
2321
  _run_signalement,
2322
  inputs=[js_term, js_relation, js_upload,
2323
- jarvis_drops_key, jarvis_model, jarvis_budget],
 
2324
  outputs=[js_chat, js_file, js_preview],
2325
  )
2326
 
@@ -2417,7 +2465,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2417
  visible=False,
2418
  )
2419
 
2420
- def _run_stats(term, relations, upload, drops_key, model, budget_label):
 
2421
  from jarvis import build_stats_prompt, run_jarvis_flow
2422
  from jdm_agent.tools.jdm_agent import build_jdm_agent
2423
  prompt = build_stats_prompt(
@@ -2445,6 +2494,7 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2445
  build_llm_fn=_build_llm,
2446
  build_agent_fn=build_jdm_agent,
2447
  get_client_fn=get_client,
 
2448
  ):
2449
  yield (
2450
  messages,
@@ -2455,7 +2505,8 @@ with gr.Blocks(theme=THEME, title="JDMAgent Demo", head=_HEAD_JS, css=_CHATBOT_C
2455
  jst_launch.click(
2456
  _run_stats,
2457
  inputs=[jst_term, jst_relation, jst_upload,
2458
- jarvis_drops_key, jarvis_model, jarvis_budget],
 
2459
  outputs=[jst_chat, jst_file, jst_preview],
2460
  )
2461
 
 
296
  }
297
 
298
 
299
+ def _build_llm(model: str, api_key: str, *, use_thinking: bool = True):
300
  """Instancie le ChatModel selon le modèle choisi.
301
 
302
  - claude-* → Anthropic via clé visiteur (BYOK, sk-ant-...)
 
306
  pour son architecture (Cerebras pour Qwen, Together pour
307
  Mistral) via le suffixe :provider du nom de modèle.
308
 
309
+ `use_thinking` ne s'applique pour l'instant qu'aux modèles Gemini 3.x
310
+ natifs (les seuls où on active explicitement `include_thoughts` /
311
+ `thinking_level`). Désactivé (False) : on n'active pas le « thought
312
+ summary » côté API → première réponse plus rapide, comportement
313
+ fonctionnel inchangé (tool-calling, sortie texte identiques).
314
+
315
  Lève ValueError avec message utilisateur explicite si la clé manque.
316
  """
317
  if model.startswith("claude-"):
 
346
  # 3.x preview → SDK natif Google (préserve thought_signature).
347
  # 2.x stables → endpoint OpenAI-compat (déjà éprouvé, plus simple).
348
  if model in GEMINI_NATIVE_REQUIRED:
349
+ return _build_gemini_native(model, use_thinking=use_thinking)
350
  return _build_openai_compat(
351
  model_id=model, label="Google Gemini",
352
  env_var="GOOGLE_API_KEY",
 
394
  )
395
 
396
 
397
+ def _build_gemini_native(model_id: str, *, use_thinking: bool = True):
398
  """Builder spécifique pour les Gemini 3.x preview via SDK natif Google.
399
 
400
  Le SDK `langchain-google-genai` (qui enveloppe le SDK Python officiel
 
444
  "google_api_key": token,
445
  "temperature": 1.0,
446
  }
447
+ if not use_thinking:
448
+ # Pas de chain-of-thought demandé → on n'active rien. Gemini répond
449
+ # plus vite (pas de génération du « thought summary »), et le
450
+ # comportement fonctionnel reste identique (mêmes outils, mêmes
451
+ # sorties). On laisse aussi `thinking_level` non-défini : selon
452
+ # la version langchain-google-genai, cela tombe sur le défaut
453
+ # « no thinking » du SDK.
454
+ return ChatGoogleGenerativeAI(**base_kwargs)
455
  try:
456
  return ChatGoogleGenerativeAI(
457
  **base_kwargs,
 
499
  return lc
500
 
501
 
502
+ def chat_with_agent(message: str, history: list[dict], api_key: str, model: str,
503
+ use_thinking: bool = True):
504
  """Générateur de streaming pour ChatInterface.
505
 
506
  Yields la trace progressive (thinking + appels d'outils + résultats)
 
518
  yield "Pose une question sur la langue française.", _NOOP_FILE
519
  return
520
  try:
521
+ llm = _build_llm(model, api_key, use_thinking=use_thinking)
522
  except ValueError as e:
523
  yield f"⚠️ {e}", _NOOP_FILE
524
  return
 
1661
  label="Modèle",
1662
  scale=2,
1663
  )
1664
+ chat_thinking = gr.Checkbox(
1665
+ value=False,
1666
+ label="Afficher le raisonnement du LLM (Gemini 3.x)",
1667
+ info=(
1668
+ "Décoché : démarrage plus rapide, comportement "
1669
+ "fonctionnel strictement identique (mêmes outils, "
1670
+ "mêmes sorties — seule la narration interne du "
1671
+ "raisonnement n'est pas affichée)."
1672
+ ),
1673
+ )
1674
 
1675
  # Toggle dynamique : la clé API n'est saisissable que pour les
1676
  # modèles BYOK (Claude / GPT). Sur un modèle Gemini hébergé,
 
1708
  )
1709
  chat = gr.ChatInterface(
1710
  fn=chat_with_agent,
1711
+ additional_inputs=[key_in, model_in, chat_thinking],
1712
  additional_outputs=[viz_html_out],
1713
  # Chatbot agrandi : 780 px de haut (+30 % vs 600).
1714
  # Tentative d'HTML/<details> abandonnée — gr.Chatbot v5
 
1733
  # Tous les exemples passent par Gemini 3.1 Flash Lite
1734
  # (quota le plus large : 500 req/jour, le plus rapide).
1735
  # En cas d'épuisement, BYOK Claude / GPT.
1736
+ # 4 valeurs par exemple : [message, key, model, use_thinking]
1737
+ ["Quels sont les synonymes de voiture ?", "", "gemini-3.1-flash-lite", False],
1738
+ ["Le saumon est-il un mammifère selon JDM ?", "", "gemini-3.1-flash-lite", False],
1739
+ ["Pour le sens juridique de 'avocat', donne-moi 5 synonymes.", "", "gemini-3.1-flash-lite", False],
1740
+ ["Que peut faire un chat ?", "", "gemini-3.1-flash-lite", False],
1741
+ ["Quelles sont les composantes typiques d'un smartphone ?", "", "gemini-3.1-flash-lite", False],
1742
  ],
1743
  cache_examples=False,
1744
  type="messages",
 
1794
  label="Budget d'appels d'outils",
1795
  scale=1,
1796
  )
1797
+ with gr.Row():
1798
+ jarvis_thinking = gr.Checkbox(
1799
+ value=False,
1800
+ label="Afficher le raisonnement du LLM (Gemini 3.x)",
1801
+ info=(
1802
+ "Décoché : démarrage plus rapide, comportement "
1803
+ "fonctionnel strictement identique (mêmes outils, "
1804
+ "mêmes sorties — seule la narration interne du "
1805
+ "raisonnement n'est pas affichée)."
1806
+ ),
1807
+ )
1808
 
1809
  # ====== Sous-onglets ======
1810
  with gr.Tabs() as jarvis_tabs:
 
1885
  )
1886
 
1887
  def _run_enrich(term, relations, target_n, vary, iterate, upload,
1888
+ drops_key, model, budget_label, use_thinking):
1889
  """Wrapper Gradio : construit le prompt, lance le flow Jarvis.
1890
  Yield (chatbot, file_update, preview_update)."""
1891
  from jarvis import build_enrich_prompt, run_jarvis_flow
 
1914
  build_llm_fn=_build_llm,
1915
  build_agent_fn=build_jdm_agent,
1916
  get_client_fn=get_client,
1917
+ use_thinking=bool(use_thinking),
1918
  ):
1919
  yield (
1920
  messages,
 
1926
  _run_enrich,
1927
  inputs=[je_term, je_relation, je_target_n, je_vary,
1928
  je_iterate, je_upload,
1929
+ jarvis_drops_key, jarvis_model, jarvis_budget,
1930
+ jarvis_thinking],
1931
  outputs=[je_chat, je_file, je_preview],
1932
  )
1933
 
 
2027
  visible=False,
2028
  )
2029
 
2030
+ def _run_audit(term, relations, upload, drops_key, model, budget_label,
2031
+ use_thinking):
2032
  from jarvis import build_audit_prompt, run_jarvis_flow
2033
  from jdm_agent.tools.jdm_agent import build_jdm_agent
2034
  prompt = build_audit_prompt(
 
2049
  build_llm_fn=_build_llm,
2050
  build_agent_fn=build_jdm_agent,
2051
  get_client_fn=get_client,
2052
+ use_thinking=bool(use_thinking),
2053
  ):
2054
  yield (
2055
  messages,
 
2060
  ja_launch.click(
2061
  _run_audit,
2062
  inputs=[ja_term, ja_relation, ja_upload,
2063
+ jarvis_drops_key, jarvis_model, jarvis_budget,
2064
+ jarvis_thinking],
2065
  outputs=[ja_chat, ja_file, ja_preview],
2066
  )
2067
 
 
2142
  height=400,
2143
  )
2144
 
2145
+ def _run_gap_detection(term, relations, min_pos, drops_key, model,
2146
+ budget_label, use_thinking):
2147
  """Détecte les gaps DIRECTEMENT (rapide, déterministe)
2148
  puis lance l'agent pour la synthèse narrative."""
2149
  from jarvis import build_gap_prompt, run_jarvis_flow
 
2222
  build_llm_fn=_build_llm,
2223
  build_agent_fn=build_jdm_agent,
2224
  get_client_fn=get_client,
2225
+ use_thinking=bool(use_thinking),
2226
  ):
2227
  yield (gr.update(), gr.update(), chat_msgs)
2228
 
2229
  jg_launch.click(
2230
  _run_gap_detection,
2231
  inputs=[jg_term, jg_relations, jg_min_pos,
2232
+ jarvis_drops_key, jarvis_model, jarvis_budget,
2233
+ jarvis_thinking],
2234
  outputs=[jg_gaps_table, jg_gap_dropdown, jg_chat],
2235
  )
2236
 
 
2334
  visible=False,
2335
  )
2336
 
2337
+ def _run_signalement(term, relations, upload, drops_key, model,
2338
+ budget_label, use_thinking):
2339
  from jarvis import build_signalement_prompt, run_jarvis_flow
2340
  from jdm_agent.tools.jdm_agent import build_jdm_agent
2341
  prompt = build_signalement_prompt(
 
2356
  build_llm_fn=_build_llm,
2357
  build_agent_fn=build_jdm_agent,
2358
  get_client_fn=get_client,
2359
+ use_thinking=bool(use_thinking),
2360
  ):
2361
  yield (
2362
  messages,
 
2367
  js_launch.click(
2368
  _run_signalement,
2369
  inputs=[js_term, js_relation, js_upload,
2370
+ jarvis_drops_key, jarvis_model, jarvis_budget,
2371
+ jarvis_thinking],
2372
  outputs=[js_chat, js_file, js_preview],
2373
  )
2374
 
 
2465
  visible=False,
2466
  )
2467
 
2468
+ def _run_stats(term, relations, upload, drops_key, model,
2469
+ budget_label, use_thinking):
2470
  from jarvis import build_stats_prompt, run_jarvis_flow
2471
  from jdm_agent.tools.jdm_agent import build_jdm_agent
2472
  prompt = build_stats_prompt(
 
2494
  build_llm_fn=_build_llm,
2495
  build_agent_fn=build_jdm_agent,
2496
  get_client_fn=get_client,
2497
+ use_thinking=bool(use_thinking),
2498
  ):
2499
  yield (
2500
  messages,
 
2505
  jst_launch.click(
2506
  _run_stats,
2507
  inputs=[jst_term, jst_relation, jst_upload,
2508
+ jarvis_drops_key, jarvis_model, jarvis_budget,
2509
+ jarvis_thinking],
2510
  outputs=[jst_chat, jst_file, jst_preview],
2511
  )
2512
 
jarvis.py CHANGED
@@ -701,6 +701,7 @@ def run_jarvis_flow(
701
  build_llm_fn,
702
  build_agent_fn,
703
  get_client_fn,
 
704
  ) -> Generator[tuple[list[dict], Optional[str], str], None, None]:
705
  """Générateur qui pilote un agent avec budget pour un sous-onglet
706
  Jarvis, et yield des tuples (messages_chatbot, file_path, file_preview)
@@ -750,7 +751,7 @@ def run_jarvis_flow(
750
  try:
751
  # LLM + agent
752
  try:
753
- llm = build_llm_fn(model, api_key)
754
  except ValueError as e:
755
  yield (
756
  [{"role": "user", "content": user_display},
 
701
  build_llm_fn,
702
  build_agent_fn,
703
  get_client_fn,
704
+ use_thinking: bool = True,
705
  ) -> Generator[tuple[list[dict], Optional[str], str], None, None]:
706
  """Générateur qui pilote un agent avec budget pour un sous-onglet
707
  Jarvis, et yield des tuples (messages_chatbot, file_path, file_preview)
 
751
  try:
752
  # LLM + agent
753
  try:
754
+ llm = build_llm_fn(model, api_key, use_thinking=use_thinking)
755
  except ValueError as e:
756
  yield (
757
  [{"role": "user", "content": user_display},