File size: 14,538 Bytes
f6a4838
9941726
 
 
 
 
 
f6a4838
9941726
 
9f063ed
5118ee2
 
 
 
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
 
9f063ed
5118ee2
 
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
 
9f063ed
5118ee2
 
 
 
 
 
 
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
 
 
 
 
 
5118ee2
9941726
 
 
 
 
9f063ed
5118ee2
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
d6240cf
9f063ed
5118ee2
 
 
 
 
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6240cf
 
 
 
 
 
 
 
9941726
 
 
 
d6240cf
9941726
 
 
 
 
 
 
 
 
 
a12de67
9941726
 
 
 
 
 
 
 
 
 
 
 
9f063ed
5118ee2
 
 
 
 
 
 
9f063ed
9941726
 
 
 
 
 
 
 
 
 
 
9f063ed
5118ee2
 
 
 
 
 
 
 
 
 
9f063ed
9941726
 
 
a12de67
9941726
 
 
 
 
d6240cf
9f063ed
5118ee2
 
 
 
 
 
 
 
 
 
 
 
9f063ed
9941726
a12de67
9941726
 
 
 
 
 
 
d6240cf
5118ee2
9941726
 
 
 
d6240cf
5118ee2
 
 
 
 
 
 
 
 
 
 
 
 
d6240cf
 
 
 
 
 
 
 
 
 
 
 
 
9f063ed
9941726
 
5118ee2
 
 
d6240cf
 
 
 
 
 
 
 
 
 
 
5118ee2
d6240cf
5118ee2
9941726
5118ee2
 
d6240cf
5118ee2
d6240cf
5118ee2
d6240cf
9941726
d6240cf
f6a4838
5118ee2
 
 
 
 
 
 
d6240cf
 
5118ee2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
import gradio as gr
import requests
import base64
import json
import os
from typing import Dict, Any
from pydub import AudioSegment

#---------------------------------------------------------------------------------------------------------------------------------
def read_audio_file(file_path: str) -> bytes:
    """
    Reads an audio file from the specified file path.

    Args:
        file_path (str): The path to the audio file.

    Returns:
        bytes: The content of the audio file.

    Raises:
        IOError: If there is an error reading the audio file.
    """
    try:
        with open(file_path, "rb") as audio_file:
            return audio_file.read()
    except IOError as e:
        raise IOError(f"Erreur de lecture du fichier audio : {e}")
#---------------------------------------------------------------------------------------------------------------------------------
def prepare_config(diarization: bool = False, word_time_offsets: bool = False) -> Dict[str, Any]:
    """
    Prepares the configuration for the audio transcription.

    Args:
        diarization (bool): Whether to add speaker diarization to the configuration.
        word_time_offsets (bool): Whether to enable word time offsets in the configuration.

    Returns:
        Dict[str, Any]: The prepared configuration.
    """
    return {
        "add_speaker_diarization_to_config": diarization,
        "enable_word_time_offsets": word_time_offsets,
        "add_word_boosting_to_config": None,
    }
#---------------------------------------------------------------------------------------------------------------------------------
def transcribe_audio(url: str, api_key: str, audio_content: bytes, config: Dict[str, Any]) -> Dict[str, Any]:
    """
    Sends a request to the API to transcribe the audio content.

    Args:
        url (str): The API endpoint URL.
        api_key (str): The API key for authentication.
        audio_content (bytes): The audio content to be transcribed.
        config (Dict[str, Any]): The configuration for the transcription.

    Returns:
        Dict[str, Any]: The transcription result.

    Raises:
        Exception: If there is an error during the API request.
    """
    headers = {"X-Ovh-Application": api_key}
    
    config_json = json.dumps(config)
    
    files = {
        'audio': ('audio.wav', audio_content, 'audio/wav'),
        'config': ('config.json', config_json, 'application/json')
    }
    
    try:
        response = requests.post(url, files=files, headers=headers)
        response.raise_for_status()  # Raise an exception for bad status codes
        return response.json()
    except requests.exceptions.RequestException as e:
        raise Exception(f"Erreur lors de la requête à l'API : {e}")
#---------------------------------------------------------------------------------------------------------------------------------
def process_transcription(transcription: Dict[str, Any]) -> str:
    """
    Processes the transcription result and returns a concatenated transcript.

    Args:
        transcription (Dict[str, Any]): The transcription result.

    Returns:
        str: The concatenated transcript.
    """
    full_transcript = ""
    for sentence in transcription:
        for alternative in sentence["alternatives"]:
            full_transcript += alternative["transcript"] + " "
    return full_transcript.strip()
