sonuprasad23 commited on
Commit
869a53e
·
1 Parent(s): e3e858a

Fixed some bugs

Browse files
Files changed (2) hide show
  1. app.py +111 -69
  2. requirements.txt +2 -1
app.py CHANGED
@@ -1,108 +1,150 @@
 
 
 
1
  from flask import Flask, request, jsonify, Response
2
  from flask_cors import CORS
3
  import google.genai as genai
4
- import json
5
- import os
6
- import threading
 
7
 
 
8
  app = Flask(__name__)
9
 
10
- frontend_url = os.environ.get("FRONTEND_URL", "*")
11
- CORS(app, resources={r"/*": {"origins": frontend_url}})
 
 
 
 
12
 
 
 
13
  API_KEY = os.environ.get("GEMINI_API_KEY")
 
 
14
 
15
- gemini_client = None
16
- client_lock = threading.Lock()
17
- active_chats = {}
18
- chat_lock = threading.Lock()
19
-
20
- def get_gemini_client():
21
- global gemini_client
22
- with client_lock:
23
- if gemini_client is None:
24
- if not API_KEY:
25
- print("❌ FATAL: GEMINI_API_KEY environment variable not found.")
26
- raise ValueError("API Key not configured.")
27
- try:
28
- print("🔵 Initializing Gemini client for the first time...")
29
- gemini_client = genai.Client(api_key=API_KEY)
30
- print("✅ Gemini client initialized successfully.")
31
- except Exception as e:
32
- print(f"❌ FATAL: Error initializing Gemini client: {e}")
33
- gemini_client = "initialization_failed"
34
- return gemini_client
35
 
36
  BIOLOGY_INSTRUCTION = """
37
- You are BioBuddy, an expert, friendly, and patient high school biology tutor. Your goal is to help students prepare for their tests in an engaging and effective way. Your personality is upbeat and you love using simple analogies. Follow these rules: 1. Ask one practice question at a time, starting easy. 2. ALWAYS ask for the user's reasoning. 3. Adapt your tone: be gentle if they are confused, enthusiastic if they are confident. 4. Never say "you're wrong"; gently guide them to the correct answer. 5. After 5 questions, offer to continue or give a summary.
 
 
 
 
 
 
 
38
  """
 
39
  FINANCE_INSTRUCTION = """
40
- You are "FinBot", a clear and patient finance explainer. Your goal is to break down complex financial topics into simple, understandable concepts. Your personality is knowledgeable and neutral. **CRITICAL RULE: You MUST NOT give any financial advice or recommendations.** Your process: 1. Explain the topic simply. 2. Use a real-world analogy. 3. Check for understanding. 4. End EVERY response with this disclaimer: "Disclaimer: This is for educational purposes only and is not financial advice. Consult a qualified professional."
 
 
 
 
 
 
 
41
  """
 
42
  MATERNAL_HEALTH_INSTRUCTION = """
43
- You are "WellMom", a warm, empathetic, and supportive guide for general maternal health information. Your tone is always reassuring and calm. **ABSOLUTE CRITICAL RULE: You are an AI assistant, NOT a doctor or a medical professional. You MUST NEVER provide medical advice or diagnosis.** Your process: 1. Acknowledge the user's question with empathy. 2. Provide clear, general, safe educational information. 3. Keep it simple. 4. End EVERY response with this disclaimer: "**Important Note:** This is not medical advice. Please consult with your doctor for any health concerns."
 
 
 
 
 
 
44
  """
45
- PERSONA_PROMPTS = { "biology": BIOLOGY_INSTRUCTION, "finance": FINANCE_INSTRUCTION, "maternal": MATERNAL_HEALTH_INSTRUCTION }
46
-
47
- @app.route('/')
48
- def health_check():
49
- client = get_gemini_client()
50
- client_status = "Inactive"
51
- if client and client != "initialization_failed":
52
- client_status = "Active"
53
- return jsonify({
54
- "status": "active",
55
- "message": "Welcome to the Shunya AI Backend ✨",
56
- "gemini_api_status": client_status
57
- })
58
 
59
  @app.route('/chat', methods=['GET'])
60
  def chat_endpoint():
61
- client = get_gemini_client()
62
- if not client or client == "initialization_failed":
63
- return jsonify({"error": "Gemini client is not initialized. Check backend server logs and API key configuration."}), 503
64
-
65
  session_id = request.args.get('sessionId')
66
  persona_id = request.args.get('personaId')
67
  user_message = request.args.get('message')
68
 
69
  if not all([session_id, persona_id, user_message]):
70
- return jsonify({"error": "Missing sessionId, personaId, or message"}), 400
71
 
72
- with chat_lock:
73
- if session_id not in active_chats:
74
- if persona_id not in PERSONA_PROMPTS:
75
- return jsonify({"error": "Invalid personaId"}), 400
76
-
77
- system_instruction = PERSONA_PROMPTS[persona_id]
78
- print(f"INFO: Creating new chat session {session_id} for persona {persona_id}")
79
-
 
80
  active_chats[session_id] = client.chats.create(
81
- model="gemini-2.5-flash",
 
82
  config=genai.types.GenerateContentConfig(
83
- system_instruction=system_instruction,
84
- temperature=0.7
85
  )
86
  )
 
 
87
 
88
- def stream_response():
89
  try:
90
  chat_session = active_chats[session_id]
91
- response_stream = chat_session.send_message_stream(user_message)
92
- for chunk in response_stream:
93
- if chunk.text:
94
- yield f"data: {json.dumps({'chunk': chunk.text})}\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
  yield f"data: [DONE]\n\n"
96
  except Exception as e:
97
- print(f"ERROR: Stream failed for session {session_id}: {e}")
98
- yield f"data: {json.dumps({'error': 'An error occurred while generating the response.'})}\n\n"
99
 
100
- return Response(stream_response(), mimetype='text/event-stream')
101
 
102
  if __name__ == '__main__':
 
 
103
  port = int(os.environ.get("PORT", 5001))
104
- print("🚀 Shunya Backend is launching...")
105
- print(f"🔗 CORS enabled for: {frontend_url}")
106
- print(f"📡 Listening on port: {port}")
107
- get_gemini_client()
108
  app.run(host='0.0.0.0', port=port, debug=False)
 
1
+ import os
2
+ import json
3
+ import time
4
  from flask import Flask, request, jsonify, Response
5
  from flask_cors import CORS
6
  import google.genai as genai
7
+ from dotenv import load_dotenv
8
+
9
+ # Load environment variables from a .env file for local development
10
+ load_dotenv()
11
 
12
+ # --- Application Setup ---
13
  app = Flask(__name__)
14
 
15
+ # --- Environment Configuration & Security ---
16
+
17
+ # Securely load the frontend URL from environment variables for CORS policy.
18
+ # This ensures only your official frontend can communicate with this backend.
19
+ FRONTEND_URL = os.environ.get("FRONTEND_URL", "http://localhost:5173")
20
+ CORS(app, resources={r"/chat": {"origins": FRONTEND_URL}})
21
 
22
+ # Securely load the Gemini API Key from environment secrets.
23
+ # This is the industry standard for handling sensitive credentials.
24
  API_KEY = os.environ.get("GEMINI_API_KEY")
25
+ if not API_KEY:
26
+ raise ValueError("FATAL: GEMINI_API_KEY environment variable is not set.")
27
 
28
+ # --- Gemini API Client Initialization ---
29
+ try:
30
+ client = genai.Client(api_key=API_KEY)
31
+ except Exception as e:
32
+ print(f"CRITICAL: Failed to initialize Gemini API client: {e}")
33
+ raise
34
+
35
+ # --- Persona System Instructions ---
36
+ CODING_INSTRUCTION = """
37
+ You are "Deva", an expert AI programming assistant. You are a master of all software development domains.
38
+ **CRITICAL RULE: You MUST ALWAYS respond in a structured JSON format.**
39
+ Your entire response must be a single, valid JSON object with three keys: "explanation", "language", and "code".
40
+ - "explanation": (string) Your text-based explanation, written in Markdown. This should be detailed and easy to understand. Use bullet points for steps.
41
+ - "language": (string) The programming language of the code (e.g., "python", "javascript"). If no code, use an empty string.
42
+ - "code": (string) ONLY the raw code block. No markdown fences. If no code, use an empty string.
43
+ """
 
 
 
 
44
 
45
  BIOLOGY_INSTRUCTION = """
46
+ You are "BioBuddy", an expert, friendly, and patient high school biology tutor. Your goal is to help students prepare for their tests in an engaging and effective way.
47
+ Your personality is upbeat, encouraging, and you love using simple analogies to explain complex topics.
48
+ Strictly follow this process for the conversation:
49
+ 1. After the user tells you the topic, generate one practice question at a time. Start with easier, foundational questions and gradually increase the difficulty if they answer correctly.
50
+ 2. After the student gives their answer, ALWAYS ask them to explain their reasoning.
51
+ 3. TONE ADAPTATION: Pay close attention to the user's language. If they express confusion, immediately adopt a very gentle, patient, and supportive tone. If they are confident, be enthusiastic.
52
+ 4. After they explain their reasoning, provide feedback. If they are incorrect, NEVER say "you're wrong." Instead, gently guide them.
53
+ 5. After 5 questions, ask if they want to continue or get a summary.
54
  """
55
+
56
  FINANCE_INSTRUCTION = """
57
+ You are "FinBot", a clear and patient finance explainer. Your goal is to break down complex financial topics into simple, understandable concepts for beginners. Your personality is like a helpful teacher: knowledgeable, neutral, and focused on education.
58
+ **CRITICAL RULE: You are NOT a financial advisor. You MUST NOT give any financial advice or investment recommendations.**
59
+ Your process:
60
+ 1. When a user asks about a financial topic, provide a simple, direct explanation.
61
+ 2. Use a real-world analogy to clarify the concept.
62
+ 3. After explaining, ask a clarifying question to check for understanding.
63
+ 4. At the end of EVERY response, you MUST include this disclaimer:
64
+ "Disclaimer: I am an AI assistant and this information is for educational purposes only. It is not financial advice. You should consult with a qualified financial professional before making any financial decisions."
65
  """
