import os from dotenv import load_dotenv from ecologits import EcoLogits from mistralai import Mistral import gradio as gr # Load environment variables from .env file load_dotenv() # Initialize EcoLogits EcoLogits.init(providers=["mistralai"]) client = Mistral(api_key=os.getenv("MISTRAL_API_KEY")) # Initialize cumulative impacts storage class CumulativeImpacts: def __init__(self): self.reset() def reset(self): self.energy_min = 0.0 self.energy_max = 0.0 self.energy_unit = "kWh" self.gwp_min = 0.0 self.gwp_max = 0.0 self.gwp_unit = "kgCO2eq" self.adpe_min = 0.0 self.adpe_max = 0.0 self.adpe_unit = "kgSbeq" self.pe_min = 0.0 self.pe_max = 0.0 self.pe_unit = "kWh" self.usage_energy_min = 0.0 self.usage_energy_max = 0.0 self.usage_gwp_min = 0.0 self.usage_gwp_max = 0.0 self.embodied_gwp = 0.0 self.embodied_gwp_unit = "kgCO2eq" self.embodied_adpe = 0.0 self.embodied_adpe_unit = "kgSbeq" self.embodied_pe = 0.0 self.embodied_pe_unit = "kWh" self.generation_count = 0 # Store previous values for delta calculation self.prev_energy_min = 0.0 self.prev_energy_max = 0.0 self.prev_gwp_min = 0.0 self.prev_gwp_max = 0.0 self.prev_adpe_min = 0.0 self.prev_adpe_max = 0.0 self.prev_pe_min = 0.0 self.prev_pe_max = 0.0 self.prev_usage_energy_min = 0.0 self.prev_usage_energy_max = 0.0 self.prev_usage_gwp_min = 0.0 self.prev_usage_gwp_max = 0.0 self.prev_embodied_gwp = 0.0 self.prev_embodied_adpe = 0.0 self.prev_embodied_pe = 0.0 def get_generation_delta(self): """Get the delta between current and previous totals (impact of last generation)""" return { 'energy_min': self.energy_min - self.prev_energy_min, 'energy_max': self.energy_max - self.prev_energy_max, 'energy_unit': self.energy_unit, 'gwp_min': self.gwp_min - self.prev_gwp_min, 'gwp_max': self.gwp_max - self.prev_gwp_max, 'gwp_unit': self.gwp_unit, 'adpe_min': self.adpe_min - self.prev_adpe_min, 'adpe_max': self.adpe_max - self.prev_adpe_max, 'adpe_unit': self.adpe_unit, 'pe_min': self.pe_min - self.prev_pe_min, 'pe_max': self.pe_max - self.prev_pe_max, 'pe_unit': self.pe_unit, 'usage_energy_min': self.usage_energy_min - self.prev_usage_energy_min, 'usage_energy_max': self.usage_energy_max - self.prev_usage_energy_max, 'usage_gwp_min': self.usage_gwp_min - self.prev_usage_gwp_min, 'usage_gwp_max': self.usage_gwp_max - self.prev_usage_gwp_max, 'embodied_gwp': float(self.embodied_gwp) - float(self.prev_embodied_gwp), 'embodied_gwp_unit': self.embodied_gwp_unit, 'embodied_adpe': float(self.embodied_adpe) - float(self.prev_embodied_adpe), 'embodied_adpe_unit': self.embodied_adpe_unit, 'embodied_pe': float(self.embodied_pe) - float(self.prev_embodied_pe), 'embodied_pe_unit': self.embodied_pe_unit } def add_impacts(self, impacts): # Store previous values before updating self.prev_energy_min = self.energy_min self.prev_energy_max = self.energy_max self.prev_gwp_min = self.gwp_min self.prev_gwp_max = self.gwp_max self.prev_adpe_min = self.adpe_min self.prev_adpe_max = self.adpe_max self.prev_pe_min = self.pe_min self.prev_pe_max = self.pe_max self.prev_usage_energy_min = self.usage_energy_min self.prev_usage_energy_max = self.usage_energy_max self.prev_usage_gwp_min = self.usage_gwp_min self.prev_usage_gwp_max = self.usage_gwp_max # Convert values to float to avoid type issues self.prev_embodied_gwp = float(self.embodied_gwp) if self.embodied_gwp is not None else 0.0 self.prev_embodied_adpe = float(self.embodied_adpe) if self.embodied_adpe is not None else 0.0 self.prev_embodied_pe = float(self.embodied_pe) if self.embodied_pe is not None else 0.0 # Update current values self.energy_min += impacts.energy.value.min self.energy_max += impacts.energy.value.max self.energy_unit = impacts.energy.unit self.gwp_min += impacts.gwp.value.min self.gwp_max += impacts.gwp.value.max self.gwp_unit = impacts.gwp.unit self.adpe_min += impacts.adpe.value.min self.adpe_max += impacts.adpe.value.max self.adpe_unit = impacts.adpe.unit self.pe_min += impacts.pe.value.min self.pe_max += impacts.pe.value.max self.pe_unit = impacts.pe.unit self.usage_energy_min += impacts.usage.energy.value.min self.usage_energy_max += impacts.usage.energy.value.max self.usage_gwp_min += impacts.usage.gwp.value.min self.usage_gwp_max += impacts.usage.gwp.value.max # Handle embodied impacts - they might be RangeValue objects or simple floats if hasattr(impacts.embodied.gwp.value, 'min'): self.embodied_gwp += impacts.embodied.gwp.value.min # Use min if it's a RangeValue else: self.embodied_gwp += impacts.embodied.gwp.value # Use direct value if it's a float self.embodied_gwp_unit = impacts.embodied.gwp.unit if hasattr(impacts.embodied.adpe.value, 'min'): self.embodied_adpe += impacts.embodied.adpe.value.min else: self.embodied_adpe += impacts.embodied.adpe.value self.embodied_adpe_unit = impacts.embodied.adpe.unit if hasattr(impacts.embodied.pe.value, 'min'): self.embodied_pe += impacts.embodied.pe.value.min else: self.embodied_pe += impacts.embodied.pe.value self.embodied_pe_unit = impacts.embodied.pe.unit self.generation_count += 1 # Global cumulative impacts instance cumulative_impacts = CumulativeImpacts() def get_response(message, history, model): message_aggregate = [] if history: message_aggregate.extend(history) message_aggregate.append({"role": "user", "content": message}) print(message_aggregate) # Use the selected model or default to mistral-tiny if not model: model = "mistral-tiny" print(f"Using model: {model}") response = client.chat.complete( messages=message_aggregate, model=model ) if response.impacts.has_warnings: # type: ignore for w in response.impacts.warnings: # type: ignore print(w) if response.impacts.has_errors: # type: ignore for e in response.impacts.errors: # type: ignore print(e) # Get the impacts and format them impacts = response.impacts # type: ignore # Add impacts to cumulative total cumulative_impacts.add_impacts(impacts) # Get the delta for this generation generation_delta = cumulative_impacts.get_generation_delta() message_aggregate.append({ "role": "assistant", "content": response.choices[0].message.content }) # Return current generation impacts, generation delta, and cumulative totals return ( message_aggregate, # Current generation impacts (raw from API) impacts.energy.value.min, impacts.energy.value.max, impacts.energy.unit, impacts.gwp.value.min, impacts.gwp.value.max, impacts.gwp.unit, f"{impacts.adpe.value.min:.2e}", f"{impacts.adpe.value.max:.2e}", impacts.adpe.unit, impacts.pe.value.min, impacts.pe.value.max, impacts.pe.unit, impacts.usage.energy.value.min, impacts.usage.energy.value.max, impacts.usage.gwp.value.min, impacts.usage.gwp.value.max, f"{impacts.embodied.gwp.value:.2e}", impacts.embodied.gwp.unit, f"{impacts.embodied.adpe.value:.2e}", impacts.embodied.adpe.unit, f"{impacts.embodied.pe.value:.2e}", impacts.embodied.pe.unit, # Generation delta (calculated difference) generation_delta['energy_min'], generation_delta['energy_max'], generation_delta['energy_unit'], generation_delta['gwp_min'], generation_delta['gwp_max'], generation_delta['gwp_unit'], f"{generation_delta['adpe_min']:.2e}", f"{generation_delta['adpe_max']:.2e}", generation_delta['adpe_unit'], generation_delta['pe_min'], generation_delta['pe_max'], generation_delta['pe_unit'], generation_delta['usage_energy_min'], generation_delta['usage_energy_max'], generation_delta['usage_gwp_min'], generation_delta['usage_gwp_max'], f"{generation_delta['embodied_gwp']:.2e}", generation_delta['embodied_gwp_unit'], f"{generation_delta['embodied_adpe']:.2e}", generation_delta['embodied_adpe_unit'], f"{generation_delta['embodied_pe']:.2e}", generation_delta['embodied_pe_unit'], # Cumulative impacts cumulative_impacts.energy_min, cumulative_impacts.energy_max, cumulative_impacts.energy_unit, cumulative_impacts.gwp_min, cumulative_impacts.gwp_max, cumulative_impacts.gwp_unit, f"{cumulative_impacts.adpe_min:.2e}", f"{cumulative_impacts.adpe_max:.2e}", cumulative_impacts.adpe_unit, cumulative_impacts.pe_min, cumulative_impacts.pe_max, cumulative_impacts.pe_unit, cumulative_impacts.usage_energy_min, cumulative_impacts.usage_energy_max, cumulative_impacts.usage_gwp_min, cumulative_impacts.usage_gwp_max, f"{cumulative_impacts.embodied_gwp:.2e}", cumulative_impacts.embodied_gwp_unit, f"{cumulative_impacts.embodied_adpe:.2e}", cumulative_impacts.embodied_adpe_unit, f"{cumulative_impacts.embodied_pe:.2e}", cumulative_impacts.embodied_pe_unit, cumulative_impacts.generation_count ) with gr.Blocks() as demo: gr.Markdown("# 🌱 EcoLogits Chat - Chat écologique avec Mistral") gr.Markdown("Discutez avec l'IA tout en surveillant l'impact environnemental de chaque requête") with gr.Row(): with gr.Column(scale=3): chatbot = gr.Chatbot( type="messages", height=600, show_label=False ) with gr.Row(): prompt = gr.Textbox( placeholder="Tapez votre message ici...", scale=4, show_label=False, max_lines=3 ) send_btn = gr.Button("Envoyer", variant="primary", scale=1) with gr.Row(): # Get available models from Mistral API try: available_models = client.models.list() model_choices = [model.id for model in available_models.data] if available_models.data else [] except Exception as e: print(f"Warning: Could not fetch models from API: {e}") model_choices = ["mistral-tiny", "mistral-small", "mistral-medium"] if not model_choices: model_choices = ["mistral-tiny", "mistral-small", "mistral-medium"] model = gr.Dropdown( choices=model_choices, value=model_choices[0] if model_choices else "mistral-tiny", label="Modèle Mistral", scale=1 ) with gr.Column(scale=2): gr.Markdown("## 📊 Impact Environnemental") # Generation counter generation_counter = gr.Number(label="🔢 Nombre de générations", precision=0, interactive=False) # Tabs for current vs cumulative impacts with gr.Tabs(): with gr.TabItem("🎯 Dernier message"): gr.Markdown("### � **Coût calculé de cette requête spécifique**") gr.Markdown("*Différence entre le total avant et après cette génération*") with gr.Accordion("⚡ Consommation d'énergie", open=True): with gr.Row(): delta_energy_min = gr.Number(label="Min", precision=8, interactive=False) delta_energy_max = gr.Number(label="Max", precision=8, interactive=False) delta_energy_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🌍 Empreinte carbone (GWP)", open=True): with gr.Row(): delta_gwp_min = gr.Number(label="Min", precision=8, interactive=False) delta_gwp_max = gr.Number(label="Max", precision=8, interactive=False) delta_gwp_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("⛏️ Épuisement des ressources (ADPe)", open=False): with gr.Row(): delta_adpe_min = gr.Textbox(label="Min", interactive=False) delta_adpe_max = gr.Textbox(label="Max", interactive=False) delta_adpe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🔋 Énergie primaire (PE)", open=False): with gr.Row(): delta_pe_min = gr.Number(label="Min", precision=8, interactive=False) delta_pe_max = gr.Number(label="Max", precision=8, interactive=False) delta_pe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("💻 Impact d'usage", open=False): gr.Markdown("**Énergie**") with gr.Row(): delta_usage_energy_min = gr.Number(label="Min", precision=8, interactive=False) delta_usage_energy_max = gr.Number(label="Max", precision=8, interactive=False) gr.Markdown("**GWP**") with gr.Row(): delta_usage_gwp_min = gr.Number(label="Min", precision=8, interactive=False) delta_usage_gwp_max = gr.Number(label="Max", precision=8, interactive=False) with gr.Accordion("🏭 Impact incorporé", open=False): delta_embodied_gwp = gr.Textbox(label="GWP", interactive=False) delta_embodied_gwp_unit = gr.Textbox(label="Unité GWP", interactive=False) delta_embodied_adpe = gr.Textbox(label="ADPe", interactive=False) delta_embodied_adpe_unit = gr.Textbox(label="Unité ADPe", interactive=False) delta_embodied_pe = gr.Textbox(label="PE", interactive=False) delta_embodied_pe_unit = gr.Textbox(label="Unité PE", interactive=False) with gr.TabItem("📈 Chat complet"): gr.Markdown("### 🔧 **Données directes de l'API EcoLogits**") with gr.Accordion("⚡ Consommation d'énergie", open=True): with gr.Row(): energy_min = gr.Number(label="Min", precision=6, interactive=False) energy_max = gr.Number(label="Max", precision=6, interactive=False) energy_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🌍 Empreinte carbone (GWP)", open=True): with gr.Row(): gwp_min = gr.Number(label="Min", precision=6, interactive=False) gwp_max = gr.Number(label="Max", precision=6, interactive=False) gwp_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("⛏️ Épuisement des ressources (ADPe)", open=False): with gr.Row(): adpe_min = gr.Textbox(label="Min", interactive=False) adpe_max = gr.Textbox(label="Max", interactive=False) adpe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🔋 Énergie primaire (PE)", open=False): with gr.Row(): pe_min = gr.Number(label="Min", precision=6, interactive=False) pe_max = gr.Number(label="Max", precision=6, interactive=False) pe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("💻 Impact d'usage", open=False): gr.Markdown("**Énergie**") with gr.Row(): usage_energy_min = gr.Number(label="Min", precision=6, interactive=False) usage_energy_max = gr.Number(label="Max", precision=6, interactive=False) gr.Markdown("**GWP**") with gr.Row(): usage_gwp_min = gr.Number(label="Min", precision=6, interactive=False) usage_gwp_max = gr.Number(label="Max", precision=6, interactive=False) with gr.Accordion("🏭 Impact incorporé", open=False): embodied_gwp = gr.Textbox(label="GWP", interactive=False) embodied_gwp_unit = gr.Textbox(label="Unité GWP", interactive=False) embodied_adpe = gr.Textbox(label="ADPe", interactive=False) embodied_adpe_unit = gr.Textbox(label="Unité ADPe", interactive=False) embodied_pe = gr.Textbox(label="PE", interactive=False) embodied_pe_unit = gr.Textbox(label="Unité PE", interactive=False) with gr.TabItem("📊 Total session"): with gr.Accordion("⚡ Consommation d'énergie totale", open=True): with gr.Row(): total_energy_min = gr.Number(label="Min", precision=6, interactive=False) total_energy_max = gr.Number(label="Max", precision=6, interactive=False) total_energy_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🌍 Empreinte carbone totale (GWP)", open=True): with gr.Row(): total_gwp_min = gr.Number(label="Min", precision=6, interactive=False) total_gwp_max = gr.Number(label="Max", precision=6, interactive=False) total_gwp_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("⛏️ Épuisement des ressources totale (ADPe)", open=False): with gr.Row(): total_adpe_min = gr.Textbox(label="Min", interactive=False) total_adpe_max = gr.Textbox(label="Max", interactive=False) total_adpe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("🔋 Énergie primaire totale (PE)", open=False): with gr.Row(): total_pe_min = gr.Number(label="Min", precision=6, interactive=False) total_pe_max = gr.Number(label="Max", precision=6, interactive=False) total_pe_unit = gr.Textbox(label="Unité", interactive=False) with gr.Accordion("💻 Impact d'usage total", open=False): gr.Markdown("**Énergie**") with gr.Row(): total_usage_energy_min = gr.Number(label="Min", precision=6, interactive=False) total_usage_energy_max = gr.Number(label="Max", precision=6, interactive=False) gr.Markdown("**GWP**") with gr.Row(): total_usage_gwp_min = gr.Number(label="Min", precision=6, interactive=False) total_usage_gwp_max = gr.Number(label="Max", precision=6, interactive=False) with gr.Accordion("🏭 Impact incorporé total", open=False): total_embodied_gwp = gr.Textbox(label="GWP", interactive=False) total_embodied_gwp_unit = gr.Textbox(label="Unité GWP", interactive=False) total_embodied_adpe = gr.Textbox(label="ADPe", interactive=False) total_embodied_adpe_unit = gr.Textbox(label="Unité ADPe", interactive=False) total_embodied_pe = gr.Textbox(label="PE", interactive=False) total_embodied_pe_unit = gr.Textbox(label="Unité PE", interactive=False) def handle_message(message, history, model): if not message.strip(): return history, "", *([None]*68) # Updated for new output count with delta values result = get_response(message, history, model) return result[0], "", *result[1:] def clear_chat(): """Clear the chat and reset cumulative impacts""" cumulative_impacts.reset() return [], *([None]*68) # Clear chatbot and all impact displays # Connect both prompt submit and button click for trigger in [prompt.submit, send_btn.click]: trigger( handle_message, inputs=[prompt, chatbot, model], outputs=[chatbot, prompt, # Current generation impacts (raw API data) energy_min, energy_max, energy_unit, gwp_min, gwp_max, gwp_unit, adpe_min, adpe_max, adpe_unit, pe_min, pe_max, pe_unit, usage_energy_min, usage_energy_max, usage_gwp_min, usage_gwp_max, embodied_gwp, embodied_gwp_unit, embodied_adpe, embodied_adpe_unit, embodied_pe, embodied_pe_unit, # Generation delta (calculated difference) delta_energy_min, delta_energy_max, delta_energy_unit, delta_gwp_min, delta_gwp_max, delta_gwp_unit, delta_adpe_min, delta_adpe_max, delta_adpe_unit, delta_pe_min, delta_pe_max, delta_pe_unit, delta_usage_energy_min, delta_usage_energy_max, delta_usage_gwp_min, delta_usage_gwp_max, delta_embodied_gwp, delta_embodied_gwp_unit, delta_embodied_adpe, delta_embodied_adpe_unit, delta_embodied_pe, delta_embodied_pe_unit, # Cumulative impacts total_energy_min, total_energy_max, total_energy_unit, total_gwp_min, total_gwp_max, total_gwp_unit, total_adpe_min, total_adpe_max, total_adpe_unit, total_pe_min, total_pe_max, total_pe_unit, total_usage_energy_min, total_usage_energy_max, total_usage_gwp_min, total_usage_gwp_max, total_embodied_gwp, total_embodied_gwp_unit, total_embodied_adpe, total_embodied_adpe_unit, total_embodied_pe, total_embodied_pe_unit, generation_counter] ) # Connect clear button chatbot.clear( clear_chat, outputs=[chatbot, # Current generation impacts (raw API data) energy_min, energy_max, energy_unit, gwp_min, gwp_max, gwp_unit, adpe_min, adpe_max, adpe_unit, pe_min, pe_max, pe_unit, usage_energy_min, usage_energy_max, usage_gwp_min, usage_gwp_max, embodied_gwp, embodied_gwp_unit, embodied_adpe, embodied_adpe_unit, embodied_pe, embodied_pe_unit, # Generation delta (calculated difference) delta_energy_min, delta_energy_max, delta_energy_unit, delta_gwp_min, delta_gwp_max, delta_gwp_unit, delta_adpe_min, delta_adpe_max, delta_adpe_unit, delta_pe_min, delta_pe_max, delta_pe_unit, delta_usage_energy_min, delta_usage_energy_max, delta_usage_gwp_min, delta_usage_gwp_max, delta_embodied_gwp, delta_embodied_gwp_unit, delta_embodied_adpe, delta_embodied_adpe_unit, delta_embodied_pe, delta_embodied_pe_unit, # Cumulative impacts total_energy_min, total_energy_max, total_energy_unit, total_gwp_min, total_gwp_max, total_gwp_unit, total_adpe_min, total_adpe_max, total_adpe_unit, total_pe_min, total_pe_max, total_pe_unit, total_usage_energy_min, total_usage_energy_max, total_usage_gwp_min, total_usage_gwp_max, total_embodied_gwp, total_embodied_gwp_unit, total_embodied_adpe, total_embodied_adpe_unit, total_embodied_pe, total_embodied_pe_unit, generation_counter] ) demo.launch()