File size: 6,461 Bytes
8ea8dc0
424479b
 
8b946f6
 
 
 
 
424479b
8ea8dc0
 
424479b
8b946f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ea8dc0
8b946f6
8ea8dc0
 
 
3cf7dd9
8b946f6
 
 
 
 
 
 
 
 
 
 
8ea8dc0
424479b
8b946f6
 
 
8ea8dc0
 
 
 
 
 
8b946f6
 
 
 
 
 
 
 
 
 
 
 
424479b
8b946f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8ea8dc0
8b946f6
033ae3f
8b946f6
 
 
 
 
 
 
 
033ae3f
8b946f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
033ae3f
 
8b946f6
 
033ae3f
8b946f6
033ae3f
8b946f6
 
424479b
 
8b946f6
 
3cf7dd9
424479b
8b946f6
 
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
import os
import gradio as gr
from huggingface_hub import InferenceClient
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
import json
from typing import Generator
import asyncio

# Récupérer le token depuis les variables d'environnement (Secrets)
HF_TOKEN = os.environ.get("HF_TOKEN")

# Définir un message système
SYSTEM_MESSAGE = """
Tu es TeachEase, un enseignant virtuel conçu pour aider les élèves à comprendre leurs cours, faire leurs exercices et devoirs.
Ton rôle est d'expliquer les concepts de manière claire et pédagogique, de fournir des exemples concrets et de poser des questions pour vérifier la compréhension.
**Instructions spéciales :**
1. **Salutations :** Réponds de manière courte et amicale.
2. **Expressions mathématiques et scientifiques :** Utilise le format LaTeX.
3. **Traduction et réponses multilingues :** Adapte ta réponse en fonction de la langue demandée.
"""

# Créer l'application FastAPI
app = FastAPI(title="TeachEase API")

def detect_language_request(message: str) -> str:
    """Détecte la langue demandée"""
    language_requests = {
        "anglais": "en", "english": "en", "espagnol": "es", "spanish": "es",
        "allemand": "de", "german": "de", "portugais": "pt", "portuguese": "pt",
        "français": "fr", "french": "fr", "italien": "it", "italian": "it",
        "chinois": "zh", "chinese": "zh", "japonais": "ja", "japanese": "ja",
        "russe": "ru",
    }
    for keyword, lang_code in language_requests.items():
        if keyword in message.lower():
            return lang_code
    return None

async def generate_stream_response(message: str, history: list = None, max_tokens: int = 256, temperature: float = 0.7, top_p: float = 0.95) -> Generator[str, None, None]:
    """Génère une réponse en streaming"""
    if not HF_TOKEN:
        yield json.dumps({"error": "HF_TOKEN non configuré"})
        return
    
    try:
        client = InferenceClient(token=HF_TOKEN, model="openai/gpt-oss-20b")
        
        messages = [{"role": "system", "content": SYSTEM_MESSAGE}]
        
        # Ajouter l'historique si disponible
        if history:
            for exchange in history[-3:]:  # Limiter aux 3 derniers échanges
                if exchange.get("user"):
                    messages.append({"role": "user", "content": exchange["user"]})
                if exchange.get("assistant"):
                    messages.append({"role": "assistant", "content": exchange["assistant"]})
        
        messages.append({"role": "user", "content": message})

        full_response = ""
        
        for chunk in client.chat_completion(
            messages,
            max_tokens=max_tokens,
            stream=True,
            temperature=temperature,
            top_p=top_p,
        ):
            if chunk.choices and chunk.choices[0].delta.content:
                token = chunk.choices[0].delta.content
                full_response += token
                # Envoyer chaque token au fur et à mesure
                yield f"data: {json.dumps({'token': token, 'full_response': full_response})}\n\n"
                await asyncio.sleep(0.01)  # Petit délai pour le streaming
                
        # Signal de fin
        yield f"data: {json.dumps({'done': True, 'full_response': full_response})}\n\n"
        
    except Exception as e:
        yield f"data: {json.dumps({'error': str(e)})}\n\n"

@app.post("/api/chat/stream")
async def chat_stream(request: Request):
    """Endpoint API pour le streaming"""
    try:
        data = await request.json()
        message = data.get("message", "")
        history = data.get("history", [])
        max_tokens = data.get("max_tokens", 256)
        temperature = data.get("temperature", 0.7)
        top_p = data.get("top_p", 0.95)
        
        return StreamingResponse(
            generate_stream_response(message, history, max_tokens, temperature, top_p),
            media_type="text/event-stream",
            headers={
                "Access-Control-Allow-Origin": "*",
                "Access-Control-Allow-Methods": "POST, OPTIONS",
                "Access-Control-Allow-Headers": "Content-Type",
            }
        )
    except Exception as e:
        return {"error": str(e)}

@app.options("/api/chat/stream")
async def options_chat_stream():
    """Gérer les requêtes OPTIONS pour CORS"""
    return {
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Methods": "POST, OPTIONS",
        "Access-Control-Allow-Headers": "Content-Type",
    }

# Interface Gradio (optionnelle)
def respond(message, history, system_message=SYSTEM_MESSAGE, max_tokens=256, temperature=0.7, top_p=0.95):
    """Fonction pour l'interface Gradio"""
    if not HF_TOKEN:
        yield "❌ Erreur : HF_TOKEN non configuré"
        return
    
    try:
        client = InferenceClient(token=HF_TOKEN, model="openai/gpt-oss-20b")
        messages = [{"role": "system", "content": system_message}]
        
        for val in history[-3:]:
            if val[0]: messages.append({"role": "user", "content": val[0]})
            if val[1]: messages.append({"role": "assistant", "content": val[1]})
        
        messages.append({"role": "user", "content": message})
        response = ""

        for message in client.chat_completion(
            messages, max_tokens=max_tokens, stream=True, temperature=temperature, top_p=top_p
        ):
            if message.choices and message.choices[0].delta.content:
                token = message.choices[0].delta.content
                response += token
                yield response
                
    except Exception as e:
        yield f"❌ Erreur : {str(e)}"

# Configuration de l'interface Gradio
demo = gr.ChatInterface(
    respond,
    additional_inputs=[
        gr.Textbox(value=SYSTEM_MESSAGE, label="System message"),
        gr.Slider(minimum=1, maximum=512, value=256, step=1, label="Max new tokens"),
        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
        gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"),
    ],
    title="TeachEase - Enseignant Virtuel",
    description="Bienvenue sur TeachEase ! API streaming disponible sur /api/chat/stream"
)

# Lancer avec Gradio App
app = gr.mount_gradio_app(app, demo, path="/")

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=7860)