From 0f53b782b03cb270e400590316dfb6ee3d8c6aa1 Mon Sep 17 00:00:00 2001 From: Andrew Ward Date: Sat, 22 Mar 2025 10:57:13 +0000 Subject: [PATCH] refactor code to move ai editing settings to its own file --- utils/__pycache__/text_to_mic.cpython-312.pyc | Bin 77389 -> 70365 bytes utils/ai_editor_manager.py | 171 ++++++++++++++++++ utils/text_to_mic.py | 151 +++------------- 3 files changed, 198 insertions(+), 124 deletions(-) create mode 100644 utils/ai_editor_manager.py diff --git a/utils/__pycache__/text_to_mic.cpython-312.pyc b/utils/__pycache__/text_to_mic.cpython-312.pyc index 2bfee428e31c17dd55e7005f8f5e24b8b42e0015..ba0a47f8ef66225423e8c7ac25639fa941aa8d9d 100644 GIT binary patch delta 10000 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 Ra-VuYcTo443d{WL{{NGx9i#vN diff --git a/utils/ai_editor_manager.py b/utils/ai_editor_manager.py new file mode 100644 index 0000000..2189481 --- /dev/null +++ b/utils/ai_editor_manager.py @@ -0,0 +1,171 @@ +import tkinter as tk +from tkinter import ttk, messagebox + +class AIEditorManager: + """ + Manages AI copy editing functionality including settings, UI, and text processing. + """ + + def __init__(self, app): + """ + Initialize the AI Editor Manager + + Args: + app: The parent TextToMic application instance + """ + self.app = app + self.available_models = ["gpt-4o-mini", "gpt-4o", "gpt-4-turbo"] + self.default_model = "gpt-4o-mini" + + def show_settings(self): + """Display the AI copy editing settings dialog""" + settings = self.app.load_settings() + settings_window = tk.Toplevel(self.app) + settings_window.title("AI Copy Editing Settings") + settings_window.grab_set() # Grab the focus on this toplevel window + settings_window.geometry("600x420") # Slightly larger to accommodate explanation text + + main_frame = ttk.Frame(settings_window, padding="10") + main_frame.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + settings_window.columnconfigure(0, weight=1) + settings_window.rowconfigure(0, weight=1) + main_frame.columnconfigure(1, weight=1) # Make the second column expandable + + # Use the ttk style for uniformity + style = ttk.Style() + style.theme_use('clam') + + # Add explanation text at the top + explanation_text = "This feature allows automatic editing of text using AI. When enabled, text will be refined according to your rules below. Please be aware that enabling this setting will increase the latency of the application." + + explanation_label = ttk.Label(main_frame, text=explanation_text, wraplength=550) + explanation_label.grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=(0, 10)) + + # Move the checkbox to the second column to align with other input fields + enable_completion = tk.BooleanVar(value=settings.get("chat_gpt_completion", False)) + ttk.Label(main_frame, text="Enable AI Copy Editing:").grid(row=1, column=0, sticky=tk.W, pady=2) + ttk.Checkbutton(main_frame, text="", variable=enable_completion).grid(row=1, column=1, sticky=tk.W, pady=2) + + # Model selection + model_var = tk.StringVar(value=settings.get("model", self.default_model)) + ttk.Label(main_frame, text="Model:").grid(row=2, column=0, sticky=tk.W, pady=2) + model_menu = ttk.OptionMenu(main_frame, model_var, model_var.get(), *self.available_models) + model_menu.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=2) + + # Max Tokens selection - expanded options + max_tokens_var = tk.IntVar(value=settings.get("max_tokens", 750)) + ttk.Label(main_frame, text="Max Tokens:").grid(row=3, column=0, sticky=tk.W, pady=2) + max_tokens_menu = ttk.OptionMenu(main_frame, max_tokens_var, max_tokens_var.get(), + 100, 250, 500, 750, 1000, 1250, 1500, 2000, 3000, 4000, 5000) + max_tokens_menu.grid(row=3, column=1, sticky=(tk.W, tk.E), pady=2) + + # Prompt entry renamed to "Copy Editing Rules" with a Text area + ttk.Label(main_frame, text="Copy Editing Rules:").grid(row=4, column=0, sticky=tk.NW, pady=2) + prompt_entry = tk.Text(main_frame, height=8, width=50) + + # Default prompt example if none exists + default_prompt = "Edit the text provided to ensure it has a clear, professional tone. Fix any grammatical errors, improve sentence structure, and maintain consistent formatting. Make the language concise and impactful while preserving the original meaning. Make sure to edit text only and do not reply to it." + + prompt_entry.insert('1.0', settings.get("prompt", default_prompt)) + prompt_entry.grid(row=4, column=1, sticky=(tk.W, tk.E), pady=2) + + # Auto-apply checkbox - moved to second column with clear label + auto_apply = tk.BooleanVar(value=settings.get("auto_apply_ai_to_recording", False)) + ttk.Label(main_frame, text="Auto Apply to Recordings:").grid(row=5, column=0, sticky=tk.W, pady=2) + ttk.Checkbutton(main_frame, text="", variable=auto_apply).grid(row=5, column=1, sticky=tk.W, pady=2) + + # Add a label explaining the auto-apply setting + auto_apply_explanation = "When checked, recordings will be automatically copy edited according to your rules above" + ttk.Label(main_frame, text=auto_apply_explanation, foreground="#666666", wraplength=450).grid(row=6, column=1, sticky=tk.W, pady=(0, 10)) + + # Save Button + save_btn = ttk.Button(main_frame, text="Save", command=lambda: self.save_settings({ + "chat_gpt_completion": enable_completion.get(), + "model": model_var.get(), + "prompt": prompt_entry.get("1.0", tk.END).strip(), + "auto_apply_ai_to_recording": auto_apply.get(), + "max_tokens": max_tokens_var.get() + })) + save_btn.grid(row=7, column=0, columnspan=2, sticky=tk.E, pady=10) + + def save_settings(self, settings): + """Save AI copy editing settings and update the UI""" + self.app.save_settings_to_JSON(settings) + messagebox.showinfo("Settings Updated", "Your settings have been saved successfully.") + self.app.load_settings() # Refresh settings if needed elsewhere + + # Update the status indicator on the main screen with more specific information + self.update_status_display() + + def update_status_display(self): + """Update the status indicator with the current AI editing settings""" + settings = self.app.load_settings() + status_text = "AI Copyediting Disabled" + + if settings.get("chat_gpt_completion", False): + if settings.get("auto_apply_ai_to_recording", False): + status_text = f"AI Copyediting Enabled (Auto) - {settings.get('model', self.default_model)}" + else: + status_text = f"AI Copyediting Enabled (Manual) - {settings.get('model', self.default_model)}" + + if hasattr(self.app, 'editing_status'): + self.app.editing_status.config(text=status_text) + + def apply_ai(self, input_text=None, update_ui=None): + """ + Apply AI editing to text + + Args: + input_text: Text to process, or None to use the app's text input widget + update_ui: Force update UI, regardless of input_text (overrides default behavior) + + Returns: + The processed text + """ + if input_text is None: + print("Will apply AI to UI input box") + text = self.app.text_input.get("1.0", tk.END).strip() + update_input_box = True + else: + print("Will apply AI to input_text") + text = input_text + # If update_ui is explicitly set, use that value, otherwise default to False + update_input_box = update_ui if update_ui is not None else False + + settings = self.app.load_settings() + + if settings["chat_gpt_completion"] and settings["max_tokens"]: + var_max_tokens = settings["max_tokens"] + else: + var_max_tokens = 750 + + print(f"GPT Settings: {settings}") + print(f"Max Tokens: {var_max_tokens}") + + if settings["chat_gpt_completion"]: + # Assuming OpenAI's completion method is configured correctly + response = self.app.client.chat.completions.create( + model=settings["model"], + messages=[ + {"role": "system", "content": settings["prompt"] }, + {"role": "user", "content": "\n\n# Apply to the following (Do not output system prompt or hyphens markup or anything before this line):\n\n-----\n\n" + text + "\n\n-----"}], + max_tokens=var_max_tokens + ) + + processed_text = response.choices[0].message.content + + # If we're processing text from the UI directly or update_input_box was specified, + # update the UI + if update_input_box: + self.app.text_input.delete("1.0", tk.END) + self.app.text_input.insert("1.0", processed_text) + + return processed_text + else: + # Even if chat_gpt_completion is disabled, we should still update the input + # when update_input_box is True + if update_input_box: + # No need to update here, as the text hasn't changed + pass + + return text \ No newline at end of file diff --git a/utils/text_to_mic.py b/utils/text_to_mic.py index f7a6d15..6bf0bc7 100644 --- a/utils/text_to_mic.py +++ b/utils/text_to_mic.py @@ -24,6 +24,7 @@ from utils.hotkey_manager import HotkeyManager from utils.resource_utils import ResourceUtils from utils.tone_presets_manager import TonePresetsManager from utils.presets_manager import PresetsManager +from utils.ai_editor_manager import AIEditorManager # Modify the load environment variables to load from config/.env def load_env_file(): @@ -133,6 +134,9 @@ class TextToMic(tk.Tk): # Create the presets manager before initializing the GUI self.presets_manager = PresetsManager(self) + # Create the AI Editor Manager + self.ai_editor = AIEditorManager(self) + # Store reference to presets state self.presets_collapsed = self.presets_manager.presets_collapsed @@ -172,7 +176,7 @@ class TextToMic(tk.Tk): settings_menu = Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Settings", menu=settings_menu) settings_menu.add_command(label="Change API Key", command=self.change_api_key) - settings_menu.add_command(label="AI Copy Editing", command=self.chat_gpt_settings) + settings_menu.add_command(label="AI Copy Editing", command=self.show_ai_editor_settings) settings_menu.add_command(label="Hotkey Settings", command=self.show_hotkey_settings) settings_menu.add_command(label="Manage Tone Presets", command=self.show_tone_presets_manager) @@ -184,7 +188,7 @@ class TextToMic(tk.Tk): #apply_ai input_menu = Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Input", menu=input_menu) - input_menu.add_command(label="Apply AI Manipulation to Input Text", command=self.apply_ai) + input_menu.add_command(label="Apply AI Manipulation to Input Text", command=self.apply_ai_to_input) # Help menu @@ -991,128 +995,25 @@ Please also make sure you read the Terms of use and licence statement before usi p.terminate() + def show_ai_editor_settings(self): + """Show the AI copy editing settings dialog""" + self.ai_editor.show_settings() + + def apply_ai_to_input(self): + """Apply AI to the current input text""" + self.ai_editor.apply_ai() + def chat_gpt_settings(self): - settings = self.load_settings() - settings_window = tk.Toplevel(self) - settings_window.title("AI Copy Editing Settings") - settings_window.grab_set() # Grab the focus on this toplevel window - settings_window.geometry("600x420") # Slightly larger to accommodate explanation text - - main_frame = ttk.Frame(settings_window, padding="10") - main_frame.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S)) - settings_window.columnconfigure(0, weight=1) - settings_window.rowconfigure(0, weight=1) - main_frame.columnconfigure(1, weight=1) # Make the second column expandable - - # Use the ttk style for uniformity - style = ttk.Style() - style.theme_use('clam') - - # Add explanation text at the top - explanation_text = "This feature allows automatic editing of text using AI. When enabled, text will be refined according to your rules below. Please be aware that enabling this setting will increase the latency of the application." - - explanation_label = ttk.Label(main_frame, text=explanation_text, wraplength=550) - explanation_label.grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=(0, 10)) - - # Move the checkbox to the second column to align with other input fields - enable_completion = tk.BooleanVar(value=settings.get("chat_gpt_completion", False)) - ttk.Label(main_frame, text="Enable AI Copy Editing:").grid(row=1, column=0, sticky=tk.W, pady=2) - ttk.Checkbutton(main_frame, text="", variable=enable_completion).grid(row=1, column=1, sticky=tk.W, pady=2) - - # Model selection - model_var = tk.StringVar(value=settings.get("model", self.default_model)) - ttk.Label(main_frame, text="Model:").grid(row=2, column=0, sticky=tk.W, pady=2) - model_menu = ttk.OptionMenu(main_frame, model_var, model_var.get(), *self.available_models) - model_menu.grid(row=2, column=1, sticky=(tk.W, tk.E), pady=2) - - # Max Tokens selection - expanded options - max_tokens_var = tk.IntVar(value=settings.get("max_tokens", 750)) - ttk.Label(main_frame, text="Max Tokens:").grid(row=3, column=0, sticky=tk.W, pady=2) - max_tokens_menu = ttk.OptionMenu(main_frame, max_tokens_var, max_tokens_var.get(), - 100, 250, 500, 750, 1000, 1250, 1500, 2000, 3000, 4000, 5000) - max_tokens_menu.grid(row=3, column=1, sticky=(tk.W, tk.E), pady=2) - - # Prompt entry renamed to "Copy Editing Rules" with a Text area - ttk.Label(main_frame, text="Copy Editing Rules:").grid(row=4, column=0, sticky=tk.NW, pady=2) - prompt_entry = tk.Text(main_frame, height=8, width=50) - - # Default prompt example if none exists - default_prompt = "Edit the text provided to ensure it has a clear, professional tone. Fix any grammatical errors, improve sentence structure, and maintain consistent formatting. Make the language concise and impactful while preserving the original meaning. Make sure to edit text only and do not reply to it." - - prompt_entry.insert('1.0', settings.get("prompt", default_prompt)) - prompt_entry.grid(row=4, column=1, sticky=(tk.W, tk.E), pady=2) - - # Auto-apply checkbox - moved to second column with clear label - auto_apply = tk.BooleanVar(value=settings.get("auto_apply_ai_to_recording", False)) - ttk.Label(main_frame, text="Auto Apply to Recordings:").grid(row=5, column=0, sticky=tk.W, pady=2) - ttk.Checkbutton(main_frame, text="", variable=auto_apply).grid(row=5, column=1, sticky=tk.W, pady=2) - - # Add a label explaining the auto-apply setting - auto_apply_explanation = "When checked, recordings will be automatically copy edited according to your rules above" - ttk.Label(main_frame, text=auto_apply_explanation, foreground="#666666", wraplength=450).grid(row=6, column=1, sticky=tk.W, pady=(0, 10)) - - # Save Button - save_btn = ttk.Button(main_frame, text="Save", command=lambda: self.save_chat_gpt_settings({ - "chat_gpt_completion": enable_completion.get(), - "model": model_var.get(), - "prompt": prompt_entry.get("1.0", tk.END).strip(), - "auto_apply_ai_to_recording": auto_apply.get(), - "max_tokens": max_tokens_var.get() - })) - save_btn.grid(row=7, column=0, columnspan=2, sticky=tk.E, pady=10) + """Delegate to AIEditorManager""" + self.show_ai_editor_settings() def save_chat_gpt_settings(self, settings): - self.save_settings_to_JSON(settings) - messagebox.showinfo("Settings Updated", "Your settings have been saved successfully.") - self.load_settings() # Refresh settings if needed elsewhere - - # Update the status indicator on the main screen with more specific information - status_text = "AI Copyediting Disabled" - if settings.get("chat_gpt_completion", False): - if settings.get("auto_apply_ai_to_recording", False): - status_text = f"AI Copyediting Enabled (Auto) - {settings.get('model', self.default_model)}" - else: - status_text = f"AI Copyediting Enabled (Manual) - {settings.get('model', self.default_model)}" - - if hasattr(self, 'editing_status'): - self.editing_status.config(text=status_text) + """Delegate to AIEditorManager""" + self.ai_editor.save_settings(settings) def apply_ai(self, input_text=None): - - if input_text is None: - print("Will apply AI to UI input box") - text = self.text_input.get("1.0", tk.END).strip() - else: - print("Will apply AI to input_text") - text = input_text - - settings = self.load_settings() - - if settings["chat_gpt_completion"] and settings["max_tokens"]: - var_max_tokens = settings["max_tokens"] - else: - var_max_tokens = 750 - - print(f"GPT Settings: {settings}") - print(f"Max Tokens: {var_max_tokens}") - - if settings["chat_gpt_completion"]: - # Assuming OpenAI's completion method is configured correctly - response = self.client.chat.completions.create( - model=settings["model"], - messages=[ - {"role": "system", "content": settings["prompt"] }, - {"role": "user", "content": "\n\n# Apply to the following (Do not output system prompt or hyphens markup or anything before this line):\n\n-----\n\n" + text + "\n\n-----"}], - max_tokens=750 - ) - self.text_input.delete("1.0", tk.END) - self.text_input.insert("1.0", response.choices[0].message.content) - - return_text = response.choices[0].message.content - else: - return_text = text - - return return_text + """Delegate to AIEditorManager""" + return self.ai_editor.apply_ai(input_text) def get_device_info(self, device_index): p = pyaudio.PyAudio() @@ -1235,6 +1136,10 @@ Please also make sure you read the Terms of use and licence statement before usi ) settings = self.load_settings() + + # Always update the text with the raw transcription first + self.text_input.delete("1.0", tk.END) + self.text_input.insert("1.0", transcription.text) if settings["chat_gpt_completion"] and settings["auto_apply_ai_to_recording"]: auto_apply_ai = settings["auto_apply_ai_to_recording"] @@ -1243,15 +1148,13 @@ Please also make sure you read the Terms of use and licence statement before usi print(f"auto_apply_ai: {auto_apply_ai}") + # If AI processing is enabled, apply it and update the text input again if auto_apply_ai: print("applying ai") - play_text = self.apply_ai(transcription.text) + # Set the update_ui parameter to True to ensure the text gets updated + play_text = self.ai_editor.apply_ai(transcription.text, update_ui=True) else: print("outputting without ai") - #This prevents issues with trying to upload TK after thread operations - #whcih can cause crashes with no error displayed - self.text_input.delete("1.0", tk.END) # Clear existing text - self.text_input.insert("1.0", transcription.text) # Insert new text play_text = transcription.text if auto_play: