Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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 |
-
|
| 471 |
-
if
|
| 472 |
-
response_text +=
|
| 473 |
-
|
|
|
|
| 474 |
else:
|
| 475 |
if first_response.content:
|
| 476 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 477 |
except Exception as e:
|
| 478 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 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 |
-
|
| 1613 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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();
|