#---------------------------------------------------------------------------------------------------------------------------------
def summary(content: str, context: str, choice: str) -> str:
    """
    Generates a summary for the given content using the specified language model.

    Args:
        content (str): The content to be summarized.
        choice (str): The language model choice.

    Returns:
        str: The generated summary.

    Raises:
        Exception: If there is an error during the API request.
    """
    if choice == "Llama-3-70B-Instruct":
        url = "https://llama-3-70b-instruct.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Meta-Llama-3-70B-Instruct"
    elif choice == "Llama-3-8B-Instruct":
        url = "https://Llama-3-8B-Instruct.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Meta-Llama-3-8B-Instruct"
    elif choice == "Mistral-7B-Instruct":
        url = "https://Mistral-7B-Instruct-v02.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Mistral-7B-Instruct-v0.2"
    elif choice == "Mixtral-8x22b-Instruct":
        url = "https://Mixtral-8x22B-Instruct-v01.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Mixtral-8x22B-Instruct-v0.1"
    elif choice == "Mixtral-8x7b-instruct":
        url = "https://mixtral-8x7b-instruct-v01.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Mixtral-8x7B-Instruct-v0.1"
    else:
        url = "https://llama-3-70b-instruct.endpoints.kepler.ai.cloud.ovh.net/api/openai_compat/v1/chat/completions"
        model="Meta-Llama-3-70B-Instruct"
    
    prompt = f"""
                Vous êtes mon assistant et votre seule mission est de me fournir un résumé du texte situé entre <>.
                Le texte supplémentaire présent entre ## (s'il n'est pas vide) fournit des éléments de contexte à ce texte.
                Veuillez fournir votre réponse en français uniquement, avec une longueur maximale de 5 phrases.
                Texte : <{content}>
                Contexte : #{context}#
            """
    
    payload = {
        "max_tokens": 512,
        "messages": [
            {
                "content":  prompt,
                "name": "User",
                "role": "user"
            }
        ],
        "model": model,
        "temperature": 0,
    }
    
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {os.getenv('OVH_API_KEY')}",
    }
    
    response = requests.post(url, json=payload, headers=headers)
    if response.status_code == 200:
        response_data = response.json()
        choices = response_data["choices"]
        for choice in choices:
            return choice["message"]["content"]
    else:
        return f"Error: {response.status_code}"
#---------------------------------------------------------------------------------------------------------------------------------
def choose_language(choice: str) -> str:
    """
    Returns the appropriate API URL based on the selected language.

    Args:
        choice (str): The language choice.

    Returns:
        str: The API URL for the selected language.
    """
    languages = {
        "fr": "https://nvr-asr-fr-fr.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize",
        "en": "https://nvr-asr-en-gb.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize",
        "en/us": "https://nvr-asr-en-us.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize",
        "de": "https://nvr-asr-de-de.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize",
        "es": "https://nvr-asr-es-es.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize",
        "it": "https://nvr-asr-it-it.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize"
    }
    return languages.get(choice, "https://nvr-asr-fr-fr.endpoints.kepler.ai.cloud.ovh.net/api/v1/asr/recognize")
#---------------------------------------------------------------------------------------------------------------------------------
def convert_audio_to_16000hz(input_file: str) -> str:
    """
    Converts the given audio file to 16000 Hz and returns the path to the converted file.

    Args:
        input_file (str): The path to the input audio file.

    Returns:
        str: The path to the converted audio file.

    Raises:
        Exception: If there is an error during the conversion.
    """
    try:
        audio = AudioSegment.from_file(input_file)
        audio = audio.set_channels(1).set_frame_rate(16000)
        output_file = "processed_audio.wav"
        audio.export(output_file, format="wav")
        return output_file
    except Exception as e:
        raise Exception(f"Erreur lors de la conversion du fichier audio : {e}")
#---------------------------------------------------------------------------------------------------------------------------------
def transcribe_and_summarize(audio_file, language_choice, llm_choice, context):
    """
    Transcribes the audio file and summarizes the transcription using the specified language model.

    Args:
        audio_file: The path to the audio file.
        language_choice: The selected language for transcription.
        llm_choice: The selected language model for summarization.

    Returns:
        tuple: The transcription and the summary.

    Raises:
        Exception: If there is an error during the process.
    """
    try:
        api_key = os.environ.get("OVH_API_KEY", "")  # Utiliser une variable d'environnement pour la clé API
        processed_audio = convert_audio_to_16000hz(audio_file)
        audio_content = read_audio_file(processed_audio)
        url = choose_language(language_choice)        
        config = prepare_config()
        transcription = transcribe_audio(url, api_key, audio_content, config)
        result = process_transcription(transcription)
        
        summary_result = summary(content=result, context=context, choice=llm_choice)
        return result, summary_result[:-10]
    except Exception as e:
        return f"Une erreur est survenue : {e}", ""