66
+
67
  MATERNAL_HEALTH_INSTRUCTION = """
68
+ You are "WellMom", a warm, empathetic, and supportive guide providing general educational information on maternal health topics. Your tone is always reassuring, calm, and caring.
69
+ **ABSOLUTE CRITICAL RULE: You are an AI assistant, NOT a doctor or a medical professional. You MUST NEVER provide medical advice, diagnosis, or treatment plans.**
70
+ Your process:
71
+ 1. Acknowledge the user's question with empathy.
72
+ 2. Provide clear, general, and safe educational information based on well-known, standard knowledge.
73
+ 3. At the end of EVERY SINGLE response, you MUST include this clear and prominent disclaimer:
74
+ "**Important Note:** This information is for educational purposes only and is not a substitute for professional medical advice. Please consult with your doctor or a qualified healthcare provider for any health concerns."
75
  """
76
+
77
+ PERSONA_CONFIG = {
78
+ "coding": { "system_instruction": CODING_INSTRUCTION, "model_name": "gemini-2.5-pro", "response_mime_type": "application/json", "temperature": 0.3 },
79
+ "biology": { "system_instruction": BIOLOGY_INSTRUCTION, "model_name": "gemini-2.5-flash", "temperature": 0.7 },
80
+ "finance": { "system_instruction": FINANCE_INSTRUCTION, "model_name": "gemini-2.5-flash", "temperature": 0.7 },
81
+ "maternal": { "system_instruction": MATERNAL_HEALTH_INSTRUCTION, "model_name": "gemini-2.5-flash", "temperature": 0.8 }
82
+ }
83
+
84
+ active_chats = {}
 
 
 
 
85
 
86
  @app.route('/chat', methods=['GET'])
87
  def chat_endpoint():
 
 
 
 
88
  session_id = request.args.get('sessionId')
89
  persona_id = request.args.get('personaId')
90
  user_message = request.args.get('message')
91
 
92
  if not all([session_id, persona_id, user_message]):
93
+ return jsonify({"error": "Missing required query parameters"}), 400
94
 
95
+ if session_id not in active_chats:
96
+ if persona_id not in PERSONA_CONFIG:
97
+ return jsonify({"error": f"Invalid personaId"}), 400
98
+ config = PERSONA_CONFIG[persona_id]
99
+ primed_history = [
100
+ {'role': 'user', 'parts': [{'text': config["system_instruction"]}]},
101
+ {'role': 'model', 'parts': [{'text': 'Understood. I am ready to begin.'}]}
102
+ ]
103
+ try:
104
  active_chats[session_id] = client.chats.create(
105
+ model=config["model_name"],
106
+ history=primed_history,
107
  config=genai.types.GenerateContentConfig(
108
+ temperature=config["temperature"],
109
+ response_mime_type=config.get("response_mime_type")
110
  )
111
  )
112
+ except Exception as e:
113
+ return jsonify({"error": "Could not create new chat session."}), 500
114
 
115
+ def event_stream():
116
  try:
117
  chat_session = active_chats[session_id]
118
+
119
+ if persona_id == 'coding':
120
+ response = chat_session.send_message(user_message)
121
+ yield f"data: {json.dumps({'status': 'Analyzing request...'})}\n\n"
122
+ time.sleep(0.5) # Simulate thinking
123
+
124
+ full_response_text = response.text
125
+ yield f"data: {json.dumps({'status': 'Finalizing response...'})}\n\n"
126
+ time.sleep(0.5)
127
+
128
+ # Send the entire structured content at once
129
+ data = json.loads(full_response_text)
130
+ yield f"data: {json.dumps({'content': data})}\n\n"
131
+ else:
132
+ # Stream responses for non-coding personas
133
+ response_stream = chat_session.send_message_stream(user_message)
134
+ for chunk in response_stream:
135
+ if chunk.text:
136
+ yield f"data: {json.dumps({'explanation_chunk': chunk.text})}\n\n"
137
+
138
  yield f"data: [DONE]\n\n"
139
  except Exception as e:
140
+ print(f"Error during message stream: {e}")
141
+ yield f"data: {json.dumps({'error': 'An error occurred.'})}\n\n"
142
 
143
+ return Response(event_stream(), mimetype='text/event-stream')
144
 
145
  if __name__ == '__main__':
146
+ # This block is for local development only.
147
+ # The production server (Gunicorn) is specified in the Dockerfile.
148
  port = int(os.environ.get("PORT", 5001))
149
+ # Setting debug=False is a best practice for a production-like test environment.
 
 
 
150
  app.run(host='0.0.0.0', port=port, debug=False)
requirements.txt CHANGED
@@ -1,4 +1,5 @@
1
  Flask
2
  Flask-Cors
3
  google-genai
4
- gunicorn
 
 
1
  Flask
2
  Flask-Cors
3
  google-genai
4
+ gunicorn
5
+ python-dotenv