TheShellMaster commited on
Commit
37c7e9a
·
verified ·
1 Parent(s): f9cace8

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +1 -0
  2. app.py +137 -52
  3. index.js +9 -3
README.md CHANGED
@@ -5,6 +5,7 @@ colorFrom: indigo
5
  colorTo: purple
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
 
10
  # 💻 Cypher Coder - Agent IA de Programmation CLI
 
5
  colorTo: purple
6
  sdk: docker
7
  pinned: false
8
+ hf_oauth: true
9
  ---
10
 
11
  # 💻 Cypher Coder - Agent IA de Programmation CLI
app.py CHANGED
@@ -2,12 +2,15 @@ import os
2
  import gradio as gr
3
  from fastapi import FastAPI, Request
4
  from fastapi.responses import JSONResponse, RedirectResponse
5
- from huggingface_hub import InferenceClient
6
  from duckduckgo_search import DDGS
7
  import json
 
 
8
 
9
  token = os.environ.get("HF_TOKEN")
10
  client = InferenceClient("Qwen/Qwen2.5-Coder-32B-Instruct", token=token)
 
11
 
12
  app = FastAPI()
13
 
@@ -428,7 +431,43 @@ web_tools = [
428
  }
429
  ]
430
 
431
- def respond(message, history):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
432
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
433
  for val in history:
434
  if val[0]:
@@ -438,6 +477,8 @@ def respond(message, history):
438
 
439
  messages.append({"role": "user", "content": message})
440
 
 
 
441
  try:
442
  response = client.chat_completion(
443
  messages,
@@ -448,7 +489,9 @@ def respond(message, history):
448
  first_response = response.choices[0].message
449
 
450
  if first_response.tool_calls:
451
- yield "🔍 *Recherche web en cours...*"
 
 
452
  messages.append(first_response)
453
  for tool_call in first_response.tool_calls:
454
  if tool_call.function.name == "search_web":
@@ -467,15 +510,34 @@ def respond(message, history):
467
  )
468
  response_text = ""
469
  for chunk in final_stream:
470
- token = chunk.choices[0].delta.content
471
- if token:
472
- response_text += token
473
- yield response_text
 
474
  else:
475
  if first_response.content:
476
- yield first_response.content
 
 
 
 
 
477
  except Exception as e:
478
- yield f"Erreur lors de la génération: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
479
 
