File size: 7,344 Bytes
1b9e6e3
 
fc43f27
 
3bdaa78
f9c30bd
1b9e6e3
3bdaa78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ea41834
 
 
 
 
 
 
 
 
 
3bdaa78
421af5a
e972600
421af5a
2bfec82
421af5a
 
 
 
f91d6af
 
 
 
 
 
 
8b09c72
2bfec82
421af5a
 
 
 
dbaa4dc
421af5a
 
846f316
421af5a
360ff2a
 
 
072db12
96da2bc
 
072db12
 
96da2bc
1b9e6e3
e972600
3bdaa78
ff87670
3bdaa78
 
 
 
 
ff87670
3f0800d
 
1b9e6e3
3f0800d
08f9aa9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3bdaa78
 
 
 
 
 
 
 
 
08f9aa9
 
3bdaa78
 
08f9aa9
 
 
 
 
3bdaa78
3f0800d
3bdaa78
3f0800d
3bdaa78
 
 
 
 
 
 
 
 
1b9e6e3
3bdaa78
 
 
 
 
 
 
 
 
 
2bfec82
08f9aa9
 
b9dcc6f
ff87670
 
 
3bdaa78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ff87670
ea41834
ff87670
 
ea41834
3bdaa78
 
9bc6a92
2bfec82
c2f81a6
1b9e6e3
3f0800d
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
import gradio as gr
from huggingface_hub import InferenceClient
import os

# Get HF token from environment variable
HF_TOKEN = os.getenv("HF_TOKEN")

# Initialize client with proper error handling
def get_client():
    if HF_TOKEN:
        try:
            # Try with the preferred model first
            return InferenceClient("HuggingFaceH4/zephyr-7b-beta", token=HF_TOKEN)
        except Exception as e:
            print(f"Failed to initialize zephyr model: {e}")
            # Fallback to mistral with token
            try:
                return InferenceClient("mistralai/Mistral-7B-Instruct-v0.1", token=HF_TOKEN)
            except Exception as e2:
                print(f"Failed to initialize mistral model: {e2}")
                return None
    else:
        print("No HF_TOKEN found. Please set your Hugging Face token.")
        return None

client = get_client()

# Dynamic prompt builder based on CEFR level
def level_to_prompt(level):
    return {
        "A1": "You are a friendly French tutor. Speak mostly in English, use simple French, and explain everything.",
        "A2": "You are a patient French tutor. Use short French phrases and explain them in English.",
        "B1": "You are a helpful French tutor. Speak mostly in French but clarify in English when needed.",
        "B2": "You are a French tutor. Speak primarily in French with rare English support.",
        "C1": "You are a native French tutor. Speak entirely in French, clearly and professionally.",
        "C2": "You are a native French professor. Speak in rich, complex French. Avoid English."
    }.get(level, "You are a helpful French tutor.")

# Custom background CSS
css = """
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP&family=Playfair+Display&display=swap');
body {
  background-image: url('https://cdn-uploads.huggingface.co/production/uploads/67351c643fe51cb1aa28f2e5/wuyd5UYTh9jPrMJGmV9yC.jpeg');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}
.gradio-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 100vh;
  padding-top: 2rem;
  padding-bottom: 2rem;
}  
#chat-panel {
  background-color: rgba(255, 255, 255, 0.85);
  padding: 2rem;
  border-radius: 12px;
  max-width: 700px;
  height: 70vh;
  margin: auto;
  box-shadow: 0 0 12px rgba(0, 0, 0, 0.3);
  overflow-y: auto;
}
.gradio-container .chatbot h1 {
  color: var(--custom-title-color) !important;
  font-family: 'Playfair Display', serif !important;
  font-size: 5rem !important;
  font-weight: bold !important;
  text-align: center !important;
  margin-bottom: 1.5rem !important;
  width: 100%;
}
"""

# Chat logic with proper error handling
def respond(message, history, level, max_tokens, temperature, top_p):
    # Check if client is available
    if client is None:
        yield "❌ Désolé! The AI service is not available. Please check your Hugging Face token configuration."
        return
    
    system_message = level_to_prompt(level)
    
    # Generate response
    response = ""
    try:
        # Create a proper prompt format for instruction-following models
        prompt = f"<|system|>\n{system_message}\n\n"
        
        # Add conversation history
        if history:
            for turn in history:
                if isinstance(turn, dict):
                    if turn.get("role") == "user":
                        prompt += f"<|user|>\n{turn['content']}\n\n"
                    elif turn.get("role") == "assistant":
                        prompt += f"<|assistant|>\n{turn['content']}\n\n"
                else:
                    # Handle tuple format (user, assistant)
                    user_msg, bot_msg = turn
                    if user_msg:
                        prompt += f"<|user|>\n{user_msg}\n\n"
                    if bot_msg:
                        prompt += f"<|assistant|>\n{bot_msg}\n\n"
        
        # Add current user message
        prompt += f"<|user|>\n{message}\n\n<|assistant|>\n"
        
        # Generate response with streaming
        for token in client.text_generation(
            prompt, 
            max_new_tokens=max_tokens, 
            stream=True, 
            temperature=temperature, 
            top_p=top_p,
            do_sample=True,
            return_full_text=False,
            stop_sequences=["<|user|>", "<|system|>"]  # Stop if model tries to continue conversation
        ):
            if token:  # Handle None tokens
                # Clean up any unwanted tokens
                token = token.replace("<|user|>", "").replace("<|system|>", "").replace("<|assistant|>", "")
                if token.strip():  # Only add non-empty tokens
                    response += token
                    yield response
                
    except Exception as e:
        error_msg = str(e)
        print(f"Error in chat completion: {e}")
        
        if "401" in error_msg or "Unauthorized" in error_msg:
            yield "🔑 Authentication Error: Please check your Hugging Face token. Make sure it's valid and has the correct permissions."
        elif "429" in error_msg or "rate limit" in error_msg.lower():
            yield "⏰ Rate limit exceeded. Please wait a moment before trying again."
        elif "503" in error_msg or "Service Unavailable" in error_msg:
            yield "🔧 The AI service is temporarily unavailable. Please try again later."
        else:
            yield f"❌ Désolé! There was an error: {error_msg}"

# UI layout
with gr.Blocks(css=css, title="French Tutor") as demo:
    gr.Markdown("# 🇫🇷 French Tutor", elem_id="custom-title")
    
    # Add status indicator
    if client is None:
        gr.Markdown("⚠️ **Warning**: No Hugging Face token found. Please set your HF_TOKEN environment variable.")
    else:
        gr.Markdown("✅ **Status**: Connected to AI service")
    
    with gr.Column(elem_id="chat-panel"):
        gr.Markdown("""
        
        with gr.Accordion("⚙️ Advanced Settings", open=False):
            level = gr.Dropdown(
                choices=["A1", "A2", "B1", "B2", "C1", "C2"],
                value="A1",
                label="Your French Level (CEFR)",
                info="Choose your current French proficiency level"
            )
            max_tokens = gr.Slider(
                1, 2048, 
                value=512, 
                step=1, 
                label="Response Length",
                info="Maximum number of tokens in the response"
            )
            temperature = gr.Slider(
                0.1, 2.0, 
                value=0.7, 
                step=0.1, 
                label="Creativity",
                info="Higher values make responses more creative"
            )
            top_p = gr.Slider(
                0.1, 1.0, 
                value=0.95, 
                step=0.05, 
                label="Dynamic Text",
                info="Controls text diversity"
            )
        
        gr.ChatInterface(
            fn=respond,
            additional_inputs=[level, max_tokens, temperature, top_p],
            type="messages",
            title="Chat with your French Tutor",
            description="Ask questions, practice conversation, or get help with French grammar!"
        )
""")
if __name__ == "__main__":
    demo.launch()