File size: 5,305 Bytes
3ebc508
1deacc5
3ebc508
 
 
 
1deacc5
 
14c0817
 
3ebc508
 
 
 
14c0817
 
 
 
 
3ebc508
1deacc5
3ebc508
 
14c0817
 
 
 
 
 
 
 
 
 
 
 
 
3ebc508
14c0817
 
 
 
 
3ebc508
1deacc5
 
14c0817
 
1deacc5
14c0817
 
 
263ee79
14c0817
1deacc5
263ee79
14c0817
1deacc5
 
 
 
3ebc508
 
 
 
 
 
263ee79
 
3ebc508
263ee79
14c0817
263ee79
3ebc508
14c0817
 
 
 
 
1deacc5
3ebc508
14c0817
 
3ebc508
 
 
14c0817
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3ebc508
 
14c0817
3ebc508
 
1deacc5
 
14c0817
1deacc5
 
14c0817
1deacc5
3ebc508
e81567c
14c0817
 
e81567c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# app.py - Flask Backend
from flask import Flask, request, jsonify, send_from_directory
import google.generativeai as genai
from dotenv import load_dotenv
import os
from flask_cors import CORS
import markdown2
import re
from gtts import gTTS # <-- Import gTTS
import uuid # <-- Import UUID for unique filenames

# Load environment variables
load_dotenv()

# Define paths and create static audio directory if it doesn't exist
AUDIO_FOLDER = os.path.join('static', 'audio')
if not os.path.exists(AUDIO_FOLDER):
    os.makedirs(AUDIO_FOLDER)

# Initialize Flask app
app = Flask(__name__, static_folder='static')
CORS(app)  # Enable CORS for all routes

# Configure Gemini with a system instruction
# This guides the AI's behavior and ensures responses are good for TTS.
system_instruction_text = """
You are a helpful, friendly, and informative AI assistant named AstroChat.
Your goal is to provide clear, concise, and natural-sounding answers to user queries.
When you respond:
- Use clear and simple language.
- Avoid overly complex sentence structures that might be hard to read aloud.
- Keep the user engaged and offer follow-up questions or related topics where appropriate.
- Ensure your responses are suitable for text-to-speech conversion.
- Provide factual and accurate information.
"""

genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
# Initialize the model with the system instruction
model = genai.GenerativeModel(
    'gemini-2.5-flash', # Using 1.5-flash for better performance and system_instruction support
    system_instruction=system_instruction_text
)

def convert_markdown_to_html(text):
    # Convert markdown to HTML
    # Using 'fenced-code-blocks' and 'tables' for better markdown support
    html = markdown2.markdown(text, extras=["fenced-code-blocks", "tables"])
    
    # Add custom styling to code blocks (pre blocks)
    # This specifically targets `<pre><code>` blocks generated by markdown2 for styling.
    html = re.sub(r'<pre><code(.*?)>', r'<pre class="code-block"><code\1>', html)
    
    # Convert **bold** to <strong> for better visibility (markdown2 usually handles this, but good to ensure)
    html = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', html)
    
    # Convert *italic* to <em> (markdown2 usually handles this, but good to ensure)
    html = re.sub(r'\*(.*?)\*', r'<em>\1</em>', html)
    
    return html

@app.route('/chat', methods=['POST'])
def chat():
    try:
        data = request.json
        user_message = data.get('message')
        
        if not user_message:
            return jsonify({"error": "No message provided"}), 400
        
        # Generate response using Gemini
        # For multi-turn conversations, you might manage chat history here
        response = model.generate_content(user_message)
        
        # Get plain text for audio generation
        plain_text_response = response.text
        
        # Convert markdown to HTML for display
        html_response = convert_markdown_to_html(plain_text_response)
        
        return jsonify({
            "response_html": html_response,
            "response_text": plain_text_response # Send plain text for TTS
        })
        
    except Exception as e:
        app.logger.error(f"Chat Error: {e}")
        return jsonify({"error": str(e)}), 500

@app.route('/generate-audio', methods=['POST'])
def generate_audio():
    try:
        data = request.json
        text_to_speak = data.get('text')

        if not text_to_speak:
            return jsonify({"error": "No text provided"}), 400

        # Sanitize text for TTS (remove common markdown characters for smoother pronunciation)
        # This prevents gTTS from trying to pronounce asterisks, backticks, etc.
        cleaned_text = re.sub(r'[\*_`#]', '', text_to_speak) # Remove bold, italic, code, headers markdown
        cleaned_text = re.sub(r'\s+', ' ', cleaned_text).strip() # Replace multiple spaces with single space

        if not cleaned_text: # If text becomes empty after cleaning
            return jsonify({"error": "Text became empty after cleaning, cannot generate audio."}), 400

        # Generate a unique filename using UUID to prevent collisions
        filename = f"{uuid.uuid4()}.mp3"
        filepath = os.path.join(AUDIO_FOLDER, filename)

        # Create TTS object and save to file
        tts = gTTS(text=cleaned_text, lang='en', slow=False) # 'en' for English, 'slow=False' for normal speed
        tts.save(filepath)

        # Return the URL to the audio file, converting path separators for web use
        audio_url = f"/{filepath.replace(os.path.sep, '/')}"
        return jsonify({"audio_url": audio_url})

    except Exception as e:
        app.logger.error(f"Audio Generation Error: {e}")
        return jsonify({"error": str(e)}), 500

# Serve the main index.html file
@app.route('/')
def serve_index():
    return send_from_directory('static', 'index.html')

# Serve other static files (CSS, JS, audio files)
@app.route('/<path:path>')
def serve_static(path):
    # Ensure that only files from 'static' are served
    return send_from_directory('static', path)

if __name__ == '__main__':
    # Run the Flask app
    # debug=True allows automatic reloading on code changes and provides more detailed error messages
    app.run(host="0.0.0.0", port=7860)