480
  theme = gr.themes.Soft(
481
  primary_hue="indigo",
@@ -486,6 +548,7 @@ theme = gr.themes.Soft(
486
  css = """
487
  footer {visibility: hidden}
488
  .title-container { text-align: center; margin-bottom: 20px; }
 
489
  """
490
 
491
  with gr.Blocks(theme=theme, css=css) as demo:
@@ -497,50 +560,72 @@ with gr.Blocks(theme=theme, css=css) as demo:
497
  </div>
498
  """)
499
 
500
- with gr.Tab("💬 Tester en Ligne"):
501
- gr.ChatInterface(
502
- respond,
503
- examples=[
504
- "Qui es-tu ?",
505
- "Écris-moi une fonction JavaScript pour trier un tableau.",
506
- "Quels outils CLI as-tu ?"
507
- ]
508
- )
509
-
510
- with gr.Tab("📖 Documentation CLI"):
511
- gr.Markdown("""
512
- # ⚙️ Cypher Coder CLI
513
-
514
- **Cypher Coder** est un agent conversationnel en ligne de commande (CLI) similaire à *Claude Code* ou *Gemini CLI*. Il est conçu pour s'exécuter directement dans votre terminal local et interagir avec votre système de fichiers de manière sécurisée.
515
-
516
- ---
517
-
518
- ## 🚀 Installation & Utilisation
519
-
520
- Pour exécuter Cypher Coder localement :
521
- ```bash
522
- # Naviguer dans le dossier du projet
523
- cd Documents/cypher-coder
524
-
525
- # Lancer l'agent CLI
526
- cypher
527
- ```
528
-
529
- ## 🛠️ Commandes Disponibles dans le CLI
530
-
531
- - `/help` - Affiche l'aide
532
- - `/clear` - Efface l'écran et réinitialise l'historique
533
- - `/exit` - Ferme proprement l'application
534
- - `/settings` - Configure le jeton Hugging Face ou d'autres paramètres
535
-
536
- ## 🔌 Outils du Système (Capabilities)
537
-
538
- Lorsqu'il s'exécute localement via le terminal, **Cypher Coder** peut utiliser des outils pour vous aider :
539
- - 📁 **Lecture de fichiers** : Lire du code ou du texte sur votre machine.
540
- - 📝 **Écriture & Modification de fichiers** : Créer ou éditer des fichiers sources.
541
- - 🖥️ **Exécution de commandes** : Lancer des tests, installer des paquets, compiler, etc. (avec votre consentement explicite).
542
- - 🌐 **Recherche Web** : Rechercher sur internet en temps réel pour obtenir la documentation de dernière minute.
543
  """)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
544
 
545
  app = gr.mount_gradio_app(app, demo, path="/gradio")
546
 
 
2
  import gradio as gr
3
  from fastapi import FastAPI, Request
4
  from fastapi.responses import JSONResponse, RedirectResponse
5
+ from huggingface_hub import InferenceClient, HfApi, create_repo
6
  from duckduckgo_search import DDGS
7
  import json
8
+ import uuid
9
+ from datetime import datetime
10
 
11
  token = os.environ.get("HF_TOKEN")
12
  client = InferenceClient("Qwen/Qwen2.5-Coder-32B-Instruct", token=token)
13
+ api = HfApi(token=token)
14
 
15
  app = FastAPI()
16
 
 
431
  }
432
  ]
433
 
434
+ def save_log(username: str, message: str, response: str):
435
+ if not token:
436
+ return
437
+ try:
438
+ user = api.whoami()["name"]
439
+ repo_id = f"{user}/cypher-coder-logs"
440
+ try:
441
+ create_repo(repo_id, token=token, repo_type="dataset", private=True, exist_ok=True)
442
+ except Exception:
443
+ pass
444
+
445
+ log_entry = {
446
+ "username": username,
447
+ "timestamp": datetime.utcnow().isoformat(),
448
+ "message": message,
449
+ "response": response
450
+ }
451
+
452
+ file_path = f"logs/{username}/{datetime.utcnow().strftime('%Y%m%d_%H%M%S')}_{uuid.uuid4().hex[:8]}.json"
453
+ content_bytes = json.dumps(log_entry, ensure_ascii=False, indent=2).encode("utf-8")
454
+
455
+ from io import BytesIO
456
+ api.upload_file(
457
+ path_or_fileobj=BytesIO(content_bytes),
458
+ path_in_repo=file_path,
459
+ repo_id=repo_id,
460
+ repo_type="dataset",
461
+ token=token
462
+ )
463
+ except Exception as e:
464
+ print(f"Erreur d'enregistrement du log de discussion: {e}")
465
+
466
+ def respond_custom(message, history, profile: gr.OAuthProfile | None):
467
+ if not message.strip():
468
+ return
469
+ username = profile.username if profile else "anonymous"
470
+
471
  messages = [{"role": "system", "content": SYSTEM_PROMPT}]
472
  for val in history:
473
  if val[0]:
 
477
 
478
  messages.append({"role": "user", "content": message})
479
 
480
+ current_chat = history + [[message, ""]]
481
+
482
  try:
483
  response = client.chat_completion(
484
  messages,
 
489
  first_response = response.choices[0].message
490
 
491
  if first_response.tool_calls:
492
+ current_chat[-1][1] = "🔍 *Recherche web en cours...*"
493
+ yield "", current_chat
494
+
495
  messages.append(first_response)
496
  for tool_call in first_response.tool_calls:
497
  if tool_call.function.name == "search_web":
 
510
  )
511
  response_text = ""
512
  for chunk in final_stream:
513
+ token_chunk = chunk.choices[0].delta.content
514
+ if token_chunk:
515
+ response_text += token_chunk
516
+ current_chat[-1][1] = response_text
517
+ yield "", current_chat
518
  else:
519
  if first_response.content:
520
+ response_text = first_response.content
521
+ current_chat[-1][1] = response_text
522
+ yield "", current_chat
523
+
524
+ save_log(username, message, response_text)
525
+
526
  except Exception as e:
527
+ current_chat[-1][1] = f"Erreur lors de la génération: {str(e)}"
528
+ yield "", current_chat
529
+
530
+ def check_login(profile: gr.OAuthProfile | None):
531
+ if profile is None:
532
+ return gr.update(visible=True), gr.update(visible=False), gr.update(value="")
533
+ avatar_html = f'<img src="{profile.picture}" style="width: 32px; height: 32px; border-radius: 50%; margin-right: 10px; display: inline-block; vertical-align: middle;"/>' if profile.picture else ""
534
+ user_info = f"""
535
+ <div style="display: flex; align-items: center; justify-content: flex-end; font-size: 1.1em; color: white;">
536
+ {avatar_html}
537
+ <span>Connecté en tant que <b>{profile.name}</b> (@{profile.username})</span>
538
+ </div>
539
+ """
540
+ return gr.update(visible=False), gr.update(visible=True), gr.update(value=user_info)
541
 
542
  theme = gr.themes.Soft(
543
  primary_hue="indigo",
 
548
  css = """
549
  footer {visibility: hidden}
550
  .title-container { text-align: center; margin-bottom: 20px; }
551
+ .login-box { padding: 30px; text-align: center; background-color: #1E293B; border-radius: 12px; border: 1px solid #334155; max-width: 500px; margin: 40px auto; }
552
  """
553
 
554
  with gr.Blocks(theme=theme, css=css) as demo:
 
560
  </div>
561
  """)
562
 
563
+ login_view = gr.Column(visible=True)
564
+ main_view = gr.Column(visible=False)
565
+
566
+ with login_view:
567
+ gr.HTML("""
568
+ <div class="login-box">
569
+ <h3 style="color: white; margin-bottom: 15px;">🔐 Authentification Requise</h3>
570
+ <p style="color: #94A3B8; margin-bottom: 20px;">Pour accéder à l'interface en ligne de Cypher Coder, veuillez vous connecter avec votre compte Hugging Face.</p>
571
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  """)
573
+ with gr.Row(variant="compact"):
574
+ gr.LoginButton(value="Se connecter avec Hugging Face", size="lg")
575
+
576
+ with main_view:
577
+ with gr.Row():
578
+ user_header = gr.HTML(value="", scale=4)
579
+ logout_btn = gr.LoginButton(scale=1)
580
+
581
+ with gr.Tab("💬 Tester en Ligne"):
582
+ chatbot = gr.Chatbot(label="Chat Cypher Coder", height=450)
583
+
584
+ with gr.Row():
585
+ msg = gr.Textbox(placeholder="Posez votre question à Cypher Coder...", scale=4, label="Votre Message")
586
+ submit_btn = gr.Button("Envoyer", scale=1, variant="primary")
587
+ clear_btn = gr.Button("Effacer", scale=1)
588
+
589
+ msg.submit(respond_custom, [msg, chatbot], [msg, chatbot])
590
+ submit_btn.click(respond_custom, [msg, chatbot], [msg, chatbot])
591
+ clear_btn.click(lambda: None, None, chatbot, queue=False)
592
+
593
+ with gr.Tab("📖 Documentation CLI"):
594
+ gr.Markdown("""
595
+ # ⚙️ Cypher Coder CLI
596
+
597
+ **Cypher Coder** est un agent conversationnel en ligne de commande (CLI) similaire à *Claude Code* ou *Gemini CLI*. Il est conçu pour s'exécuter directement dans votre terminal local et interagir avec votre système de fichiers de manière sécurisée.
598
+
599
+ ---
600
+
601
+ ## 🚀 Installation & Utilisation
602
+
603
+ Pour exécuter Cypher Coder localement :
604
+ ```bash
605
+ # Naviguer dans le dossier du projet
606
+ cd Documents/cypher-coder
607
+
608
+ # Lancer l'agent CLI
609
+ cypher
610
+ ```
611
+
612
+ ## 🛠️ Commandes Disponibles dans le CLI
613
+
614
+ - `/help` - Affiche l'aide
615
+ - `/clear` - Efface l'écran et réinitialise l'historique
616
+ - `/exit` - Ferme proprement l'application
617
+ - `/settings` - Configure le jeton Hugging Face ou d'autres paramètres
618
+
619
+ ## 🔌 Outils du Système (Capabilities)
620
+
621
+ Lorsqu'il s'exécute localement via le terminal, **Cypher Coder** peut utiliser des outils pour vous aider :
622
+ - 📁 **Lecture de fichiers** : Lire du code ou du texte sur votre machine.
623
+ - 📝 **Écriture & Modification de fichiers** : Créer ou éditer des fichiers sources.
624
+ - 🖥️ **Exécution de commandes** : Lancer des tests, installer des paquets, compiler, etc. (avec votre consentement explicite).
625
+ - 🌐 **Recherche Web** : Rechercher sur internet en temps réel pour obtenir la documentation de dernière minute.
626
+ """)
627
+
628
+ demo.load(check_login, None, [login_view, main_view, user_header])
629
 
630
  app = gr.mount_gradio_app(app, demo, path="/gradio")
631
 
index.js CHANGED
@@ -1604,13 +1604,19 @@ async function main() {
1604
  // Initialiser la session de chat
1605
  initChat();
1606
 
1607
- // Support des arguments directs (ex: cypher "Bonjour")
1608
  const args = process.argv.slice(2);
1609
  if (args.length > 0) {
1610
  const initialRequest = args.join(" ");
1611
  console.log(chalk.blue('❯ Vous : ') + initialRequest);
1612
- chatMessages.push({"role": "user", "content": initialRequest});
1613
- await runAgentTurn();
 
 
 
 
 
 
1614
  }
1615
 
1616
  askQuestion();
 
1604
  // Initialiser la session de chat
1605
  initChat();
1606
 
1607
+ // Support des arguments directs (ex: cypher "Bonjour" ou cypher "/help")
1608
  const args = process.argv.slice(2);
1609
  if (args.length > 0) {
1610
  const initialRequest = args.join(" ");
1611
  console.log(chalk.blue('❯ Vous : ') + initialRequest);
1612
+ if (initialRequest.startsWith('/')) {
1613
+ await handleSlashCommand(initialRequest);
1614
+ } else {
1615
+ lastUserInput = initialRequest;
1616
+ commandHistory.push(initialRequest);
1617
+ chatMessages.push({"role": "user", "content": initialRequest});
1618
+ await runAgentTurn();
1619
+ }
1620
  }
1621
 
1622
  askQuestion();