these codes functionally work but don't produce coherent outputs and have outputs that start to break down and cause generation errors - examine scripts and develop methods to better achieve goals #!/bin/bash
set -e

BUILD_DIR="$HOME/Desktop/CustomAIToolBuild"
DESKTOP_DIR="$HOME/Desktop"
APP_NAME="CustomAITool"

mkdir -p "$BUILD_DIR"
python3 -m venv "$BUILD_DIR/venv"
source "$BUILD_DIR/venv/bin/activate"

pip install --upgrade pip
pip install groq pyinstaller markdown requests tqdm

cat << 'EOF' > "$BUILD_DIR/custom_ai_tool_gui.py"
import os
import sys
import json
import threading
import time
from groq import Groq
import tkinter as tk
from tkinter import messagebox, filedialog, simpledialog, scrolledtext
from tkinter.ttk import Progressbar
import re
import requests
from tqdm import tqdm
import random

DEFAULT_MODEL = "llama-3.1-70b-versatile"
# API KEY HARDCODED (NOT RECOMMENDED)
DEFAULT_API_KEY = "<TRUFFLEHOG_REDACTED_GROQ>"
MAX_TOKENS = 4000  # Adjust according to API limitations
SUMMARIZE_AFTER_MESSAGES = 5  # Summarize after this many generations 
MAX_CONTEXT_TOKENS = 2000  # Threshold to start summarizing - lowered
DEFAULT_TEMPERATURE = 0.7 # Default temperature

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys.MEIPASS
    except Exception:
        base_path = os.path.dirname(sys.argv[0])

    return os.path.join(base_path, relative_path)

