Update app.py
Browse files
app.py
CHANGED
@@ -6,29 +6,54 @@ import os
|
|
6 |
from flask_cors import CORS
|
7 |
import markdown2
|
8 |
import re
|
|
|
|
|
9 |
|
10 |
# Load environment variables
|
11 |
load_dotenv()
|
12 |
|
|
|
|
|
|
|
|
|
|
|
13 |
# Initialize Flask app
|
14 |
app = Flask(__name__, static_folder='static')
|
15 |
CORS(app) # Enable CORS for all routes
|
16 |
|
17 |
-
# Configure Gemini
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
19 |
-
model
|
|
|
|
|
|
|
|
|
20 |
|
21 |
def convert_markdown_to_html(text):
|
22 |
# Convert markdown to HTML
|
23 |
-
|
|
|
24 |
|
25 |
-
# Add custom styling to code blocks
|
26 |
-
|
|
|
27 |
|
28 |
-
# Convert **bold** to <strong> for better visibility
|
29 |
html = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', html)
|
30 |
|
31 |
-
# Convert *italic* to <em>
|
32 |
html = re.sub(r'\*(.*?)\*', r'<em>\1</em>', html)
|
33 |
|
34 |
return html
|
@@ -43,25 +68,69 @@ def chat():
|
|
43 |
return jsonify({"error": "No message provided"}), 400
|
44 |
|
45 |
# Generate response using Gemini
|
|
|
46 |
response = model.generate_content(user_message)
|
47 |
|
48 |
-
#
|
49 |
-
|
|
|
|
|
|
|
50 |
|
51 |
return jsonify({
|
52 |
-
"
|
|
|
53 |
})
|
54 |
|
55 |
except Exception as e:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
return jsonify({"error": str(e)}), 500
|
57 |
|
|
|
58 |
@app.route('/')
|
59 |
def serve_index():
|
60 |
return send_from_directory('static', 'index.html')
|
61 |
|
|
|
62 |
@app.route('/<path:path>')
|
63 |
def serve_static(path):
|
|
|
64 |
return send_from_directory('static', path)
|
65 |
|
66 |
if __name__ == '__main__':
|
|
|
|
|
67 |
app.run(host="0.0.0.0", port=7860)
|
|
|
6 |
from flask_cors import CORS
|
7 |
import markdown2
|
8 |
import re
|
9 |
+
from gtts import gTTS # <-- Import gTTS
|
10 |
+
import uuid # <-- Import UUID for unique filenames
|
11 |
|
12 |
# Load environment variables
|
13 |
load_dotenv()
|
14 |
|
15 |
+
# Define paths and create static audio directory if it doesn't exist
|
16 |
+
AUDIO_FOLDER = os.path.join('static', 'audio')
|
17 |
+
if not os.path.exists(AUDIO_FOLDER):
|
18 |
+
os.makedirs(AUDIO_FOLDER)
|
19 |
+
|
20 |
# Initialize Flask app
|
21 |
app = Flask(__name__, static_folder='static')
|
22 |
CORS(app) # Enable CORS for all routes
|
23 |
|
24 |
+
# Configure Gemini with a system instruction
|
25 |
+
# This guides the AI's behavior and ensures responses are good for TTS.
|
26 |
+
system_instruction_text = """
|
27 |
+
You are a helpful, friendly, and informative AI assistant named AstroChat.
|
28 |
+
Your goal is to provide clear, concise, and natural-sounding answers to user queries.
|
29 |
+
When you respond:
|
30 |
+
- Use clear and simple language.
|
31 |
+
- Avoid overly complex sentence structures that might be hard to read aloud.
|
32 |
+
- Keep the user engaged and offer follow-up questions or related topics where appropriate.
|
33 |
+
- Ensure your responses are suitable for text-to-speech conversion.
|
34 |
+
- Provide factual and accurate information.
|
35 |
+
"""
|
36 |
+
|
37 |
genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
|
38 |
+
# Initialize the model with the system instruction
|
39 |
+
model = genai.GenerativeModel(
|
40 |
+
'gemini-2.5-flash', # Using 1.5-flash for better performance and system_instruction support
|
41 |
+
system_instruction=system_instruction_text
|
42 |
+
)
|
43 |
|
44 |
def convert_markdown_to_html(text):
|
45 |
# Convert markdown to HTML
|
46 |
+
# Using 'fenced-code-blocks' and 'tables' for better markdown support
|
47 |
+
html = markdown2.markdown(text, extras=["fenced-code-blocks", "tables"])
|
48 |
|
49 |
+
# Add custom styling to code blocks (pre blocks)
|
50 |
+
# This specifically targets `<pre><code>` blocks generated by markdown2 for styling.
|
51 |
+
html = re.sub(r'<pre><code(.*?)>', r'<pre class="code-block"><code\1>', html)
|
52 |
|
53 |
+
# Convert **bold** to <strong> for better visibility (markdown2 usually handles this, but good to ensure)
|
54 |
html = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', html)
|
55 |
|
56 |
+
# Convert *italic* to <em> (markdown2 usually handles this, but good to ensure)
|
57 |
html = re.sub(r'\*(.*?)\*', r'<em>\1</em>', html)
|
58 |
|
59 |
return html
|
|
|
68 |
return jsonify({"error": "No message provided"}), 400
|
69 |
|
70 |
# Generate response using Gemini
|
71 |
+
# For multi-turn conversations, you might manage chat history here
|
72 |
response = model.generate_content(user_message)
|
73 |
|
74 |
+
# Get plain text for audio generation
|
75 |
+
plain_text_response = response.text
|
76 |
+
|
77 |
+
# Convert markdown to HTML for display
|
78 |
+
html_response = convert_markdown_to_html(plain_text_response)
|
79 |
|
80 |
return jsonify({
|
81 |
+
"response_html": html_response,
|
82 |
+
"response_text": plain_text_response # Send plain text for TTS
|
83 |
})
|
84 |
|
85 |
except Exception as e:
|
86 |
+
app.logger.error(f"Chat Error: {e}")
|
87 |
+
return jsonify({"error": str(e)}), 500
|
88 |
+
|
89 |
+
@app.route('/generate-audio', methods=['POST'])
|
90 |
+
def generate_audio():
|
91 |
+
try:
|
92 |
+
data = request.json
|
93 |
+
text_to_speak = data.get('text')
|
94 |
+
|
95 |
+
if not text_to_speak:
|
96 |
+
return jsonify({"error": "No text provided"}), 400
|
97 |
+
|
98 |
+
# Sanitize text for TTS (remove common markdown characters for smoother pronunciation)
|
99 |
+
# This prevents gTTS from trying to pronounce asterisks, backticks, etc.
|
100 |
+
cleaned_text = re.sub(r'[\*_`#]', '', text_to_speak) # Remove bold, italic, code, headers markdown
|
101 |
+
cleaned_text = re.sub(r'\s+', ' ', cleaned_text).strip() # Replace multiple spaces with single space
|
102 |
+
|
103 |
+
if not cleaned_text: # If text becomes empty after cleaning
|
104 |
+
return jsonify({"error": "Text became empty after cleaning, cannot generate audio."}), 400
|
105 |
+
|
106 |
+
# Generate a unique filename using UUID to prevent collisions
|
107 |
+
filename = f"{uuid.uuid4()}.mp3"
|
108 |
+
filepath = os.path.join(AUDIO_FOLDER, filename)
|
109 |
+
|
110 |
+
# Create TTS object and save to file
|
111 |
+
tts = gTTS(text=cleaned_text, lang='en', slow=False) # 'en' for English, 'slow=False' for normal speed
|
112 |
+
tts.save(filepath)
|
113 |
+
|
114 |
+
# Return the URL to the audio file, converting path separators for web use
|
115 |
+
audio_url = f"/{filepath.replace(os.path.sep, '/')}"
|
116 |
+
return jsonify({"audio_url": audio_url})
|
117 |
+
|
118 |
+
except Exception as e:
|
119 |
+
app.logger.error(f"Audio Generation Error: {e}")
|
120 |
return jsonify({"error": str(e)}), 500
|
121 |
|
122 |
+
# Serve the main index.html file
|
123 |
@app.route('/')
|
124 |
def serve_index():
|
125 |
return send_from_directory('static', 'index.html')
|
126 |
|
127 |
+
# Serve other static files (CSS, JS, audio files)
|
128 |
@app.route('/<path:path>')
|
129 |
def serve_static(path):
|
130 |
+
# Ensure that only files from 'static' are served
|
131 |
return send_from_directory('static', path)
|
132 |
|
133 |
if __name__ == '__main__':
|
134 |
+
# Run the Flask app
|
135 |
+
# debug=True allows automatic reloading on code changes and provides more detailed error messages
|
136 |
app.run(host="0.0.0.0", port=7860)
|