import os import json from openai import OpenAI from markdown import markdown from bs4 import BeautifulSoup # Define available models AVAILABLE_MODELS = { "DeepSeek V3 (Hyperbolic.xyz)": { "model_name": "deepseek-ai/DeepSeek-V3", "type": "hyperbolic" }, "Llama3.3-70b-Instruct (Hyperbolic.xyz)": { "model_name": "meta-llama/Llama-3.3-70B-Instruct", "type": "hyperbolic" }, #"DeepSeek V3 (HuggingFace.co)": { # "model_name": "deepseek-ai/DeepSeek-V3", # "type": "huggingface" #}, } # Load email examples from JSON file def load_examples(): """ Load example emails from a JSON file. Returns a list of example dictionaries or a default empty example if loading fails. """ try: with open("email_examples.json", "r") as f: return json.load(f) except Exception as e: print(f"Error loading examples: {e}") # Return default empty examples if file can't be loaded return [ {"title": "No examples found", "preceding_conversation": "", "drafted_user_reply": ""} ] # Set up the clients for API access def setup_clients(): """ Initialize and configure API clients for different model providers. Returns a dictionary of configured clients. """ clients = { "hyperbolic": {"key": os.getenv('HYPERBOLIC_XYZ_KEY'), "endpoint": "https://api.hyperbolic.xyz/v1"}, #"huggingface": {"key": os.getenv('HF_KEY'), "endpoint": "https://huggingface.co/api/inference-proxy/together"}, } for client_type in clients: clients[client_type]["client"] = OpenAI( base_url=clients[client_type]["endpoint"], api_key=clients[client_type]["key"] ) return clients def markdown_to_text(markdown_string): # Convert Markdown to HTML html = markdown(markdown_string) # Parse HTML and extract text soup = BeautifulSoup(html, features="html.parser") return soup.get_text() def load_email_guidelines(): """ Load email formatting guidelines from a text file. Returns the content as a string or a default message if loading fails. """ try: with open("email_guidelines.txt", "r") as f: return f.read().strip() except Exception as e: print(f"Error loading email guidelines: {e}") return "No guidelines available." def create_email_format_prompt(email_guidelines, identity=None): """ Create the prompt template that will be sent to the language model. Incorporates the provided email guidelines and placeholders for user input. Args: email_guidelines: The guidelines text to include in the prompt identity: Optional identity perspective to write from """ identity_section = "" if identity and identity.strip(): identity_section = f""" The email is being written from the perspective of: {identity} """ return f""" {email_guidelines} {identity_section} ========================= Your Task: 1. Review the user's draft email 2. Apply the style, formatting, and content guidelines above to improve the email 3. Provide a polished, ready-to-send version Previous email thread: {{preceding_conversation}} User's draft reply: {{drafted_user_reply}} Rewrite the draft to follow the guidelines while preserving the user's intent. Begin with EMAIL_STARTS_HERE followed by the revised email. If you have suggestions, add them after SUGGESTIONS_START_HERE. Don't add any information not found in the original draft or thread. Don't create new URLs, phone numbers, attachments, or subject lines. Start directly with the email body after EMAIL_STARTS_HERE. Include the user's signature if present in their draft. Don't use markdown formatting.""" def process_email( preceding_conversation, drafted_user_reply, session_password, system_message, model_choice, max_tokens, temperature, top_p, password, clients, custom_guidelines=None, identity=None ): """ Process the email and return a formatted response and suggestions. Args: preceding_conversation: The email thread context drafted_user_reply: The user's draft email reply session_password: The current session password system_message: System instructions for the AI model model_choice: The selected AI model max_tokens: Maximum tokens for the response temperature: Temperature setting for response generation top_p: Top-p setting for response generation password: The actual password to verify against clients: Dictionary of configured API clients custom_guidelines: Optional custom guidelines to use instead of default identity: Optional identity perspective to write from Returns: Tuple of (formatted_email, suggestions) """ # Re-check the session password if session_password != password: return "Error: Invalid session password. Please refresh the page and enter the correct password.", "" if model_choice not in AVAILABLE_MODELS: return "Error: Invalid model selection.", "" # Use custom guidelines if provided, otherwise load from file if custom_guidelines is None or custom_guidelines.strip() == "": email_guidelines = load_email_guidelines() else: email_guidelines = custom_guidelines # Get the email format prompt template email_format_prompt = create_email_format_prompt(email_guidelines, identity) # Format the prompt using the template formatted_prompt = email_format_prompt.format( preceding_conversation=preceding_conversation, drafted_user_reply=drafted_user_reply, ) messages = [{"role": "system", "content": system_message}] messages.append({"role": "user", "content": formatted_prompt}) selected_client = clients[AVAILABLE_MODELS[model_choice]["type"]]["client"] try: response = selected_client.chat.completions.create( model=AVAILABLE_MODELS[model_choice]["model_name"], messages=messages, max_tokens=max_tokens, temperature=temperature, top_p=top_p, ) # Split the response based on EMAIL_STARTS_HERE and SUGGESTIONS_START_HERE full_response = response.choices[0].message.content #remove markdown formatting full_response = markdown_to_text(full_response) # First check if EMAIL_STARTS_HERE is in the response if "EMAIL_STARTS_HERE" in full_response: email_parts = full_response.split("EMAIL_STARTS_HERE", 1) email_content = email_parts[1].strip() # Then check if there are suggestions if "SUGGESTIONS_START_HERE" in email_content: parts = email_content.split("SUGGESTIONS_START_HERE", 1) return parts[0].strip(), parts[1].strip() else: return email_content.strip(), "" else: # Fallback to original behavior if marker isn't found if "SUGGESTIONS_START_HERE" in full_response: parts = full_response.split("SUGGESTIONS_START_HERE", 1) return parts[0].strip(), parts[1].strip() else: return full_response.strip(), "" except Exception as e: return f"Error: {str(e)}", ""