├── .gitignore ├── README.md ├── audio └── audio_chat.py ├── functions ├── conv_history.py ├── guide.py ├── html_guide │ ├── btn_copy.js │ ├── guide.css │ └── guide.html ├── start_chatbot.py └── start_talk.py ├── main.py ├── requirements.txt ├── ressources ├── agentis_logo.png ├── guide_button.png ├── interface_talk_settings.png ├── list_example_voices.png └── web_chatbot.png ├── start-app.bat └── web ├── static └── agentis_favicon.ico └── web_chat.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | .env 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Agentis 2 | ![Agentis Logo](ressources/agentis_logo.png) 3 | 4 | **Agentis** is an application with an interface to facilitate the installation of Ollama to download your **AI models locally** and also has a guide of the commands to execute. 5 | 6 | Also, **Agentis** has a **dynamic web interface** with several themes and allows the user to chat with the model of their choice locally on a modern web interface, the text conversation can be saved in a CSV or HTML file. 7 | 8 | Moreover, **Agentis** use several **synthetic voices** that you allow to talk with your local models. 9 | 10 | To launch the code in python please be careful to have python install on your machine as well as all the required packages that you can find bellow. 11 | 12 | 13 | ## Run Agentis 14 | 15 | You can click on the **'start-app.bat'** and the application will **launch and make the installation automatically**. 16 | 17 | If you want to run the code from your code environment, do this : 18 | 19 | => You need to install **Python 3.11** 20 | 21 | 1-/ Clone this repository ```git clone https://github.com/nixiz0/Agentis.git``` 22 | 23 | 2-/ Create your environment ```python -m venv .env``` 24 | 25 | 3-/ Download required libraries ```pip install -r requirements.txt``` 26 | 27 | 4- Run the main.py ```python main.py``` 28 | 29 | Once the main.py is launched please follow the **installation guide** to have Ollama and launch your server as well as install your models. 30 | 31 | ![Installation Guide Button](ressources/guide_button.png) 32 | 33 | 34 | ## ChatBot Info 35 | 36 | As mentioned, you can chat with your models locally (with text) once you have launched the ollama server in parallel, you have the possibility to download in CSV and HTML your conversation, the files will be downloaded in your download directory from your computer. 37 | 38 | ![Web Chatbot Illustration](ressources/web_chatbot.png) 39 | 40 | 41 | ## Talking with your Models 42 | 43 | ![Interface Talk Settings](ressources/interface_talk_settings.png) 44 | 45 | To talk to your model you must : 46 | 1. Put the model that you installed and launched in the first input. 47 | 48 | 2. Select a language, this is important because if you select the wrong language the voice recognition will not be adapted and will therefore not understand what you are saying. 49 | 50 | 3. Select from the list of all your microphones the one you want to use. 51 | 52 | 4. Select a voice from the list you have, obviously please select the synthetic voice which is adapted to your language (normally you will find next to each voice the language that the voice use). 53 | 54 | And that's it, please remember to have launched your ollama server in parallel and to have installed the local models that you want to use for this to work. 55 | 56 | **Vocal commands that you can use :** 57 | - If you said these words in the same order followed by the video you want to search for **'recherche sur youtube', 'find on youtube', 'find in youtube'**, it will launch youtube and search the video that you asked. 58 | 59 | - If you said these words in the same order **'quelle heure est-il', 'l\'heure actuelle', 'what time is it'**, it will return you the current time. 60 | 61 | - If you said these words in the same order **'date actuelle', 'date d\'aujourd\'hui', 'current date', 'today\'s date', 'date of today'**, it will return you the current date. 62 | 63 | - If you said these words in the same order **'sauvegarde notre discussion', 'sauvegarde notre conversation', 'sauvegarde la discussion', 'sauvegarde la conversation', 'save our discussion', 'save our conversation', 'save the discussion', 'save the conversation'**, it will saved all your discussion with the model on a CSV file (in your download folder). 64 | 65 | - If you said these words in the same order **'stoppe notre discussion', 'stoppe notre conversation', 'stoppe la discussion', 'stoppe la conversation', 'stop our discussion', 'stop our conversation', 'stop the discussion', 'stop the conversation'**, it will stop the conversation. 66 | 67 | 68 | ## To have more Synthetic Voices Available (on Windows) 69 | 70 | ![List Synthetic Voices](ressources/list_example_voices.png) 71 | 72 | If you want to have more synthetic voices available, on Windows you have to go to the narrator settings and you can download the voices you want. 73 | 74 | If this doesn't work and doesn't recognize the voices you have installed on the narrator settings, follow this steps : 75 | 1. Open the **Registry Editor** by pressing the **“Windows” and “R”** keys simultaneously, then type **“regedit”** and press Enter. 76 | 77 | 2. Navigate to the registry key : **HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens**. 78 | 79 | 3. Export this key to a **REG file** (with a right click on the file). 80 | 81 | 4. Open this file with a text editor and replace all occurrences of **HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech_OneCore\Voices\Tokens** 82 | with **HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\SPEECH\Voices\Tokens**. 83 | 84 | 5. Save the modified file and double-click it to import the changes to the registry. 85 | 86 | 87 | ## Tech Used 88 | 89 | **AI-Models:** Ollama (version 0.1.20 minimum) 90 | 91 | **Interface:** Tkinter 92 | 93 | **Talk with AI:** pyaudio (detect audio devices) / pyttsx3 (text-to-speech conversion) / speech_recognition (recognize voice and translate to text) 94 | 95 | **Computer Commands:** pywhatkit (search on youtube videos) 96 | 97 | 98 | ## Author 99 | 100 | - [@nixiz0](https://github.com/nixiz0) -------------------------------------------------------------------------------- /audio/audio_chat.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import pyttsx3 3 | import speech_recognition as sr 4 | import pywhatkit 5 | import requests 6 | import json 7 | import os 8 | import datetime 9 | 10 | 11 | def start_talk_chatbot(model, language="en-EN", mic_index=0, voice_id='HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MSTTS_V110_enGB_HazelM'): 12 | url = "http://localhost:11434/api/chat" 13 | headers = {'Content-Type': "application/json",} 14 | conversation_history = [] 15 | 16 | # Initialize the text-to-speech engine 17 | engine = pyttsx3.init() 18 | 19 | # Set the selected voice 20 | engine.setProperty('voice', voice_id) 21 | 22 | # Initialize the voice recognizer 23 | recognizer = sr.Recognizer() 24 | 25 | def beforeSay(response): 26 | return response 27 | 28 | def say(response): 29 | if len(response) == 0: 30 | return 31 | engine.say(beforeSay(response)) 32 | engine.runAndWait() 33 | 34 | def generate_response(prompt, chat_history): 35 | if len(prompt) == 0: 36 | return "", chat_history 37 | 38 | full_prompt = [] 39 | for i in chat_history: 40 | full_prompt.append({ 41 | "role": "user", 42 | "content": i[0] 43 | }) 44 | full_prompt.append({ 45 | "role": "assistant", 46 | "content": i[1] 47 | }) 48 | full_prompt.append({ 49 | "role": "user", 50 | "content": prompt 51 | }) 52 | 53 | data = { 54 | "model": model, 55 | "stream": True, 56 | "messages": full_prompt, 57 | } 58 | 59 | response = requests.post(url, headers=headers, data=json.dumps(data), stream=True) 60 | 61 | if response.status_code == 200: 62 | print('\nAssistant:', end='') 63 | all_response = '' 64 | this_response = '' 65 | for line in response.iter_lines(): 66 | if line: 67 | jsonData = json.loads(line) 68 | this_response += jsonData["message"]['content'] 69 | if '.' in this_response or '?' in this_response or '!' in this_response: 70 | print(f'{this_response}', end='') 71 | say(this_response) 72 | all_response += this_response 73 | this_response = '' 74 | if len(this_response) > 0: 75 | print(f'{this_response}', end='') 76 | say(this_response) 77 | all_response += this_response 78 | this_response = '' 79 | chat_history.append((prompt, all_response)) 80 | 81 | return "", chat_history 82 | else: 83 | return "Error: Unable to fetch response", chat_history 84 | 85 | def save_conversation(conversation_history): 86 | filename = f"conversation_history.csv" 87 | with open(os.path.join(os.path.expanduser('~'), 'Downloads', filename), 'w', newline='', encoding='utf-8') as file: 88 | writer = csv.writer(file) 89 | writer.writerow(["User", "Assistant"]) 90 | for chat in conversation_history: 91 | writer.writerow([chat[0], chat[1]]) 92 | 93 | while True: 94 | with sr.Microphone(device_index=mic_index) as source: 95 | print("\nListening...") 96 | audio = recognizer.listen(source) 97 | 98 | try: 99 | # Recognize user voice 100 | user_input = recognizer.recognize_google(audio, language=language) 101 | print("\nUser: " + user_input) 102 | 103 | # Check if the user wants to search in YouTube a video 104 | detect_youtube_keywords = ['recherche sur youtube', 'find on youtube', 'find in youtube'] 105 | if any(keyword in user_input.lower() for keyword in detect_youtube_keywords): 106 | ytb_command = user_input.replace('Open YouTube and find', '') 107 | pywhatkit.playonyt(ytb_command) 108 | continue 109 | 110 | # Check if the user wants to check the time 111 | detect_time_keywords = ['quelle heure est-il', 'l\'heure actuelle', 'what time is it'] 112 | if any(keyword in user_input.lower() for keyword in detect_time_keywords): 113 | engine.say(datetime.datetime.now().strftime('%H:%M:%S')) 114 | engine.runAndWait() 115 | continue 116 | 117 | # Check if the user wants to check the date 118 | detect_datetime_keywords = ['date actuelle', 'date d\'aujourd\'hui', 119 | 'current date', 'today\'s date', 'date of today' 120 | ] 121 | if any(keyword in user_input.lower() for keyword in detect_datetime_keywords): 122 | current_datetime = datetime.datetime.now() 123 | formatted_datetime = current_datetime.strftime('%A %d %B %Y - %H:%M') 124 | engine.say(formatted_datetime) 125 | engine.runAndWait() 126 | continue 127 | 128 | # Check if the user wants to save the conversation 129 | detect_save_keyords = ['sauvegarde notre discussion', 'sauvegarde notre conversation', 'sauvegarde la discussion', 'sauvegarde la conversation', 130 | 'save our discussion', 'save our conversation', 'save the discussion', 'save the conversation', 131 | ] 132 | if any(keyword in user_input.lower() for keyword in detect_save_keyords): 133 | save_conversation(conversation_history) 134 | print("Conversation saved.") 135 | continue 136 | 137 | # Check if the user wants to stop the conversation 138 | detect_stop_keyords = ['stoppe notre discussion', 'stoppe notre conversation', 'stoppe la discussion', 'stoppe la conversation', 139 | 'stop our discussion', 'stop our conversation', 'stop the discussion', 'stop the conversation', 140 | ] 141 | if any(keyword in user_input.lower() for keyword in detect_stop_keyords): 142 | engine.say("Okay Bye") 143 | engine.runAndWait() 144 | print("Stopping the conversation.") 145 | break 146 | 147 | # Generate a response 148 | user_input_str = str(user_input) 149 | _, chat_history = generate_response(user_input_str, conversation_history) 150 | 151 | except sr.UnknownValueError: 152 | print("Google Speech Recognition could not understand audio") 153 | except sr.RequestError as e: 154 | print("Could not request results from Google Speech Recognition service; {0}".format(e)) -------------------------------------------------------------------------------- /functions/conv_history.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import subprocess 4 | 5 | 6 | def conversation_history(): 7 | if sys.platform.startswith('linux') or sys.platform.startswith('darwin'): 8 | download_folder = os.path.expanduser("~") + "/Downloads" 9 | if os.path.isdir(download_folder): 10 | if sys.platform.startswith('linux'): 11 | # For Linux 12 | subprocess.Popen(['xdg-open', download_folder]) 13 | elif sys.platform.startswith('darwin'): 14 | # For MacOS 15 | subprocess.Popen(['open', download_folder]) 16 | else: 17 | print("Download folder not found.") 18 | elif sys.platform.startswith('win'): 19 | # For Windows 20 | subprocess.Popen(['explorer', os.path.join(os.path.expanduser("~"), "Downloads")]) 21 | else: 22 | print("Unsupported operating system.") -------------------------------------------------------------------------------- /functions/guide.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import requests 4 | import webbrowser 5 | import subprocess 6 | 7 | 8 | def open_html_file(file_path): 9 | webbrowser.open(f"file://{os.path.realpath(file_path)}") 10 | 11 | def installation_guide(): 12 | html_file_path = "functions/html_guide/guide.html" 13 | try: 14 | # Checks if WSL is installed 15 | wsl_install = subprocess.run(["wsl", "-l"], capture_output=True, text=True) 16 | if wsl_install.returncode != 0: 17 | # Download and install WSL if not already done 18 | subprocess.run(["wsl", "--install"], check=True) 19 | subprocess.run(["wsl", "sudo", "apt", "update", "&&", "sudo", "apt", "upgrade"], check=True) 20 | open_html_file(html_file_path) 21 | else: 22 | print("WSL is already installed.") 23 | open_html_file(html_file_path) 24 | 25 | except subprocess.CalledProcessError as e: 26 | print(f"An error has occurred: {e}") 27 | 28 | -------------------------------------------------------------------------------- /functions/html_guide/btn_copy.js: -------------------------------------------------------------------------------- 1 | // Sélectionner tous les éléments avec la classe "command" 2 | const commandElements = document.querySelectorAll('.command'); 3 | 4 | // Pour chaque élément, ajouter un bouton de copie 5 | commandElements.forEach(element => { 6 | const copyButton = document.createElement('button'); 7 | copyButton.innerHTML = '+'; 8 | copyButton.classList.add('copy-button'); 9 | 10 | // Créer un gestionnaire d'événements pour le clic sur le bouton de copie 11 | copyButton.addEventListener('click', (event) => { 12 | const textToCopy = element.textContent.trim(); 13 | 14 | // Créer un texte temporaire pour copier le texte sans inclure le bouton 15 | const tempElem = document.createElement('textarea'); 16 | tempElem.value = textToCopy; 17 | document.body.appendChild(tempElem); 18 | tempElem.select(); 19 | document.execCommand('copy'); 20 | document.body.removeChild(tempElem); 21 | }); 22 | 23 | // Ajouter le bouton de copie à côté de l'élément 24 | element.parentNode.insertBefore(copyButton, element.nextSibling); 25 | }); 26 | -------------------------------------------------------------------------------- /functions/html_guide/guide.css: -------------------------------------------------------------------------------- 1 | ::-webkit-scrollbar { 2 | width: 14px; 3 | } 4 | 5 | ::-webkit-scrollbar-track { 6 | background-color: #4c4c4c; 7 | } 8 | 9 | ::-webkit-scrollbar-thumb { 10 | background-color: #292929; 11 | border-radius: 6px; 12 | border: 3px solid #ffffff; 13 | } 14 | 15 | html, body { 16 | margin: 0; 17 | padding: 0; 18 | list-style: none; 19 | font-family: 'Times New Roman', Times, serif; 20 | } 21 | 22 | body { 23 | background-color: #384454; 24 | color: white; 25 | text-shadow: 0px 0px 4px #000000; 26 | } 27 | 28 | ul, ol { 29 | list-style: none; 30 | } 31 | 32 | a { 33 | text-decoration: none; 34 | text-shadow: 0px 0px 4px #000000; 35 | color: cyan; 36 | } 37 | 38 | .main { 39 | text-align: center; 40 | font-size: 3vh; 41 | } 42 | 43 | .agentis_logo { 44 | margin-top: 1em; 45 | } 46 | 47 | .agentis_logo img { 48 | width: 10vh; 49 | height: auto; 50 | } 51 | 52 | h1 { 53 | color: cyan; 54 | text-shadow: 0px 0px 4px #000000; 55 | } 56 | 57 | h3 { 58 | text-decoration: underline; 59 | } 60 | 61 | .shell_explain { 62 | 63 | } 64 | 65 | .command { 66 | color: #1ec91e; 67 | position: relative; 68 | } 69 | 70 | .attention { 71 | color: red; 72 | font-size: 1.45em; 73 | } 74 | 75 | .basic_title { 76 | color: #ff8400; 77 | font-size: 1.45em; 78 | } 79 | 80 | .shell_details, .download_models { 81 | margin-top: 3em; 82 | } 83 | 84 | /* Styles pour le bouton de copie */ 85 | .copy-button { 86 | cursor: pointer; 87 | padding: 4px 7px; 88 | background-color: #767676; 89 | color: white; 90 | border: none; 91 | border-radius: 5px; 92 | margin-left: 5px; 93 | font-size: 0.6em; 94 | } 95 | 96 | .copy-button:hover { 97 | background-color: #696969; 98 | } 99 | 100 | .copy-button:active { 101 | background-color: #565656; 102 | } 103 | -------------------------------------------------------------------------------- /functions/html_guide/guide.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Agentis Guide 9 | 10 | 11 |
12 | 15 | 16 |