class CustomAITool(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Custom AI Tool")
        self.geometry("800x600")

        self.config_file = resource_path("config.json")
        self.prompt_file = resource_path("saved_prompt.json")
        self.create_default_files()
        self.load_config()
        self.prompt = self.load_prompt()
        self.full_story = []  # Store the entire story here
        self.summaries = []
        self.total_tokens = 0  # Track total tokens used
        self.current_generation = 0  # Track current generation
        self.target_length = 0 # Target number of generations

        self.create_widgets()

    def create_default_files(self):
        if not os.path.exists(self.config_file):
            with open(self.config_file, 'w') as f:
                json.dump({"api_key": DEFAULT_API_KEY, "model": DEFAULT_MODEL, "font_size": 12, "copy_raw": False, "temperature": DEFAULT_TEMPERATURE}, f)
        if not os.path.exists(self.prompt_file):
            with open(self.prompt_file, 'w') as f:
                json.dump({"prompt": "You are a helpful and concise AI assistant that can generate different creative text formats, like stories, articles, reports, and other written content. Avoid unnecessary introductory phrases."}, f)

    def load_config(self):
        try:
            with open(self.config_file, 'r') as f:
                config = json.load(f)
                self.api_key = config.get("api_key", DEFAULT_API_KEY)
                self.model = config.get("model", DEFAULT_MODEL)
                self.font_size = config.get("font_size", 12)
                self.copy_raw = config.get("copy_raw", False)
                self.temperature = config.get("temperature", DEFAULT_TEMPERATURE)
        except Exception as e:
            self.show_error("Error loading config", str(e))
            self.api_key, self.model, self.font_size, self.copy_raw, self.temperature = DEFAULT_API_KEY, DEFAULT_MODEL, 12, False, DEFAULT_TEMPERATURE

    def save_config(self):
        try:
            with open(self.config_file, 'w') as f:
                json.dump({"api_key": self.api_key, "model": self.model, "font_size": self.font_size, "copy_raw": self.copy_raw, "temperature": self.temperature}, f)
        except Exception as e:
            self.show_error("Error saving config", str(e))

    def show_options_dialog(self):
        dialog = OptionsDialog(self)
        self.wait_window(dialog)
        if dialog.result:
            self.api_key, self.model, self.font_size, self.copy_raw, self.temperature = dialog.result
            self.save_config()
            self.update_font_size()
            self.show_notification("Settings Updated", "Your settings have been updated successfully.")

    def update_font_size(self):
        self.conversation_display.configure(font=("TkDefaultFont", self.font_size))

    def create_widgets(self):
        toolbar = tk.Frame(self)
        toolbar.pack(side=tk.TOP, fill=tk.X)

        options_button = tk.Button(toolbar, text="Options", command=self.show_options_dialog)
        options_button.pack(side=tk.LEFT)

        new_prompt_button = tk.Button(toolbar, text="New Prompt", command=self.new_prompt)
        new_prompt_button.pack(side=tk.LEFT)

        clear_history_button = tk.Button(toolbar, text="Clear History", command=self.clear_history)
        clear_history_button.pack(side=tk.LEFT)

        export_button = tk.Button(toolbar, text="Export", command=self.export_generations)
        export_button.pack(side=tk.LEFT)

        self.conversation_display = scrolledtext.ScrolledText(self, state='disabled', wrap=tk.WORD)
        self.conversation_display.pack(fill=tk.BOTH, expand=True)
        self.update_font_size()

        copy_buttons_frame = tk.Frame(self)
        copy_buttons_frame.pack()

        copy_last_button = tk.Button(copy_buttons_frame, text="Copy Last Message", command=self.copy_last_message)
        copy_last_button.pack(side=tk.LEFT)

        copy_conversation_button = tk.Button(copy_buttons_frame, text="Copy Conversation", command=self.copy_conversation)
        copy_conversation_button.pack(side=tk.LEFT)

        self.progress_label = tk.Label(self, text="")
        self.progress_label.pack()

        input_frame = tk.Frame(self)
        input_frame.pack(fill=tk.X)

        self.input_edit = tk.Entry(input_frame)
        self.input_edit.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.input_edit.bind("<Return>", self.send_message)

        self.send_button = tk.Button(input_frame, text="Send", command=self.send_message)
        self.send_button.pack(side=tk.RIGHT)

        # Label to display current generation
        self.generation_label = tk.Label(self, text="Generation: 0")
        self.generation_label.pack()

        # Progress bar for real-time progress
        self.progressbar = Progressbar(self, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.progressbar.pack(fill=tk.X, padx=10, pady=5)

    def load_prompt(self):
        try:
            with open(self.prompt_file, 'r') as f:
                return json.load(f).get("prompt", "You are a helpful AI assistant.")
        except Exception as e:
            self.show_error("Error loading prompt", str(e))
            return "You are a helpful AI assistant."

    def save_prompt(self, prompt):
        try:
            with open(self.prompt_file, 'w') as f:
                json.dump({"prompt": prompt}, f)
        except Exception as e:
            self.show_error("Error saving prompt", str(e))

    def new_prompt(self):
        prompt = simpledialog.askstring("New Prompt", "Enter your new prompt:", initialvalue=self.prompt)
        if prompt:
            self.prompt = prompt
            self.save_prompt(prompt)
            self.clear_history()
            self.show_notification("Success", "New prompt saved and conversation history cleared.")

    def clear_history(self):
        self.full_story = []
        self.summaries = []
        self.total_tokens = 0
        self.current_generation = 0
        self.target_length = 0
        self.conversation_display.configure(state='normal')
        self.conversation_display.delete(1.0, tk.END)
        self.conversation_display.insert(tk.END, "Conversation history cleared. Start a new conversation!\n")
        self.conversation_display.configure(state='disabled')
        self.generation_label.config(text="Generation: 0")  # Reset generation label

    def copy_last_message(self):
        if self.full_story:
            self.copy_to_clipboard(self.full_story[-1])

    def copy_conversation(self):
        if self.full_story:
            conversation = "\n\n".join(self.full_story)
            self.copy_to_clipboard(conversation)

    def copy_to_clipboard(self, text):
        try:
            self.clipboard_clear()
            self.clipboard_append(text)
            self.show_notification("Copied", "Text copied to clipboard.")
        except Exception as e:
            self.show_error("Error copying to clipboard", str(e))

    def show_notification(self, title, message):
        messagebox.showinfo(title, message)

    def show_error(self, title, message):
        messagebox.showerror(title, message)

    def export_generations(self):
        filename = filedialog.asksaveasfilename(title="Export Generations", defaultextension=".txt",
                                                filetypes=[("Text Files", ".txt")])
        if filename:
            try:
                with open(filename, 'w') as f:
                    f.write("\n\n".join(self.full_story)) # Write the entire story
                self.show_notification("Export Successful", f"Generations exported to {filename}")
            except Exception as e:
                self.show_error("Export Failed", str(e))

    def send_message(self, event=None):
        user_input = self.input_edit.get().strip()
        if not user_input or not self.api_key:
            return

        num_generations = simpledialog.askinteger("Generations", "Enter the number of generations:", minvalue=1)
        if num_generations is None:
            return  # User cancelled

        self.target_length = num_generations
        self.full_story.append(user_input)  # Add user input to the story
        self.display_message("You", user_input)
        self.input_edit.delete(0, tk.END)

        self.send_button.config(state='disabled')
        self.input_edit.config(state='disabled')
        self.progress_label.config(text="Generating response...")

        threading.Thread(target=self.generate_multiple, args=(num_generations,)).start()

    def generate_multiple(self, num_generations):
        for _ in range(num_generations):
            self.current_generation += 1
            self.manage_conversation_length()
            conversation_for_ai = self.build_conversation_for_ai()
            self.generation_label.config(text=f"Generation: {self.current_generation} / {self.target_length}") 

            try:
                client = Groq(api_key=self.api_key)
                time.sleep(0.5)  # Rate limiting (adjust as needed)

                self.update_progress_bar(0)

                random_prompt_addition = random.choice([
                    "Elaborate further.", 
                    "Provide more details.", 
                    "Expand on this.",
                    "Continue on this line of thought.",
                    "Develop this idea further."
                ])

                # Add the prompt addition to the last message
                conversation_for_ai[-1]['content'] += f" {random_prompt_addition}"

                chat_completion = client.chat.completions.create(
                    messages=conversation_for_ai, 
                    model=self.model, 
                    max_tokens=800, # Increased max_tokens 
                    temperature=self.temperature) 
                ai_response = chat_completion.choices[0].message.content

                self.full_story.append(ai_response) # Add to the full story
                self.display_message("-", ai_response) 

                self.update_document_summary()

            except Exception as e:
                self.show_error("AI Error", f"An error occurred: {e}")
                break  # Stop further generations on error
            finally:
                self.update_progress_bar(100)

        # Re-enable buttons
        self.send_button.config(state='normal')
        self.input_edit.config(state='normal')
        self.input_edit.focus()
        self.progress_label.config(text="")

    def display_message(self, sender, message):
        self.conversation_display.configure(state='normal')
        self.conversation_display.insert(tk.END, f"{sender} {message}\n\n") 
        self.conversation_display.configure(state='disabled')
        self.conversation_display.see(tk.END)

    def get_ai_response(self, full_conversation):
        try:
            client = Groq(api_key=self.api_key)
            time.sleep(0.5)  # Rate limiting

            self.update_progress_bar(0)
            chat_completion = client.chat.completions.create(
                messages=full_conversation, model=self.model)
            ai_response = chat_completion.choices[0].message.content
            self.full_story.append(ai_response)
            self.display_message("AI", ai_response)

        except Exception as e:
            self.show_error("AI Error", f"An error occurred: {e}")
        finally:
            self.send_button.config(state='normal')
            self.input_edit.config(state='normal')
            self.input_edit.focus()
            self.progress_label.config(text="")
            self.update_progress_bar(100)

    def update_progress_bar(self, value):
        self.progressbar['value'] = value
        self.update_idletasks()

    def manage_conversation_length(self):
        self.total_tokens = sum(len(msg.split()) for msg in self.full_story)
        if self.total_tokens > MAX_CONTEXT_TOKENS:
            self.summarize_conversation()

    def summarize_conversation(self):
        try:
            to_summarize = self.full_story[-SUMMARIZE_AFTER_MESSAGES:] # Summarize last few generations
            summarization_prompt = [
                {"role": "system", "content": "Provide a very brief summary of the following text, focusing on key points and overall theme. Be extremely concise:"},
                {"role": "user", "content": "\n\n".join(to_summarize)}
            ]
            client = Groq(api_key=self.api_key)
            summary_completion = client.chat.completions.create(
                messages=summarization_prompt, model=self.model, max_tokens=100)
            summary = summary_completion.choices[0].message.content
            self.summaries.append(summary) 
            self.full_story = self.full_story[:-SUMMARIZE_AFTER_MESSAGES] + [summary] # Replace with summary
            self.total_tokens = sum(len(msg.split()) for msg in self.full_story)
        except Exception as e:
            self.show_error("Summarization Error", str(e))

    def update_document_summary(self):
        if self.current_generation % SUMMARIZE_AFTER_MESSAGES == 0: 
            self.summarize_conversation()

    def build_conversation_for_ai(self):
        conversation = [{"role": "system", "content": self.prompt}]
        if self.summaries:
            conversation.append({"role": "assistant", "content": " ".join(self.summaries)})
        conversation.append({"role": "assistant", "content": self.full_story[-1]}) # Add the latest part
        return conversation

class OptionsDialog(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.title("Options")
        self.result = None

        self.api_key_var = tk.StringVar(value=parent.api_key)
        self.model_var = tk.StringVar(value=parent.model)
        self.font_size_var = tk.IntVar(value=parent.font_size)
        self.copy_raw_var = tk.BooleanVar(value=parent.copy_raw)
        self.temperature_var = tk.DoubleVar(value=parent.temperature)

        tk.Label(self, text="API Key:").pack()
        self.api_key_entry = tk.Entry(self, textvariable=self.api_key_var, show='')
        self.api_key_entry.pack(fill=tk.X)

        tk.Label(self, text="Select Model:").pack()
        self.model_combo = tk.OptionMenu(self, self.model_var, "llama-3.1-70b-versatile",
                                         "llama-3.1-405b-reasoning", "llama-3.1-8b-instant", "Custom")
        self.model_combo.pack(fill=tk.X)

        tk.Label(self, text="Custom Model Name:").pack()
        self.custom_model_entry = tk.Entry(self)
        self.custom_model_entry.pack(fill=tk.X)
        if self.model_var.get() != "Custom":
            self.custom_model_entry.config(state='disabled')
        else:
            self.custom_model_entry.insert(0, parent.model)

        def update_custom_state(*args):
            if self.model_var.get() == "Custom":
                self.custom_model_entry.config(state='normal')
            else:
                self.custom_model_entry.config(state='disabled')

        self.model_var.trace('w', update_custom_state)

        tk.Label(self, text="Font Size:").pack()
        self.font_size_spinbox = tk.Spinbox(self, from_=8, to=24, textvariable=self.font_size_var)
        self.font_size_spinbox.pack()

        self.copy_raw_checkbox = tk.Checkbutton(self, text="Copy Raw Text (Disable Formatting)", variable=self.copy_raw_var)
        self.copy_raw_checkbox.pack()

        tk.Label(self, text="Temperature (0.0 - 1.0):").pack()
        self.temperature_spinbox = tk.Spinbox(self, from_=0.0, to=1.0, increment=0.1, textvariable=self.temperature_var)
        self.temperature_spinbox.pack()

        button_frame = tk.Frame(self)
        button_frame.pack(pady=10)

        ok_button = tk.Button(button_frame, text="OK", command=self.on_ok)
        ok_button.pack(side=tk.LEFT, padx=5)
        cancel_button = tk.Button(button_frame, text="Cancel", command=self.destroy)
        cancel_button.pack(side=tk.RIGHT, padx=5)

        self.grab_set()

    def on_ok(self):
        model = self.custom_model_entry.get() if self.model_var.get() == "Custom" else self.model_var.get()
        self.result = (self.api_key_var.get(), model, self.font_size_var.get(), self.copy_raw_var.get(), self.temperature_var.get())
        self.destroy()

if __name__ == '__main__':
    app = CustomAITool()
    app.mainloop()
EOF

# Create the config.json file (API key is now hardcoded in Python)
cat << EOF > "$BUILD_DIR/config.json"
{
    "model": "llama-3.1-70b-versatile",
    "font_size": 12,
    "copy_raw": false,
    "temperature": 0.7
}
EOF

# Create the saved_prompt.json file
cat << EOF > "$BUILD_DIR/saved_prompt.json"
{
    "prompt": "You are a helpful and concise AI assistant that can generate different creative text formats,  like stories, articles, reports, and other written content. Avoid unnecessary introductory phrases."
}
EOF

cd "$BUILD_DIR"

# Build the app using PyInstaller without a spec file
pyinstaller --windowed --name "$APP_NAME" \
    --add-data "config.json:." \
    --add-data "saved_prompt.json:." \
    custom_ai_tool_gui.py

# Copy the app to Desktop
cp -R "dist/$APP_NAME.app" "$DESKTOP_DIR/"

# Create a DMG file (macOS only)
hdiutil create -volname "$APP_NAME" -srcfolder "$DESKTOP_DIR/$APP_NAME.app" -ov -format UDZO "$DESKTOP_DIR/$APP_NAME.dmg"

# Deactivate the virtual environment
deactivate

echo "$APP_NAME.dmg has been created on your Desktop at $DESKTOP_DIR/$APP_NAME.dmg"
echo "The app includes default config.json and saved_prompt.json files."
echo "You can now manually remove the build directory at $BUILD_DIR"error example standard laxative, which Nurse Johnson administers to Ms. Jackson.

MS. JACKSON
(gratefully)
Thank you, Doctor. I feel so much better already.

NURSE JOHNSON
(smiling)
I'm glad I could help, Ms. Jackson. Even if it wasn't with my grandma's prune juice.

The scene ends with Nurse Johnson and Dr. Smith smiling and Ms. Jackson looking relieved.

FADE OUT.

The scene is a comedic take on the importance of following established medical protocols, while also highlighting the potential benefits of alternative treatments. The character of Nurse Johnson is a well-meaning but eccentric nurse who is trying to make a difference, even if his methods are unorthodox. The scene also showcases the importance of teamwork and communication in a medical setting.

    This is generation 20 out of 50. Continue on this line of thought. This is generation 21 out of 50. Continue on this line of thought. This is generation 22 out of 50. Continue on this line of thought. This is generation 23 out of 50. Continue on this line of thought. This is generation 24 out of 50. Continue on this line of thought. This is generation 25 out of 50. Continue on this line of thought. This is generation 26 out of 50. Continue on this line of thought. This is generation 27 out of 50. Continue on this line of thought. This is generation 28 out of 50. Continue on this line of thought. This is generation 29 out of 50. Continue on this line of thought. This is generation 30 out of 50. Continue on this line of thought. This is generation 31 out of 50. Continue on this line of thought. This is generation 32 out of 50. Continue on this line of thought. This is generation 33 out of 50. Continue on this line of thought. This is generation 34 out of 50. Continue on this line of thought. This is generation 35 out of 50. Continue on this line of thought. This is generation 36 out of 50. Continue on this line of thought. This is generation 37 out of 50. Continue on this line of thought. This is generation 38 out of 50. Continue on this line of thought. This is generation 39 out of 50. Continue on this line of thought. This is generation 40 out of 50. Continue on this line of thought. This is generation 41 out of 50. Continue on this line of thought. This is generation 42 out of 50. Continue on this line of thought. This is generation 43 out of 50. Continue on this line of thought. This is generation 44 out of 50. Continue on this line of thought. This is generation 45 out of 50. Continue on this line of thought. This is generation 46 out of 50. Continue on this line of thought. This is generation 47 out of 50. Continue on this line of thought. This is generation 48 out of 50. Continue on this line of thought. This is generation 49 out of 50. Continue on this line of thought. This is generation 50 out of 50. Continue on this line of thought. This is generation 51 out of 50. Continue on this line of thought. This is generation 52 out of 50. Continue on this line of thought. This is generation 53 out of 50. Continue on this line of thought. This is generation 54 out of 50. Continue on this line of thought. This is generation 55 out of 50. Continue on this line of thought. This is generation 56 out of 50. Continue on this line of thought. This is generation 57 out of 50. Continue on this line of thought. This is generation 58 out of 50. Continue on this line of thought. This is generation 59 out of 50. Continue on this line of thought. This is generation 60 out of 50. Continue on this line of thought. This is generation 61 out of 50. Continue on this line of thought. This

    Here is the remainder of the scene:

INT. HOSPITAL ROOM - DAY

Nurse Johnson, played by Kevin Hart, stands beside Ms. Jackson's hospital bed, holding a glass of prune juice.

NURSE JOHNSON:
Come on, Ms. Jackson. Drink this. It's my grandma's special recipe. It'll get things moving in no time.

MS. JACKSON:
(skeptical)
I don't know, Nurse Johnson. I'm not sure I want to drink that.

NURSE JOHNSON:
(persistent)
Trust me, it's natural. No harsh chemicals or side effects. Just pure, unadulterated prunes.

MS. JACKSON:
(hesitant)
Okay, fine. But if this makes me worse, I'm blaming you.

Nurse Johnson hands Ms. Jackson the glass, and she takes a hesitant sip.

Just as Ms. Jackson is about to take another sip, Dr. Smith, played by Idris Elba, enters the room.

DR. SMITH:
What's going on here?

NURSE JOHNSON:
(nervously)
Just trying to help Ms. Jackson with her, uh, digestive issues.

DR. SMITH:
(skeptical)
I see. And what exactly is that you're giving her?

NURSE JOHNSON:
(sheepishly)
My grandma's special prune juice recipe.

DR. SMITH:
(firmly)
Nurse Johnson, we need to follow establish#!/bin/bash
set -e

BUILD_DIR="$HOME/Desktop/CustomAIToolBuild"
DESKTOP_DIR="$HOME/Desktop"
APP_NAME="CustomAITool"

mkdir -p "$BUILD_DIR"
python3 -m venv "$BUILD_DIR/venv"
source "$BUILD_DIR/venv/bin/activate"

pip install --upgrade pip
pip install groq pyinstaller markdown requests tqdm

cat << 'EOF' > "$BUILD_DIR/custom_ai_tool_gui.py"
import os
import sys
import json
import threading
import time
from groq import Groq
import tkinter as tk
from tkinter import messagebox, filedialog, simpledialog, scrolledtext
from tkinter.ttk import Progressbar
import re
import requests
from tqdm import tqdm
import random

DEFAULT_MODEL = "llama-3.1-70b-versatile"
# API KEY HARDCODED (NOT RECOMMENDED)
DEFAULT_API_KEY = "<TRUFFLEHOG_REDACTED_GROQ>"
MAX_TOKENS = 4000  # Adjust according to API limitations
SUMMARIZE_AFTER_MESSAGES = 5  # Number of messages after which to start summarizing 
MAX_CONTEXT_TOKENS = 2000  # Threshold to start summarizing - lowered
DEFAULT_TEMPERATURE = 0.7 # Default temperature

def resource_path(relative_path):
    """ Get absolute path to resource, works for dev and for PyInstaller """
    try:
        # PyInstaller creates a temp folder and stores path in _MEIPASS
        base_path = sys.MEIPASS
    except Exception:
        base_path = os.path.dirname(sys.argv[0])

    return os.path.join(base_path, relative_path)

class CustomAITool(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("Custom AI Tool")
        self.geometry("800x600")

        self.config_file = resource_path("config.json")
        self.prompt_file = resource_path("saved_prompt.json")
        self.create_default_files()
        self.load_config()
        self.prompt = self.load_prompt()
        self.conversation_history = []
        self.summaries = []
        self.total_tokens = 0  # Track total tokens used
        self.current_generation = 0  # Track current generation
        self.document_summary = "" # Summary of the entire document so far
        self.target_length = 0 # Target number of generations

        self.create_widgets()

    def create_default_files(self):
        if not os.path.exists(self.config_file):
            with open(self.config_file, 'w') as f:
                json.dump({"api_key": DEFAULT_API_KEY, "model": DEFAULT_MODEL, "font_size": 12, "copy_raw": False, "temperature": DEFAULT_TEMPERATURE}, f)
        if not os.path.exists(self.prompt_file):
            with open(self.prompt_file, 'w') as f:
                json.dump({"prompt": "You are a helpful and concise AI assistant that can generate different creative text formats,  like stories, articles, reports, and other written content. Avoid unnecessary introductory phrases."}, f)

    def load_config(self):
        try:
            with open(self.config_file, 'r') as f:
                config = json.load(f)
                self.api_key = config.get("api_key", DEFAULT_API_KEY)
                self.model = config.get("model", DEFAULT_MODEL)
                self.font_size = config.get("font_size", 12)
                self.copy_raw = config.get("copy_raw", False)
                self.temperature = config.get("temperature", DEFAULT_TEMPERATURE)
        except Exception as e:
            self.show_error("Error loading config", str(e))
            self.api_key, self.model, self.font_size, self.copy_raw, self.temperature = DEFAULT_API_KEY, DEFAULT_MODEL, 12, False, DEFAULT_TEMPERATURE

    def save_config(self):
        try:
            with open(self.config_file, 'w') as f:
                json.dump({"api_key": self.api_key, "model": self.model, "font_size": self.font_size, "copy_raw": self.copy_raw, "temperature": self.temperature}, f)
        except Exception as e:
            self.show_error("Error saving config", str(e))

    def show_options_dialog(self):
        dialog = OptionsDialog(self)
        self.wait_window(dialog)
        if dialog.result:
            self.api_key, self.model, self.font_size, self.copy_raw, self.temperature = dialog.result
            self.save_config()
            self.update_font_size()
            self.show_notification("Settings Updated", "Your settings have been updated successfully.")

    def update_font_size(self):
        self.conversation_display.configure(font=("TkDefaultFont", self.font_size))

    def create_widgets(self):
        toolbar = tk.Frame(self)
        toolbar.pack(side=tk.TOP, fill=tk.X)

        options_button = tk.Button(toolbar, text="Options", command=self.show_options_dialog)
        options_button.pack(side=tk.LEFT)

        new_prompt_button = tk.Button(toolbar, text="New Prompt", command=self.new_prompt)
        new_prompt_button.pack(side=tk.LEFT)

        clear_history_button = tk.Button(toolbar, text="Clear History", command=self.clear_history)
        clear_history_button.pack(side=tk.LEFT)

        export_button = tk.Button(toolbar, text="Export", command=self.export_generations)
        export_button.pack(side=tk.LEFT)

        self.conversation_display = scrolledtext.ScrolledText(self, state='disabled', wrap=tk.WORD)
        self.conversation_display.pack(fill=tk.BOTH, expand=True)
        self.update_font_size()

        copy_buttons_frame = tk.Frame(self)
        copy_buttons_frame.pack()

        copy_last_button = tk.Button(copy_buttons_frame, text="Copy Last Message", command=self.copy_last_message)
        copy_last_button.pack(side=tk.LEFT)

        copy_conversation_button = tk.Button(copy_buttons_frame, text="Copy Conversation", command=self.copy_conversation)
        copy_conversation_button.pack(side=tk.LEFT)

        self.progress_label = tk.Label(self, text="")
        self.progress_label.pack()

        input_frame = tk.Frame(self)
        input_frame.pack(fill=tk.X)

        self.input_edit = tk.Entry(input_frame)
        self.input_edit.pack(side=tk.LEFT, fill=tk.X, expand=True)
        self.input_edit.bind("<Return>", self.send_message)

        self.send_button = tk.Button(input_frame, text="Send", command=self.send_message)
        self.send_button.pack(side=tk.RIGHT)

        # Label to display current generation
        self.generation_label = tk.Label(self, text="Generation: 0")
        self.generation_label.pack()

        # Progress bar for real-time progress
        self.progressbar = Progressbar(self, orient=tk.HORIZONTAL, length=100, mode='determinate')
        self.progressbar.pack(fill=tk.X, padx=10, pady=5)

    def load_prompt(self):
        try:
            with open(self.prompt_file, 'r') as f:
                return json.load(f).get("prompt", "You are a helpful AI assistant.")
        except Exception as e:
            self.show_error("Error loading prompt", str(e))
            return "You are a helpful AI assistant."

    def save_prompt(self, prompt):
        try:
            with open(self.prompt_file, 'w') as f:
                json.dump({"prompt": prompt}, f)
        except Exception as e:
            self.show_error("Error saving prompt", str(e))

    def new_prompt(self):
        prompt = simpledialog.askstring("New Prompt", "Enter your new prompt:", initialvalue=self.prompt)
        if prompt:
            self.prompt = prompt
            self.save_prompt(prompt)
            self.clear_history()
            self.show_notification("Success", "New prompt saved and conversation history cleared.")

    def clear_history(self):
        self.conversation_history = []
        self.summaries = []
        self.total_tokens = 0
        self.current_generation = 0
        self.document_summary = ""
        self.target_length = 0
        self.conversation_display.configure(state='normal')
        self.conversation_display.delete(1.0, tk.END)
        self.conversation_display.insert(tk.END, "Conversation history cleared. Start a new conversation!\n")
        self.conversation_display.configure(state='disabled')
        self.generation_label.config(text="Generation: 0")  # Reset generation label

    def copy_last_message(self):
        if self.conversation_history:
            self.copy_to_clipboard(self.conversation_history[-1]['content'])

    def copy_conversation(self):
        if self.conversation_history:
            conversation = "\n\n".join([f"{msg['role'].capitalize()}: {msg['content']}" for msg in self.conversation_history])
            self.copy_to_clipboard(conversation)

    def copy_to_clipboard(self, text):
        try:
            self.clipboard_clear()
            self.clipboard_append(text)
            self.show_notification("Copied", "Text copied to clipboard.")
        except Exception as e:
            self.show_error("Error copying to clipboard", str(e))

    def show_notification(self, title, message):
        messagebox.showinfo(title, message)

    def show_error(self, title, message):
        messagebox.showerror(title, message)

    def export_generations(self):
        filename = filedialog.asksaveasfilename(title="Export Generations", defaultextension=".txt",
                                                filetypes=[("Text Files", ".txt")])
        if filename:
            try:
                with open(filename, 'w') as f:
                    for msg in self.conversation_history:
                        f.write(f"{msg['content']}\n\n")  # Write only the content, no role
                self.show_notification("Export Successful", f"Generations exported to {filename}")
            except Exception as e:
                self.show_error("Export Failed", str(e))

    def send_message(self, event=None):
        user_input = self.input_edit.get().strip()
        if not user_input or not self.api_key:
            return

        # Get number of generations from user
        num_generations = simpledialog.askinteger("Generations", "Enter the number of generations:", minvalue=1)
        if num_generations is None:
            return  # User cancelled

        self.target_length = num_generations
        self.conversation_history.append({"role": "user", "content": user_input})
        self.display_message("You", user_input)
        self.input_edit.delete(0, tk.END)

        self.send_button.config(state='disabled')
        self.input_edit.config(state='disabled')
        self.progress_label.config(text="Generating response...")

        # Start generations in a separate thread
        threading.Thread(target=self.generate_multiple, args=(num_generations,)).start()

    def generate_multiple(self, num_generations):
        for _ in range(num_generations):
            self.current_generation += 1
            self.manage_conversation_length()
            full_conversation = self.build_full_conversation()
            self.generation_label.config(text=f"Generation: {self.current_generation} / {self.target_length}")

            try:
                client = Groq(api_key=self.api_key)
                time.sleep(0.5)  # Rate limiting (adjust as needed)

                # Show progress bar
                self.update_progress_bar(0)

                # Add a more generic prompt addition
                random_prompt_addition = random.choice([
                    "Elaborate further.", 
                    "Provide more details.", 
                    "Expand on this.",
                    "Continue on this line of thought."
                ])

                # Incorporate information about the document's progress
                progress_info = f"This is generation {self.current_generation} out of {self.target_length}. "

                # Construct the full prompt
                full_conversation[-1]['content'] += f" {progress_info} {random_prompt_addition}"

                chat_completion = client.chat.completions.create(
                    messages=full_conversation, model=self.model, max_tokens=800, temperature=self.temperature) # Increased max_tokens
                ai_response = chat_completion.choices[0].message.content

                self.conversation_history.append({"role": "assistant", "content": ai_response})
                self.display_message("-", ai_response)

                # Update the document summary after each generation
                self.update_document_summary()

            except Exception as e:
                self.show_error("AI Error", f"An error occurred: {e}")
                break  # Stop further generations on error
            finally:
                self.update_progress_bar(100)

        # Re-enable buttons after all generations are complete
        self.send_button.config(state='normal')
        self.input_edit.config(state='normal')
        self.input_edit.focus()
        self.progress_label.config(text="")

    def display_message(self, sender, message):
        self.conversation_display.configure(state='normal')
        self.conversation_display.insert(tk.END, f"{sender} {message}\n\n") 
        self.conversation_display.configure(state='disabled')
        self.conversation_display.see(tk.END)

    def get_ai_response(self, full_conversation):
        try:
            client = Groq(api_key=self.api_key)
            time.sleep(0.5)  # Rate limiting (adjust as needed)

            self.update_progress_bar(0)
            chat_completion = client.chat.completions.create(
                messages=full_conversation, model=self.model)
            ai_response = chat_completion.choices[0].message.content
            self.conversation_history.append({"role": "assistant", "content": ai_response})
            self.display_message("AI", ai_response)

        except Exception as e:
            self.show_error("AI Error", f"An error occurred: {e}")
        finally:
            self.send_button.config(state='normal')
            self.input_edit.config(state='normal')
            self.input_edit.focus()
            self.progress_label.config(text="")
            self.update_progress_bar(100)

    def update_progress_bar(self, value):
        self.progressbar['value'] = value
        self.update_idletasks()

    def manage_conversation_length(self):
        self.total_tokens = sum(len(msg['content'].split()) for msg in self.conversation_history)
        if self.total_tokens > MAX_CONTEXT_TOKENS:
            self.summarize_conversation()

    def summarize_conversation(self):
        try:
            to_summarize = self.conversation_history[-3:] # Summarize last 3 generations
            summarization_prompt = [
                {"role": "system", "content": "Provide a very brief summary of the following text, focusing on key points and overall theme. Be extremely concise:"},
                {"role": "user", "content": "\n\n".join([f"{msg['content']}" for msg in to_summarize])}
            ]
            client = Groq(api_key=self.api_key)
            summary_completion = client.chat.completions.create(
                messages=summarization_prompt, model=self.model, max_tokens=100) # Limit summary length
            summary = summary_completion.choices[0].message.content

            # Update the document summary
            self.document_summary += " " + summary 

            # Reset conversation history with the document summary
            self.conversation_history = [{"role": "assistant", "content": self.document_summary}]
            self.total_tokens = sum(len(msg['content'].split()) for msg in self.conversation_history)
        except Exception as e:
            self.show_error("Summarization Error", str(e))

    def update_document_summary(self):
        if self.current_generation % 3 == 0: # Update every 3 generations
            self.summarize_conversation()

    def build_full_conversation(self):
        full_conversation = [{"role": "system", "content": self.prompt}]
        full_conversation.extend(self.conversation_history)
        return full_conversation

class OptionsDialog(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)
        self.title("Options")
        self.result = None

        self.api_key_var = tk.StringVar(value=parent.api_key)
        self.model_var = tk.StringVar(value=parent.model)
        self.font_size_var = tk.IntVar(value=parent.font_size)
        self.copy_raw_var = tk.BooleanVar(value=parent.copy_raw)
        self.temperature_var = tk.DoubleVar(value=parent.temperature)

        tk.Label(self, text="API Key:").pack()
        self.api_key_entry = tk.Entry(self, textvariable=self.api_key_var, show='')
        self.api_key_entry.pack(fill=tk.X)

        tk.Label(self, text="Select Model:").pack()
        self.model_combo = tk.OptionMenu(self, self.model_var, "llama-3.1-70b-versatile",
                                         "llama-3.1-405b-reasoning", "llama-3.1-8b-instant", "Custom")
        self.model_combo.pack(fill=tk.X)

        tk.Label(self, text="Custom Model Name:").pack()
        self.custom_model_entry = tk.Entry(self)
        self.custom_model_entry.pack(fill=tk.X)
        if self.model_var.get() != "Custom":
            self.custom_model_entry.config(state='disabled')
        else:
            self.custom_model_entry.insert(0, parent.model)

        def update_custom_state(*args):
            if self.model_var.get() == "Custom":
                self.custom_model_entry.config(state='normal')
            else:
                self.custom_model_entry.config(state='disabled')

        self.model_var.trace('w', update_custom_state)

        tk.Label(self, text="Font Size:").pack()
        self.font_size_spinbox = tk.Spinbox(self, from_=8, to=24, textvariable=self.font_size_var)
        self.font_size_spinbox.pack()

        self.copy_raw_checkbox = tk.Checkbutton(self, text="Copy Raw Text (Disable Formatting)", variable=self.copy_raw_var)
        self.copy_raw_checkbox.pack()

        tk.Label(self, text="Temperature (0.0 - 1.0):").pack()
        self.temperature_spinbox = tk.Spinbox(self, from_=0.0, to=1.0, increment=0.1, textvariable=self.temperature_var)
        self.temperature_spinbox.pack()

        button_frame = tk.Frame(self)
        button_frame.pack(pady=10)

        ok_button = tk.Button(button_frame, text="OK", command=self.on_ok)
        ok_button.pack(side=tk.LEFT, padx=5)
        cancel_button = tk.Button(button_frame, text="Cancel", command=self.destroy)
        cancel_button.pack(side=tk.RIGHT, padx=5)

        self.grab_set()

    def on_ok(self):
        model = self.custom_model_entry.get() if self.model_var.get() == "Custom" else self.model_var.get()
        self.result = (self.api_key_var.get(), model, self.font_size_var.get(), self.copy_raw_var.get(), self.temperature_var.get())
        self.destroy()

if __name__ == '__main__':
    app = CustomAITool()
    app.mainloop()
EOF

# Create the config.json file (API key is now hardcoded in Python)
cat << EOF > "$BUILD_DIR/config.json"
{
    "model": "llama-3.1-70b-versatile",
    "font_size": 12,
    "copy_raw": false,
    "temperature": 0.7
}
EOF

# Create the saved_prompt.json file
cat << EOF > "$BUILD_DIR/saved_prompt.json"
{
    "prompt": "You are a helpful and concise AI assistant that can generate different creative text formats,  like stories, articles, reports, and other written content. Avoid unnecessary introductory phrases."
}
EOF

cd "$BUILD_DIR"

# Build the app using PyInstaller without a spec file
pyinstaller --windowed --name "$APP_NAME" \
    --add-data "config.json:." \
    --add-data "saved_prompt.json:." \
    custom_ai_tool_gui.py

# Copy the app to Desktop
cp -R "dist/$APP_NAME.app" "$DESKTOP_DIR/"

# Create a DMG file (macOS only)
hdiutil create -volname "$APP_NAME" -srcfolder "$DESKTOP_DIR/$APP_NAME.app" -ov -format UDZO "$DESKTOP_DIR/$APP_NAME.dmg"

# Deactivate the virtual environment
deactivate

echo "$APP_NAME.dmg has been created on your Desktop at $DESKTOP_DIR/$APP_NAME.dmg"
echo "The app includes default config.json and saved_prompt.json files."
echo "You can now manually remove the build directory at $BUILD_DIR" goals: Improve this to be a much more robust app designed for generation of long documents like scripts and novels

Implement a sophisticated mechanism to keep track of all data so the new generations understand what was generated before and that they must continue. But you must do this through clever summarization mechanisms and tagging so that you don't overload the context window of the ai - as you generate more simply copying prior generations will quickly overload token limits - and rudimentary nonspecfic summarization prompting systems will fail to capture nuance - must design much more sophisticated system

Understand api limits and rate calling and also prevent app from getting stuck - it is important that you implement this without wasting the user's time by making them wait for no reason too - a detailed balanced and speed oriented system is preferred

Progress of generations and content of generations should be visually displayed to user in real time - display in gui text window

Ability to export entire generations or a set of generations or single generations as txts during and after running through gui buttons and text windows is preferred.