Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -4,14 +4,32 @@ from langdetect import detect
|
|
4 |
from huggingface_hub import InferenceClient
|
5 |
import pandas as pd
|
6 |
import os
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
|
8 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
9 |
|
10 |
-
# Fonction pour appeler l'API Zephyr
|
11 |
-
def call_zephyr_api(prompt, hf_token=HF_TOKEN):
|
12 |
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta", token=hf_token)
|
13 |
try:
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
return response
|
16 |
except Exception as e:
|
17 |
raise gr.Error(f"❌ Erreur d'appel API Hugging Face : {str(e)}")
|
@@ -23,6 +41,18 @@ classifier = pipeline("sentiment-analysis", model="mrm8488/distilroberta-finetun
|
|
23 |
translator_to_en = pipeline("translation", model="Helsinki-NLP/opus-mt-mul-en")
|
24 |
translator_to_fr = pipeline("translation", model="Helsinki-NLP/opus-mt-en-fr")
|
25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
26 |
# Fonction pour suggérer le meilleur modèle
|
27 |
def suggest_model(text):
|
28 |
word_count = len(text.split())
|
@@ -36,35 +66,30 @@ def suggest_model(text):
|
|
36 |
# Fonction pour créer une jauge de sentiment
|
37 |
def create_sentiment_gauge(sentiment, score):
|
38 |
score_percentage = score * 100
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
color = "green"
|
43 |
elif sentiment.lower() == "negative":
|
44 |
-
color = "
|
45 |
-
else:
|
46 |
-
color = "gray"
|
47 |
|
48 |
html = f"""
|
49 |
<div style='width: 100%; max-width: 300px; margin: 10px 0;'>
|
50 |
-
<div style='background-color: #
|
51 |
-
<div style='background-color: {color}; width: {score_percentage}%; height: 100%; border-radius: 5px;'>
|
52 |
-
|
53 |
-
<span style='position: absolute; top: 0; left: 50%; transform: translateX(-50%); color: black; font-size: 12px; line-height: 20px;'>
|
54 |
-
{score_percentage:.1f}%
|
55 |
-
</span>
|
56 |
-
</div>
|
57 |
-
<div style='text-align: center; font-size: 14px; margin-top: 5px;'>
|
58 |
-
Sentiment: {sentiment}
|
59 |
</div>
|
|
|
60 |
</div>
|
61 |
"""
|
62 |
return html
|
63 |
|
64 |
# Fonction d'analyse
|
65 |
-
def full_analysis(text, mode, detail_mode, count, history):
|
66 |
if not text:
|
67 |
-
|
|
|
|
|
|
|
68 |
|
69 |
try:
|
70 |
lang = detect(text)
|
@@ -72,41 +97,35 @@ def full_analysis(text, mode, detail_mode, count, history):
|
|
72 |
lang = "unknown"
|
73 |
|
74 |
if lang != "en":
|
75 |
-
|
|
|
|
|
76 |
|
77 |
-
|
78 |
-
prediction_prompt = f"""<|system|>
|
79 |
-
You are a professional financial analyst AI with expertise in economic forecasting.
|
80 |
-
</s>
|
81 |
-
<|user|>
|
82 |
-
Given the following question about a potential economic event: "{text}"
|
83 |
|
84 |
-
|
85 |
-
|
86 |
-
<|assistant|>"""
|
87 |
-
prediction_response = call_zephyr_api(prediction_prompt)
|
88 |
-
|
89 |
-
# Étape 2 : Analyser le sentiment de la réponse de Zephyr
|
90 |
-
result = classifier(prediction_response)[0]
|
91 |
sentiment_output = f"Sentiment prédictif : {result['label']} (Score: {result['score']:.2f})"
|
92 |
sentiment_gauge = create_sentiment_gauge(result['label'], result['score'])
|
93 |
|
94 |
-
|
|
|
95 |
explanation_prompt = f"""<|system|>
|
96 |
-
You are a professional financial analyst AI.
|
97 |
</s>
|
98 |
<|user|>
|
99 |
Given the following question about a potential economic event: "{text}"
|
100 |
|
101 |
-
|
102 |
-
|
103 |
-
The predicted sentiment for this impact is: {result['label'].lower()}.
|
104 |
|
105 |
-
|
106 |
</s>
|
107 |
<|assistant|>"""
|
108 |
-
explanation_en = call_zephyr_api(explanation_prompt)
|
109 |
-
|
|
|
|
|
|
|
110 |
|
111 |
count += 1
|
112 |
history.append({
|
@@ -117,9 +136,9 @@ Now, explain why the sentiment is {result['label'].lower()} using a logical, fac
|
|
117 |
"Explication_FR": explanation_fr
|
118 |
})
|
119 |
|
120 |
-
|
121 |
|
122 |
-
#
|
123 |
def download_history(history):
|
124 |
if not history:
|
125 |
return None
|
@@ -128,44 +147,62 @@ def download_history(history):
|
|
128 |
df.to_csv(file_path, index=False)
|
129 |
return file_path
|
130 |
|
131 |
-
#
|
132 |
def launch_app():
|
133 |
-
|
134 |
-
|
135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
136 |
|
137 |
count = gr.State(0)
|
138 |
history = gr.State([])
|
139 |
|
140 |
with gr.Row():
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
choices=["
|
146 |
-
value="Équilibré",
|
147 |
-
label="Mode recommandé selon la taille"
|
148 |
-
)
|
149 |
-
detail_mode_selector = gr.Dropdown(
|
150 |
-
choices=["Normal", "Expert"],
|
151 |
-
value="Normal",
|
152 |
-
label="Niveau de détail"
|
153 |
-
)
|
154 |
|
155 |
analyze_btn = gr.Button("Analyser")
|
156 |
-
|
157 |
-
download_btn = gr.Button("Télécharger CSV")
|
158 |
-
|
159 |
-
with gr.Row():
|
160 |
-
sentiment_output = gr.Textbox(label="Résultat du Sentiment Prédictif")
|
161 |
-
|
162 |
-
sentiment_gauge = gr.HTML(label="Jauge de Sentiment")
|
163 |
|
164 |
with gr.Row():
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
|
|
|
|
169 |
|
170 |
download_file = gr.File(label="Fichier CSV")
|
171 |
|
@@ -174,7 +211,7 @@ def launch_app():
|
|
174 |
analyze_btn.click(
|
175 |
full_analysis,
|
176 |
inputs=[input_text, mode_selector, detail_mode_selector, count, history],
|
177 |
-
outputs=[sentiment_output, explanation_output_en, explanation_output_fr, count, history, sentiment_gauge]
|
178 |
)
|
179 |
|
180 |
download_btn.click(
|
@@ -183,7 +220,7 @@ def launch_app():
|
|
183 |
outputs=[download_file]
|
184 |
)
|
185 |
|
186 |
-
iface.launch()
|
187 |
|
188 |
if __name__ == "__main__":
|
189 |
launch_app()
|
|
|
4 |
from huggingface_hub import InferenceClient
|
5 |
import pandas as pd
|
6 |
import os
|
7 |
+
import asyncio
|
8 |
+
import nltk
|
9 |
+
from nltk.tokenize import sent_tokenize
|
10 |
+
|
11 |
+
# Téléchargement de punkt_tab avec gestion d'erreur
|
12 |
+
try:
|
13 |
+
nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')
|
14 |
+
except Exception as e:
|
15 |
+
raise Exception(f"Erreur lors du téléchargement de punkt_tab : {str(e)}. Veuillez vérifier votre connexion réseau et les permissions du répertoire /usr/local/share/nltk_data.")
|
16 |
|
17 |
HF_TOKEN = os.getenv("HF_TOKEN")
|
18 |
|
19 |
+
# Fonction pour appeler l'API Zephyr avec des paramètres ajustés
|
20 |
+
async def call_zephyr_api(prompt, mode, hf_token=HF_TOKEN):
|
21 |
client = InferenceClient("HuggingFaceH4/zephyr-7b-beta", token=hf_token)
|
22 |
try:
|
23 |
+
if mode == "Rapide":
|
24 |
+
max_new_tokens = 50
|
25 |
+
temperature = 0.3
|
26 |
+
elif mode == "Équilibré":
|
27 |
+
max_new_tokens = 100
|
28 |
+
temperature = 0.5
|
29 |
+
else: # Précis
|
30 |
+
max_new_tokens = 150
|
31 |
+
temperature = 0.7
|
32 |
+
response = await asyncio.to_thread(client.text_generation, prompt, max_new_tokens=max_new_tokens, temperature=temperature)
|
33 |
return response
|
34 |
except Exception as e:
|
35 |
raise gr.Error(f"❌ Erreur d'appel API Hugging Face : {str(e)}")
|
|
|
41 |
translator_to_en = pipeline("translation", model="Helsinki-NLP/opus-mt-mul-en")
|
42 |
translator_to_fr = pipeline("translation", model="Helsinki-NLP/opus-mt-en-fr")
|
43 |
|
44 |
+
# Traduction en français avec Helsinki-NLP
|
45 |
+
def safe_translate_to_fr(text, max_length=512):
|
46 |
+
try:
|
47 |
+
sentences = sent_tokenize(text)
|
48 |
+
translated_sentences = []
|
49 |
+
for sentence in sentences:
|
50 |
+
translated = translator_to_fr(sentence, max_length=max_length)[0]['translation_text']
|
51 |
+
translated_sentences.append(translated)
|
52 |
+
return " ".join(translated_sentences)
|
53 |
+
except Exception as e:
|
54 |
+
return f"Erreur de traduction : {str(e)}"
|
55 |
+
|
56 |
# Fonction pour suggérer le meilleur modèle
|
57 |
def suggest_model(text):
|
58 |
word_count = len(text.split())
|
|
|
66 |
# Fonction pour créer une jauge de sentiment
|
67 |
def create_sentiment_gauge(sentiment, score):
|
68 |
score_percentage = score * 100
|
69 |
+
color = "#A9A9A9"
|
70 |
+
if sentiment.lower() == "positive":
|
71 |
+
color = "#2E8B57"
|
|
|
72 |
elif sentiment.lower() == "negative":
|
73 |
+
color = "#DC143C"
|
|
|
|
|
74 |
|
75 |
html = f"""
|
76 |
<div style='width: 100%; max-width: 300px; margin: 10px 0;'>
|
77 |
+
<div style='background-color: #D3D3D3; border-radius: 5px; height: 20px; position: relative;'>
|
78 |
+
<div style='background-color: {color}; width: {score_percentage}%; height: 100%; border-radius: 5px;'></div>
|
79 |
+
<span style='position: absolute; top: 0; left: 50%; transform: translateX(-50%); font-weight: bold;'>{score_percentage:.1f}%</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
</div>
|
81 |
+
<div style='text-align: center; margin-top: 5px;'>Sentiment : {sentiment}</div>
|
82 |
</div>
|
83 |
"""
|
84 |
return html
|
85 |
|
86 |
# Fonction d'analyse
|
87 |
+
async def full_analysis(text, mode, detail_mode, count, history):
|
88 |
if not text:
|
89 |
+
yield "Entrez une phrase.", "", "", "", 0, history, "", "Aucune analyse effectuée."
|
90 |
+
return
|
91 |
+
|
92 |
+
yield "Analyse en cours... (Étape 1 : Détection de la langue)", "", "", "", count, history, "", "Détection de la langue"
|
93 |
|
94 |
try:
|
95 |
lang = detect(text)
|
|
|
97 |
lang = "unknown"
|
98 |
|
99 |
if lang != "en":
|
100 |
+
text_en = translator_to_en(text, max_length=512)[0]['translation_text']
|
101 |
+
else:
|
102 |
+
text_en = text
|
103 |
|
104 |
+
yield "Analyse en cours... (Étape 2 : Analyse du sentiment)", "", "", "", count, history, "", "Analyse du sentiment"
|
|
|
|
|
|
|
|
|
|
|
105 |
|
106 |
+
result = await asyncio.to_thread(classifier, text_en)
|
107 |
+
result = result[0]
|
|
|
|
|
|
|
|
|
|
|
108 |
sentiment_output = f"Sentiment prédictif : {result['label']} (Score: {result['score']:.2f})"
|
109 |
sentiment_gauge = create_sentiment_gauge(result['label'], result['score'])
|
110 |
|
111 |
+
yield "Analyse en cours... (Étape 3 : Explication IA)", "", "", "", count, history, "", "Génération de l'explication"
|
112 |
+
|
113 |
explanation_prompt = f"""<|system|>
|
114 |
+
You are a professional financial analyst AI with expertise in economic forecasting.
|
115 |
</s>
|
116 |
<|user|>
|
117 |
Given the following question about a potential economic event: "{text}"
|
118 |
|
119 |
+
The predicted sentiment for this event is: {result['label'].lower()}.
|
|
|
|
|
120 |
|
121 |
+
Assume the event happens. Explain why this event would likely have a {result['label'].lower()} economic impact.
|
122 |
</s>
|
123 |
<|assistant|>"""
|
124 |
+
explanation_en = await call_zephyr_api(explanation_prompt, mode)
|
125 |
+
|
126 |
+
yield "Analyse en cours... (Étape 4 : Traduction en français)", "", "", "", count, history, "", "Traduction en français"
|
127 |
+
|
128 |
+
explanation_fr = safe_translate_to_fr(explanation_en)
|
129 |
|
130 |
count += 1
|
131 |
history.append({
|
|
|
136 |
"Explication_FR": explanation_fr
|
137 |
})
|
138 |
|
139 |
+
yield sentiment_output, text, explanation_en, explanation_fr, count, history, sentiment_gauge, "✅ Analyse terminée."
|
140 |
|
141 |
+
# Historique CSV
|
142 |
def download_history(history):
|
143 |
if not history:
|
144 |
return None
|
|
|
147 |
df.to_csv(file_path, index=False)
|
148 |
return file_path
|
149 |
|
150 |
+
# Lancement Gradio avec l'interface restaurée
|
151 |
def launch_app():
|
152 |
+
custom_css = """
|
153 |
+
/* CSS restauré à la version précédente, avant les changements esthétiques non demandés */
|
154 |
+
body {
|
155 |
+
background: linear-gradient(135deg, #0A1D37 0%, #1A3C34 100%);
|
156 |
+
font-family: 'Inter', sans-serif;
|
157 |
+
color: #E0E0E0;
|
158 |
+
padding: 20px;
|
159 |
+
}
|
160 |
+
.gr-box {
|
161 |
+
background: #2A4A43 !important;
|
162 |
+
border: 1px solid #FFD700 !important;
|
163 |
+
border-radius: 12px !important;
|
164 |
+
padding: 20px !important;
|
165 |
+
box-shadow: 0px 4px 12px rgba(255, 215, 0, 0.4);
|
166 |
+
}
|
167 |
+
.gr-button {
|
168 |
+
background: linear-gradient(90deg, #FFD700, #D4AF37);
|
169 |
+
color: #0A1D37;
|
170 |
+
font-weight: bold;
|
171 |
+
border: none;
|
172 |
+
border-radius: 8px;
|
173 |
+
padding: 12px 24px;
|
174 |
+
transition: transform 0.2s;
|
175 |
+
}
|
176 |
+
.gr-button:hover {
|
177 |
+
transform: translateY(-2px);
|
178 |
+
box-shadow: 0 6px 12px rgba(255, 215, 0, 0.5);
|
179 |
+
}
|
180 |
+
"""
|
181 |
+
|
182 |
+
with gr.Blocks(theme=gr.themes.Base(), css=custom_css) as iface:
|
183 |
+
gr.Markdown("# 📈 Analyse Financière Premium avec IA")
|
184 |
+
gr.Markdown("**Posez une question économique.** L'IA analyse et explique l'impact.")
|
185 |
|
186 |
count = gr.State(0)
|
187 |
history = gr.State([])
|
188 |
|
189 |
with gr.Row():
|
190 |
+
with gr.Column(scale=2):
|
191 |
+
input_text = gr.Textbox(lines=4, label="Votre question économique")
|
192 |
+
with gr.Column(scale=1):
|
193 |
+
mode_selector = gr.Dropdown(choices=["Rapide", "Équilibré", "Précis"], value="Équilibré", label="Mode de réponse")
|
194 |
+
detail_mode_selector = gr.Dropdown(choices=["Normal", "Expert"], value="Normal", label="Niveau de détail")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
195 |
|
196 |
analyze_btn = gr.Button("Analyser")
|
197 |
+
download_btn = gr.Button("Télécharger l'historique")
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
|
199 |
with gr.Row():
|
200 |
+
sentiment_output = gr.Textbox(label="Sentiment prédictif")
|
201 |
+
displayed_prompt = gr.Textbox(label="Votre question", interactive=False)
|
202 |
+
explanation_output_en = gr.Textbox(label="Explication en anglais")
|
203 |
+
explanation_output_fr = gr.Textbox(label="Explication en français")
|
204 |
+
sentiment_gauge = gr.HTML()
|
205 |
+
progress_message = gr.Textbox(label="Progression", interactive=False)
|
206 |
|
207 |
download_file = gr.File(label="Fichier CSV")
|
208 |
|
|
|
211 |
analyze_btn.click(
|
212 |
full_analysis,
|
213 |
inputs=[input_text, mode_selector, detail_mode_selector, count, history],
|
214 |
+
outputs=[sentiment_output, displayed_prompt, explanation_output_en, explanation_output_fr, count, history, sentiment_gauge, progress_message]
|
215 |
)
|
216 |
|
217 |
download_btn.click(
|
|
|
220 |
outputs=[download_file]
|
221 |
)
|
222 |
|
223 |
+
iface.launch(share=True)
|
224 |
|
225 |
if __name__ == "__main__":
|
226 |
launch_app()
|