Installation Guide of Agentis

17 | 18 |
19 |

Open Ubuntu Shell and run this commands :

20 | 24 |

25 | Attention 26 |
27 | If you have this message : 28 |
29 | Error: listen tcp 127.0.0.1:11434: bind: address already in use 30 |

31 |

32 | Then execute this command : 33 |
34 | sudo systemctl stop ollama 35 |
36 | 37 | (Sudo will ask your user password 38 |
39 | that you set when installing WSL for Ubuntu) 40 |
41 |

42 |
43 |
44 |

Download & Use Local Models

45 |

46 | You click on Start button 47 |
48 | On the input you have to enter the model you want 49 |

50 | Attention 51 |
52 | You have to download previously the model in your machine 53 |

54 | How ? 55 |
56 | Go on this website : ollama.ai/library. 57 |
58 | Find models you want, and go in your Ubuntu shell and run : 59 |
60 | ollama pull 'name model' 61 |

62 | You can return on Agentis Menu and click on start 63 |
64 | Then put on the input the name of one model that you have downloaded 65 |

66 | If you want to see all the models that you have downloaded 67 |
68 | Run this command on your Ubuntu shell : 69 |
70 | ollama list 71 |

72 |

73 | Download History 74 |
75 | The HTML History button allows you to download your conversation 76 |
77 | with your model and stores it in your "download" folder on your computer 78 |

