Commit
·
e141e7c
1
Parent(s):
2f2f8a0
DO I remember why am I doing this ? Fuck no. but am I going to do this FUCK yes
Browse files
main.py
CHANGED
|
@@ -50,7 +50,30 @@ TABLE_DESCRIPTIONS = """
|
|
| 50 |
embeddings = None
|
| 51 |
vector_store = None
|
| 52 |
client_openai = OpenAI(api_key=OPENAI_API_KEY)
|
| 53 |
-
client_elevenlabs =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
|
| 55 |
|
| 56 |
# --- LIFESPAN ---
|
|
@@ -71,6 +94,13 @@ async def lifespan(app: FastAPI):
|
|
| 71 |
yield
|
| 72 |
logging.info("Shutting down.")
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
app = FastAPI(lifespan=lifespan)
|
| 76 |
|
|
@@ -90,16 +120,14 @@ You are a query analysis agent. Transform the user's query into a precise search
|
|
| 90 |
|
| 91 |
ANSWER_SYSTEM_PROMPT = """
|
| 92 |
You are an expert AI assistant for a premier real estate developer.
|
| 93 |
-
##
|
| 94 |
-
-
|
| 95 |
-
|
| 96 |
-
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
2. **Fact-Based Answers:** Use the provided CONTEXT to answer the user's question. If the context is empty, use your Core Business Knowledge.
|
| 102 |
-
3. **Stay on Topic:** Only answer questions related to real estate.
|
| 103 |
"""
|
| 104 |
|
| 105 |
|
|
@@ -136,28 +164,48 @@ def transcribe_audio(audio_path: str, audio_bytes: bytes) -> str:
|
|
| 136 |
return ""
|
| 137 |
return ""
|
| 138 |
|
| 139 |
-
|
| 140 |
def generate_elevenlabs_sync(text: str, voice: str) -> bytes:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
for attempt in range(3):
|
| 142 |
try:
|
| 143 |
-
|
|
|
|
|
|
|
| 144 |
text=text,
|
| 145 |
voice=voice,
|
| 146 |
model="eleven_multilingual_v2",
|
| 147 |
output_format="mp3_44100_128"
|
| 148 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
except Exception as e:
|
| 150 |
-
logging.error(f"ElevenLabs error (attempt {attempt+1}): {e}", exc_info=True) # Added exc_info
|
| 151 |
if attempt == 2:
|
| 152 |
return b''
|
| 153 |
return b''
|
|
|
|
| 154 |
|
| 155 |
-
# --- UPDATED formulate_search_plan with logging ---
|
| 156 |
async def formulate_search_plan(user_query: str) -> dict:
|
| 157 |
logging.info(f"Formulating search plan for query: {user_query}")
|
| 158 |
for attempt in range(3):
|
| 159 |
try:
|
| 160 |
-
#
|
| 161 |
formatted_prompt = QUERY_FORMULATION_PROMPT.format(
|
| 162 |
table_descriptions=TABLE_DESCRIPTIONS,
|
| 163 |
user_query=user_query
|
|
@@ -170,7 +218,6 @@ async def formulate_search_plan(user_query: str) -> dict:
|
|
| 170 |
response_format={"type": "json_object"},
|
| 171 |
temperature=0.0
|
| 172 |
)
|
| 173 |
-
# ... rest of the function ...
|
| 174 |
# Log the raw response BEFORE trying to parse
|
| 175 |
raw_response_content = response.choices[0].message.content
|
| 176 |
logging.info(f"Raw Planner LLM response content: {raw_response_content}")
|
|
@@ -188,7 +235,6 @@ async def formulate_search_plan(user_query: str) -> dict:
|
|
| 188 |
# Fallback if loop finishes unexpectedly
|
| 189 |
logging.error("Planner loop finished unexpectedly. Using fallback.")
|
| 190 |
return {"search_query": user_query, "filter_table": None}
|
| 191 |
-
# --- END UPDATED FUNCTION ---
|
| 192 |
|
| 193 |
async def get_agent_response(user_text: str) -> str:
|
| 194 |
for attempt in range(3):
|
|
@@ -273,12 +319,17 @@ async def process_audio(audio_path):
|
|
| 273 |
generate_elevenlabs_sync, agent_response, ELEVENLABS_VOICE_NAME
|
| 274 |
)
|
| 275 |
if not ai_audio_bytes:
|
| 276 |
-
|
|
|
|
|
|
|
|
|
|
| 277 |
|
| 278 |
# Save to temp file
|
| 279 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as f:
|
| 280 |
f.write(ai_audio_bytes)
|
| 281 |
out_path = f.name
|
|
|
|
|
|
|
| 282 |
|
| 283 |
return out_path, f"**You:** {user_text}\n\n**AI:** {agent_response}"
|
| 284 |
|
|
@@ -295,7 +346,7 @@ with gr.Blocks(title="Real Estate AI") as demo:
|
|
| 295 |
with gr.Row():
|
| 296 |
inp = gr.Audio(sources=["microphone"], type="filepath", label="Speak")
|
| 297 |
out_audio = gr.Audio(label="AI Response", type="filepath")
|
| 298 |
-
|
| 299 |
out_text = gr.Textbox(label="Conversation", lines=8)
|
| 300 |
|
| 301 |
inp.change(process_audio, inp, [out_audio, out_text])
|
|
|
|
| 50 |
embeddings = None
|
| 51 |
vector_store = None
|
| 52 |
client_openai = OpenAI(api_key=OPENAI_API_KEY)
|
| 53 |
+
client_elevenlabs = None # Initialize as None first
|
| 54 |
+
|
| 55 |
+
# --- ADDED: DETAILED ELEVENLABS INITIALIZATION LOGGING ---
|
| 56 |
+
try:
|
| 57 |
+
# Log the key (partially) to verify it's being read
|
| 58 |
+
key_preview = ELEVENLABS_API_KEY[:5] + "..." + ELEVENLABS_API_KEY[-4:] if ELEVENLABS_API_KEY and len(ELEVENLABS_API_KEY) > 9 else "None or too short"
|
| 59 |
+
logging.info(f"Attempting to initialize ElevenLabs client with key: {key_preview}")
|
| 60 |
+
|
| 61 |
+
# Ensure key is not None or empty before initializing
|
| 62 |
+
if not ELEVENLABS_API_KEY:
|
| 63 |
+
raise ValueError("ELEVENLABS_API_KEY environment variable not set or empty.")
|
| 64 |
+
|
| 65 |
+
client_elevenlabs = ElevenLabs(api_key=ELEVENLABS_API_KEY)
|
| 66 |
+
logging.info(f"Initialized ElevenLabs client object. Type: {type(client_elevenlabs)}")
|
| 67 |
+
|
| 68 |
+
# Try accessing a simple attribute or method to confirm initialization
|
| 69 |
+
# Note: This might make a network call during startup
|
| 70 |
+
voices = client_elevenlabs.voices.get_all()
|
| 71 |
+
logging.info(f"Successfully fetched {len(voices.voices)} voices from ElevenLabs.")
|
| 72 |
+
|
| 73 |
+
except Exception as e:
|
| 74 |
+
logging.error(f"Failed to initialize ElevenLabs client or fetch voices: {e}", exc_info=True)
|
| 75 |
+
client_elevenlabs = None # Ensure it's None if init failed
|
| 76 |
+
# --- END ADDED LOGGING ---
|
| 77 |
|
| 78 |
|
| 79 |
# --- LIFESPAN ---
|
|
|
|
| 94 |
yield
|
| 95 |
logging.info("Shutting down.")
|
| 96 |
|
| 97 |
+
# --- ADDED: LIBRARY VERSION LOGGING ---
|
| 98 |
+
try:
|
| 99 |
+
import elevenlabs
|
| 100 |
+
logging.info(f"Found elevenlabs library version: {elevenlabs.__version__}")
|
| 101 |
+
except ImportError:
|
| 102 |
+
logging.error("Could not import elevenlabs library!")
|
| 103 |
+
# --- END ADDED LOGGING ---
|
| 104 |
|
| 105 |
app = FastAPI(lifespan=lifespan)
|
| 106 |
|
|
|
|
| 120 |
|
| 121 |
ANSWER_SYSTEM_PROMPT = """
|
| 122 |
You are an expert AI assistant for a premier real estate developer.
|
| 123 |
+
## CORE KNOWLEDGE
|
| 124 |
+
- Cities: Pune, Mumbai, Bengaluru, Delhi, Chennai, Hyderabad, Goa, Gurgaon, Kolkata.
|
| 125 |
+
- Properties: Luxury apartments, villas, commercial.
|
| 126 |
+
- Budget: 45 lakhs to 5 crores.
|
| 127 |
+
## RULES
|
| 128 |
+
1. Match user language (Hinglish → Hinglish, English → English).
|
| 129 |
+
2. Use CONTEXT if available, else use core knowledge.
|
| 130 |
+
3. Only answer real estate questions.
|
|
|
|
|
|
|
| 131 |
"""
|
| 132 |
|
| 133 |
|
|
|
|
| 164 |
return ""
|
| 165 |
return ""
|
| 166 |
|
| 167 |
+
# --- UPDATED generate_elevenlabs_sync with check ---
|
| 168 |
def generate_elevenlabs_sync(text: str, voice: str) -> bytes:
|
| 169 |
+
# --- ADDED THIS CHECK ---
|
| 170 |
+
if client_elevenlabs is None:
|
| 171 |
+
logging.error("ElevenLabs client is not initialized. Cannot generate audio.")
|
| 172 |
+
return b''
|
| 173 |
+
# --- END ADDED CHECK ---
|
| 174 |
+
|
| 175 |
for attempt in range(3):
|
| 176 |
try:
|
| 177 |
+
# This call might still fail if init succeeded but key is bad at runtime
|
| 178 |
+
logging.info(f"Calling ElevenLabs generate for voice '{voice}'...")
|
| 179 |
+
audio_data = client_elevenlabs.generate(
|
| 180 |
text=text,
|
| 181 |
voice=voice,
|
| 182 |
model="eleven_multilingual_v2",
|
| 183 |
output_format="mp3_44100_128"
|
| 184 |
)
|
| 185 |
+
# Check if generate returns bytes directly or needs iteration (depends on exact version/method)
|
| 186 |
+
if isinstance(audio_data, bytes):
|
| 187 |
+
logging.info(f"ElevenLabs generate returned {len(audio_data)} bytes.")
|
| 188 |
+
return audio_data
|
| 189 |
+
else:
|
| 190 |
+
# Handle streaming iterator if necessary
|
| 191 |
+
chunks = b""
|
| 192 |
+
for chunk in audio_data:
|
| 193 |
+
chunks += chunk
|
| 194 |
+
logging.info(f"ElevenLabs generate streamed {len(chunks)} bytes.")
|
| 195 |
+
return chunks
|
| 196 |
+
|
| 197 |
except Exception as e:
|
| 198 |
+
logging.error(f"ElevenLabs error during generate (attempt {attempt+1}): {e}", exc_info=True) # Added exc_info
|
| 199 |
if attempt == 2:
|
| 200 |
return b''
|
| 201 |
return b''
|
| 202 |
+
# --- END UPDATED FUNCTION ---
|
| 203 |
|
|
|
|
| 204 |
async def formulate_search_plan(user_query: str) -> dict:
|
| 205 |
logging.info(f"Formulating search plan for query: {user_query}")
|
| 206 |
for attempt in range(3):
|
| 207 |
try:
|
| 208 |
+
# Format the prompt here with BOTH variables
|
| 209 |
formatted_prompt = QUERY_FORMULATION_PROMPT.format(
|
| 210 |
table_descriptions=TABLE_DESCRIPTIONS,
|
| 211 |
user_query=user_query
|
|
|
|
| 218 |
response_format={"type": "json_object"},
|
| 219 |
temperature=0.0
|
| 220 |
)
|
|
|
|
| 221 |
# Log the raw response BEFORE trying to parse
|
| 222 |
raw_response_content = response.choices[0].message.content
|
| 223 |
logging.info(f"Raw Planner LLM response content: {raw_response_content}")
|
|
|
|
| 235 |
# Fallback if loop finishes unexpectedly
|
| 236 |
logging.error("Planner loop finished unexpectedly. Using fallback.")
|
| 237 |
return {"search_query": user_query, "filter_table": None}
|
|
|
|
| 238 |
|
| 239 |
async def get_agent_response(user_text: str) -> str:
|
| 240 |
for attempt in range(3):
|
|
|
|
| 319 |
generate_elevenlabs_sync, agent_response, ELEVENLABS_VOICE_NAME
|
| 320 |
)
|
| 321 |
if not ai_audio_bytes:
|
| 322 |
+
# Return the text response even if TTS fails
|
| 323 |
+
logging.error("Failed to generate voice. Returning text only.")
|
| 324 |
+
return None, f"**You:** {user_text}\n\n**AI:** {agent_response}\n\n_(Audio generation failed)_"
|
| 325 |
+
|
| 326 |
|
| 327 |
# Save to temp file
|
| 328 |
with tempfile.NamedTemporaryFile(delete=False, suffix=".mp3") as f:
|
| 329 |
f.write(ai_audio_bytes)
|
| 330 |
out_path = f.name
|
| 331 |
+
logging.info(f"Saved generated audio to temp file: {out_path}")
|
| 332 |
+
|
| 333 |
|
| 334 |
return out_path, f"**You:** {user_text}\n\n**AI:** {agent_response}"
|
| 335 |
|
|
|
|
| 346 |
with gr.Row():
|
| 347 |
inp = gr.Audio(sources=["microphone"], type="filepath", label="Speak")
|
| 348 |
out_audio = gr.Audio(label="AI Response", type="filepath")
|
| 349 |
+
|
| 350 |
out_text = gr.Textbox(label="Conversation", lines=8)
|
| 351 |
|
| 352 |
inp.change(process_audio, inp, [out_audio, out_text])
|