#---------------------------------------------------------------------------------------------------------------------------------
# Gradio Interface
def process_input(choice, audio_file=None, text_input=None, lang_choice=None, llm_choice=None, context_input=None):
    """
    Processes the input based on the user's choice (Audio or Text).

    Args:
        choice: The type of input (Audio or Text).
        audio_file: The audio file input.
        text_input: The text input.
        lang_choice: The language choice for audio transcription.
        llm_choice: The language model choice for summarization.

    Returns:
        tuple: The transcription (if audio) and the summary.
    """
    max_chars = 200
    if len(context_input) > max_chars:
        return "", "Le contexte ne peut pas excéder 200 caractères."
    else :
        if choice == "Audio":
            if audio_file is not None:
                return transcribe_and_summarize(audio_file, lang_choice, llm_choice, context=context_input,)
            return "Veuillez fournir une piste audio", ""
        elif choice == "Texte":
            summary_result = summary(content=text_input, context=context_input, choice=llm_choice)
            return "", summary_result[:-10]
        else:
            return "Choix invalide", ""
#---------------------------------------------------------------------------------------------------------------------------------
language_choices = ["fr", "en", "en/us", "de", "es", "it"]
llm_choices = ["Llama-3-70B-Instruct", "Llama-3-8B-Instruct", "Mistral-7B-Instruct", "Mixtral-8x22b-Instruct", "Mixtral-8x7b-instruct"]
#---------------------------------------------------------------------------------------------------------------------------------
with gr.Blocks() as iface:
    choice_input = gr.Radio(choices=["Audio", "Texte"], label="Type d'entrée")
    
    audio_input = gr.Audio(sources=["microphone", "upload"], type="filepath", visible=False, label="Source audio")
    
    text_input = gr.Textbox(lines=5, placeholder="Entrez le texte ici...", visible=False, label='Source texte')
    
    lang_dropdown = gr.Dropdown(value=language_choices[0], choices=language_choices, label="Choisissez la langue de l'audio", visible=False)
    
    llm_dropdown = gr.Dropdown(value=llm_choices[0], choices=llm_choices, label="Choisissez le LLM pour effectuer le résumé", visible=False)
    
    context_input = gr.Textbox(lines=8, placeholder="Entrez le contexte ici (optionnel).\n\nExemples :\nIl s'agit d'un appel téléphonique professionnel entre...\nIl s'agit d'une émission de radio qui parle de...\nCe texte scientifique parle de...\n\nTaille du contexte limitée à 200 caractères", visible=False, label="Contexte du contenu")
    
    transcription_output = gr.Textbox(label="Transcription", visible=False)
    
    summary_output = gr.Textbox(label="Résumé", visible=False)

    def update_visibility(choice):
        if choice == "Audio":
            return gr.update(value=None, visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(visible=True), gr.update(value="", visible=True)
        elif choice == "Texte":
            return gr.update(visible=False), gr.update(value="", visible=True), gr.update(visible=False), gr.update(visible=True), gr.update(visible=True), gr.update(visible=False), gr.update(value="", visible=True)
        else:
            return gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)

    choice_input.change(fn=update_visibility, inputs=choice_input, outputs=[audio_input, text_input, lang_dropdown, llm_dropdown, context_input, transcription_output, summary_output])

    gr.Interface(
        fn=process_input,
        inputs=[
            choice_input,
            audio_input,
            text_input,
            lang_dropdown,
            llm_dropdown,
            context_input
        ],
        outputs=[
            transcription_output,
            summary_output
        ],
        title="""
            Créer un résumé à partir d'un texte ou d'un enregistrement audio<br>
            Utilisation des <a href="https://endpoints.ai.cloud.ovh.net/" target="_blank">AI Endpoints d'OVHcloud</a><br><br>
            Codé par <a href="https://emmanuelmancuso.com/" target="_blank">Emmanuel Mancuso</a>👋🏼
            """,
        description="""
                    Téléchargez un fichier audio 🔊 ou entrez un texte 📃<br>
                    choisissez la langue pour l'audio <br>
                    choisissez le modèle de LLM pour obtenir le résumé 🚀
                    """,
        allow_flagging='never'
    )
#---------------------------------------------------------------------------------------------------------------------------------
iface.launch()