79 | The CSV History button allows you to download your conversation 80 |
81 | with your model and stores it in your "download" folder on your computer 82 |

83 | Also you have on the app interface in Python the 84 |
85 | button "Open History" 86 |
87 | which allows direct access to the download file where 88 |
89 | you will find your histories in html and or csv 90 |

91 |
92 |
93 |

Essential Commands Ollama

94 |

95 | ollama serve 96 |
97 | Start Ollama serve API. 98 |

99 | ollama list 100 |
101 | Show all your downloaded models. 102 |

103 | ollama pull 'name model' 104 |
105 | Download a specific model. 106 |

107 | ollama run 'name of the model' 108 |
109 | Run a specific model on the terminal. 110 |

111 | ollama rm 'name model' 112 |
113 | Delete a model. 114 |

115 |

116 | If you want to see more commands 117 |
118 | just write on your Ubuntu shell 119 |
120 | Ollama 121 |

122 |
123 |
124 | 125 | 126 | -------------------------------------------------------------------------------- /functions/start_chatbot.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk, messagebox 3 | import subprocess 4 | import webbrowser 5 | 6 | 7 | def callback(url): 8 | webbrowser.open_new(url) 9 | 10 | def start_chatbot(): 11 | def on_ok_chat_click(): 12 | selected_model = model_var.get() 13 | selected_theme = theme_var.get() 14 | if selected_model: 15 | start_web_sep_shell(selected_model, selected_theme) 16 | messagebox.showinfo("Start", f"Starting with the model : {selected_model}, Theme : {selected_theme}") 17 | dialog.destroy() 18 | else: 19 | messagebox.showwarning("No model included", "Please put a model before continuing.") 20 | 21 | def start_web_sep_shell(model, theme): 22 | command = f'python -c "from web.web_chat import start_web_ui; start_web_ui(\'{model}\', \'{theme}\')"' 23 | subprocess.Popen(command, shell=True) 24 | 25 | dialog = tk.Toplevel() 26 | dialog.geometry("400x250") 27 | dialog.minsize(350, 240) 28 | dialog.maxsize(420, 280) 29 | 30 | dialog.update_idletasks() 31 | width = dialog.winfo_width() 32 | height = dialog.winfo_height() 33 | x = (dialog.winfo_screenwidth() // 2) - (width // 2) 34 | y = (dialog.winfo_screenheight() // 2) - (height // 2) 35 | dialog.geometry(f'+{x}+{y}') 36 | 37 | font_style = ("Inter", 16) 38 | 39 | dialog.title("Build Web Agent") 40 | 41 | dialog.option_add("*Font", font_style) 42 | 43 | dialog.configure(background="#384454") 44 | dialog.tk_setPalette(background="#384454", foreground="white") 45 | 46 | themes = ["default", "base", "soft", "mono", "glass"] 47 | 48 | model_var = tk.StringVar() 49 | theme_var = tk.StringVar() 50 | 51 | model_label = ttk.Label(dialog, text="Enter the model name:", foreground="white", background="#384454") 52 | model_label.pack() 53 | model_link = tk.Label(dialog, text="List of models", fg="blue", cursor="hand2", foreground="cyan", background="#384454") 54 | model_link.pack(pady=1) 55 | model_link.bind("", lambda e: callback("https://ollama.ai/library")) 56 | 57 | model_entry = ttk.Entry(dialog, textvariable=model_var) 58 | model_entry.pack() 59 | 60 | theme_label = ttk.Label(dialog, text="Select theme:", foreground="white", background="#384454") 61 | theme_label.pack(pady=(15, 1)) 62 | theme_dropdown = ttk.Combobox(dialog, textvariable=theme_var, values=themes, state="readonly") 63 | theme_dropdown.pack() 64 | 65 | ok_button = ttk.Button(dialog, text="OK", command=on_ok_chat_click) 66 | ok_button.pack(pady=15) 67 | dialog.bind('', lambda event: on_ok_chat_click()) 68 | dialog.mainloop() -------------------------------------------------------------------------------- /functions/start_talk.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk, messagebox 3 | import subprocess 4 | import pyaudio 5 | import pyttsx3 6 | import webbrowser 7 | 8 | 9 | def callback(url): 10 | webbrowser.open_new(url) 11 | 12 | def get_voices(): 13 | engine = pyttsx3.init() 14 | voices = engine.getProperty('voices') 15 | return {voice.name: voice.id for voice in voices} 16 | 17 | def start_talk_ai(): 18 | mic_index = tk.IntVar() 19 | mic_index.set(0) 20 | voice_id = tk.StringVar() 21 | voice_id.set('') 22 | voice_dict = get_voices() 23 | def on_ok_audio_click(): 24 | selected_model = model_var.get() 25 | selected_language = language.get() 26 | if selected_model and selected_language: 27 | start_web_sep_shell(selected_model, selected_language, mic_index.get(), voice_id.get()) 28 | messagebox.showinfo("Start", f"Starting with the model : {selected_model}, Language : {selected_language}, Micro : {mic_index.get()}, Voice : {voice_id.get()}") 29 | dialog.destroy() 30 | elif not selected_model: 31 | messagebox.showwarning("No model included", "Please put a model before continuing.") 32 | elif not selected_language: 33 | messagebox.showwarning("No language included", "Please put a language before continuing.") 34 | else: 35 | messagebox.showwarning("No model and language included", "Please put a model and a language before continuing.") 36 | 37 | def start_web_sep_shell(model, language, mic_index, voice_id): 38 | command = f'python -c "from audio.audio_chat import start_talk_chatbot; start_talk_chatbot(\'{model}\', \'{language}\', {int(mic_index)}, \'{voice_id}\')"' 39 | subprocess.Popen(command, shell=True) 40 | 41 | def show_mic_list(): 42 | p = pyaudio.PyAudio() 43 | mic_list = [] 44 | for i in range(p.get_device_count()): 45 | device_info = p.get_device_info_by_index(i) 46 | if device_info['maxInputChannels'] > 0: 47 | # Check if the device is available and active 48 | if device_info['hostApi'] == p.get_default_host_api_info()['index'] and device_info['maxInputChannels'] > 0: 49 | mic_list.append(device_info['name']) 50 | p.terminate() 51 | 52 | mic_list_dialog = tk.Toplevel() 53 | mic_list_dialog.title("Microphone List") 54 | 55 | # Create a canvas inside the dialog 56 | canvas = tk.Canvas(mic_list_dialog, width=290, height=200, background='#ffffff') 57 | scrollbar = ttk.Scrollbar(mic_list_dialog, orient="vertical", command=canvas.yview) 58 | scrollable_frame = ttk.Frame(canvas) 59 | 60 | # Bind the canvas's height to the scrollable frame's height 61 | scrollable_frame.bind( 62 | "", 63 | lambda e: canvas.configure( 64 | scrollregion=canvas.bbox("all") 65 | ) 66 | ) 67 | 68 | canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") 69 | canvas.configure(yscrollcommand=scrollbar.set) 70 | 71 | for idx, mic_name in enumerate(mic_list): 72 | mic_button = tk.Button(scrollable_frame, text=mic_name, command=lambda idx=idx: select_mic(idx), font=("Inter", 12)) 73 | mic_button.pack() 74 | 75 | canvas.pack(side="left", fill="both", expand=True) 76 | scrollbar.pack(side="right", fill="y") 77 | 78 | def select_mic(index): 79 | # Selected mic_index 80 | mic_index.set(index) 81 | print(f"Selected Microphone Index: {index}") 82 | 83 | def show_voice_list(): 84 | voice_list_dialog = tk.Toplevel() 85 | voice_list_dialog.title("Voice List") 86 | 87 | # Create a canvas inside the dialog 88 | canvas = tk.Canvas(voice_list_dialog, width=360, height=360, background='#ffffff') 89 | scrollbar = ttk.Scrollbar(voice_list_dialog, orient="vertical", command=canvas.yview) 90 | scrollable_frame = ttk.Frame(canvas) 91 | 92 | # Bind the canvas's height to the scrollable frame's height 93 | scrollable_frame.bind( 94 | "", 95 | lambda e: canvas.configure( 96 | scrollregion=canvas.bbox("all") 97 | ) 98 | ) 99 | 100 | canvas.create_window((0, 0), window=scrollable_frame, anchor="nw") 101 | canvas.configure(yscrollcommand=scrollbar.set) 102 | 103 | for voice_name in voice_dict.keys(): 104 | voice_button = tk.Button(scrollable_frame, text=voice_name, command=lambda voice_name=voice_name: select_voice(voice_name), font=("Inter", 12)) 105 | voice_button.pack() 106 | 107 | canvas.pack(side="left", fill="both", expand=True) 108 | scrollbar.pack(side="right", fill="y") 109 | 110 | def select_voice(voice_name): 111 | # Selected voice_id 112 | voice_id.set(voice_dict[voice_name]) 113 | print(f"Selected Voice ID: {voice_dict[voice_name]}") 114 | 115 | dialog = tk.Toplevel() 116 | dialog.geometry("400x320") 117 | dialog.minsize(350, 310) 118 | dialog.maxsize(420, 330) 119 | 120 | dialog.update_idletasks() 121 | width = dialog.winfo_width() 122 | height = dialog.winfo_height() 123 | x = (dialog.winfo_screenwidth() // 2) - (width // 2) 124 | y = (dialog.winfo_screenheight() // 2) - (height // 2) 125 | dialog.geometry(f'+{x}+{y}') 126 | 127 | font_style = ("Inter", 15) 128 | 129 | dialog.title("Build Audio Agent") 130 | 131 | dialog.option_add("*Font", font_style) 132 | 133 | dialog.configure(background="#384454") 134 | dialog.tk_setPalette(background="#384454", foreground="white") 135 | 136 | languages = ["fr-FR", "en-EN"] 137 | 138 | model_var = tk.StringVar() 139 | language = tk.StringVar() 140 | 141 | model_label = ttk.Label(dialog, text="Enter the model name:", foreground="white", background="#384454") 142 | model_label.pack() 143 | model_link = tk.Label(dialog, text="List of models", fg="blue", cursor="hand2", foreground="cyan", background="#384454") 144 | model_link.pack(pady=5) 145 | model_link.bind("", lambda e: callback("https://ollama.ai/library")) 146 | 147 | model_entry = ttk.Entry(dialog, textvariable=model_var) 148 | model_entry.pack() 149 | 150 | languages_label = ttk.Label(dialog, text="Select Language:", foreground="white", background="#384454") 151 | languages_label.pack(pady=(15, 1)) 152 | languages_dropdown = ttk.Combobox(dialog, textvariable=language, values=languages, state="readonly") 153 | languages_dropdown.pack() 154 | 155 | mic_list_button = ttk.Button(dialog, text="Microphone List", command=show_mic_list) 156 | mic_list_button.pack(pady=5) 157 | 158 | voice_list_button = ttk.Button(dialog, text="Voice List", command=show_voice_list) 159 | voice_list_button.pack(pady=5) 160 | 161 | ok_button = ttk.Button(dialog, text="OK", command=on_ok_audio_click) 162 | ok_button.pack(pady=5) 163 | dialog.bind('', lambda event: on_ok_audio_click()) 164 | dialog.mainloop() -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | from functions.guide import installation_guide 5 | from functions.start_chatbot import start_chatbot 6 | from functions.start_talk import start_talk_ai 7 | from functions.conv_history import conversation_history 8 | 9 | 10 | # Creation of the main window 11 | root = tk.Tk() 12 | root.title("Agentis") 13 | root.configure(bg='#384454') 14 | 15 | # Creating a style for the buttons 16 | style = ttk.Style() 17 | style.configure('TButton', font=('Inter', 16), background='gray', foreground='black') 18 | 19 | # Creation of the frame for the image 20 | image_frame = tk.Frame(root, bg='#384454') 21 | image_frame.pack(pady=5) 22 | img = tk.PhotoImage(file="ressources/agentis_logo.png") 23 | 24 | image_label = tk.Label(image_frame, image=img, bg='#384454') 25 | image_label.image = img 26 | image_label.pack() 27 | 28 | # Creation of buttons 29 | start_button = ttk.Button(root, text="Installation Guide", command=installation_guide, style='TButton') 30 | start_chat = ttk.Button(root, text="Start Chatbot", command=start_chatbot, style='TButton') 31 | start_talk = ttk.Button(root, text="Start Talk", command=start_talk_ai, style='TButton') 32 | create_button = ttk.Button(root, text="Open History", command=conversation_history, style='TButton') 33 | 34 | # Applying styling to buttons 35 | start_button.pack(pady=(20, 5)) 36 | start_chat.pack(pady=5) 37 | start_talk.pack(pady=5) 38 | create_button.pack(pady=5) 39 | 40 | root.minsize(210, 300) 41 | root.maxsize(260, 340) 42 | root.geometry("250x330") 43 | 44 | # Window centering 45 | window_width = root.winfo_reqwidth() 46 | window_height = root.winfo_reqheight() 47 | position_right = int(root.winfo_screenwidth() / 2 - window_width / 2) 48 | position_down = int(root.winfo_screenheight() / 2 - window_height / 1) 49 | root.geometry("+{}+{}".format(position_right, position_down)) 50 | 51 | root.mainloop() -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/requirements.txt -------------------------------------------------------------------------------- /ressources/agentis_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/ressources/agentis_logo.png -------------------------------------------------------------------------------- /ressources/guide_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/ressources/guide_button.png -------------------------------------------------------------------------------- /ressources/interface_talk_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/ressources/interface_talk_settings.png -------------------------------------------------------------------------------- /ressources/list_example_voices.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/ressources/list_example_voices.png -------------------------------------------------------------------------------- /ressources/web_chatbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/ressources/web_chatbot.png -------------------------------------------------------------------------------- /start-app.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | IF NOT EXIST .env ( 3 | python -m venv .env 4 | cd .env\Scripts 5 | call activate.bat 6 | cd ../.. 7 | pip install -r requirements.txt 8 | ) ELSE ( 9 | cd .env\Scripts 10 | call activate.bat 11 | cd ../.. 12 | ) 13 | python.exe main.py -------------------------------------------------------------------------------- /web/static/agentis_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nixiz0/Agentis/75ec4e5ba851d3b3812b683c8379cc3f9552f8cf/web/static/agentis_favicon.ico -------------------------------------------------------------------------------- /web/web_chat.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | import requests 4 | import json 5 | import gradio as gr 6 | from datetime import datetime 7 | 8 | 9 | def start_web_ui(model, theme=None): 10 | url = "http://localhost:11434/api/generate" 11 | headers = {'Content-Type': "application/json",} 12 | conversation_history = [] 13 | 14 | def generate_response(prompt, chat_history): 15 | conversation_history.append(prompt) 16 | full_prompt = "\n".join(conversation_history) 17 | 18 | data = { 19 | "model": model, 20 | "stream": False, 21 | "prompt": full_prompt, 22 | } 23 | 24 | response = requests.post(url, headers=headers, data=json.dumps(data)) 25 | 26 | if response.status_code == 200: 27 | response_text = response.text 28 | data = json.loads(response_text) 29 | actual_response = data["response"] 30 | conversation_history.append(actual_response) 31 | chat_history.append((prompt, actual_response)) 32 | return "", chat_history 33 | else: 34 | return "Error: Unable to fetch response", chat_history 35 | 36 | def download_html(): 37 | html_content = """ 38 | 39 | Conversation History 40 | 41 |

Conversation History

42 | 43 | """ 44 | for i in range(0, len(conversation_history), 2): 45 | user = conversation_history[i] if i < len(conversation_history) else "" 46 | model = conversation_history[i + 1] if i + 1 < len(conversation_history) else "" 47 | 48 | html_content += f""" 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | """ 58 | html_content += """ 59 |
User:{user}
Model:{model}
60 | 61 | 62 | """ 63 | filename = f"conversation_history_{datetime.now().strftime('%d%m%Y_%H%M%S')}.html" 64 | with open(os.path.join(os.path.expanduser('~'), 'Downloads', filename), 'w', encoding='utf-8') as file: 65 | file.write(html_content) 66 | 67 | def download_csv(): 68 | filename = f"chat_history_{datetime.now().strftime('%d%m%Y_%H%M%S')}.csv" 69 | with open(os.path.join(os.path.expanduser('~'), 'Downloads', filename), 'w', newline='', encoding='utf-8') as file: 70 | writer = csv.writer(file) 71 | writer.writerow(["User", "Model"]) 72 | for index in range(0, len(conversation_history), 2): 73 | user = conversation_history[index] 74 | model = conversation_history[index + 1] if index + 1 < len(conversation_history) else "" 75 | writer.writerow([user, model]) 76 | 77 | if theme == "soft": 78 | theme = gr.themes.Soft() 79 | elif theme == "base": 80 | theme = gr.themes.Base() 81 | elif theme == "mono": 82 | theme = gr.themes.Monochrome() 83 | elif theme == "glass": 84 | theme = gr.themes.Glass() 85 | else: 86 | theme = None 87 | 88 | css = """ 89 | .contain {margin-bottom: 3em !important;} 90 | footer {display: none !important;} 91 | gradio-app {background-color: #212121 !important;} 92 | #component-3 {width: 30% !important; margin: 0 auto !important; font-size: 1.25em !important;} 93 | #component-4 {width: 30% !important; margin: 0 auto !important; font-size: 1.25em !important;} 94 | #component-5 {width: 30% !important; margin: 0 auto !important; font-size: 1.25em !important;} 95 | #component-6 {width: 30% !important; margin: 0 auto !important; font-size: 1.25em !important;} 96 | """ 97 | 98 | with gr.Blocks(title="Agentis", theme=theme, css=css) as agentis: 99 | chatbot = gr.Chatbot(label="Agent", height=700) 100 | msg = gr.Textbox(placeholder="User Prompt", label="Prompt") 101 | btn_submit = gr.Button(value="Submit", variant='primary') 102 | btn_submit.click(generate_response, [msg, chatbot], [msg, chatbot]) 103 | clear = gr.ClearButton([msg, chatbot], variant='stop') 104 | btn_download_html = gr.Button(value="HTML History") 105 | btn_download_html.click(download_html) 106 | btn_download_csv = gr.Button(value="CSV History") 107 | btn_download_csv.click(download_csv) 108 | 109 | msg.submit(generate_response, [msg, chatbot], [msg, chatbot]) 110 | 111 | agentis.launch(favicon_path="web/static/agentis_favicon.ico", inbrowser=True) 112 | --------------------------------------------------------------------------------