Spaces:
Building
Building
Update chat_handler.py
Browse files- chat_handler.py +51 -4
chat_handler.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
"""
|
2 |
-
Flare – Chat Handler (v1.
|
3 |
==========================================
|
4 |
"""
|
5 |
|
@@ -16,6 +16,7 @@ from api_executor import call_api as execute_api
|
|
16 |
from config_provider import ConfigProvider
|
17 |
from validation_engine import validate
|
18 |
from session import session_store, Session
|
|
|
19 |
# ───────────────────────── HELPERS ───────────────────────── #
|
20 |
def _trim_response(raw: str) -> str:
|
21 |
"""
|
@@ -38,6 +39,7 @@ def _safe_intent_parse(raw: str) -> tuple[str, str]:
|
|
38 |
return "", raw
|
39 |
name = m.group(1)
|
40 |
tail = raw[m.end():]
|
|
|
41 |
return name, tail
|
42 |
|
43 |
# ───────────────────────── CONFIG ───────────────────────── #
|
@@ -65,6 +67,8 @@ async def spark_generate(s: Session, prompt: str, user_msg: str) -> str:
|
|
65 |
}
|
66 |
|
67 |
log(f"🚀 Calling Spark for session {s.session_id[:8]}...")
|
|
|
|
|
68 |
async with httpx.AsyncClient(timeout=60) as client:
|
69 |
response = await client.post(SPARK_URL + "/generate", json=payload)
|
70 |
response.raise_for_status()
|
@@ -130,6 +134,9 @@ async def chat(body: ChatRequest, x_session_id: str = Header(...)):
|
|
130 |
if not user_input:
|
131 |
raise HTTPException(400, "Empty message")
|
132 |
|
|
|
|
|
|
|
133 |
session.add_turn("user", user_input)
|
134 |
|
135 |
# Get project config
|
@@ -143,8 +150,10 @@ async def chat(body: ChatRequest, x_session_id: str = Header(...)):
|
|
143 |
|
144 |
# Handle based on state
|
145 |
if session.state == "await_param":
|
|
|
146 |
answer = await _handle_parameter_followup(session, user_input, version)
|
147 |
else:
|
|
|
148 |
answer = await _handle_new_message(session, user_input, version)
|
149 |
|
150 |
session.add_turn("assistant", answer)
|
@@ -175,11 +184,13 @@ async def _handle_new_message(session: Session, user_input: str, version) -> str
|
|
175 |
|
176 |
# Empty response fallback
|
177 |
if not raw:
|
|
|
178 |
return "Üzgünüm, mesajınızı anlayamadım. Lütfen tekrar dener misiniz?"
|
179 |
|
180 |
# Check for intent
|
181 |
if not raw.startswith("#DETECTED_INTENT"):
|
182 |
# Small talk response
|
|
|
183 |
return _trim_response(raw)
|
184 |
|
185 |
# Parse intent
|
@@ -187,19 +198,26 @@ async def _handle_new_message(session: Session, user_input: str, version) -> str
|
|
187 |
|
188 |
# Validate intent
|
189 |
if intent_name not in ALLOWED_INTENTS:
|
|
|
190 |
return _trim_response(tail) if tail else "Size nasıl yardımcı olabilirim?"
|
191 |
|
192 |
# Short message guard (less than 3 words usually means incomplete request)
|
193 |
if len(user_input.split()) < 3 and intent_name != "flight-info":
|
|
|
194 |
return _trim_response(tail) if tail else "Lütfen talebinizi biraz daha detaylandırır mısınız?"
|
195 |
|
196 |
# Find intent config
|
197 |
intent_config = next((i for i in version.intents if i.name == intent_name), None)
|
198 |
if not intent_config:
|
|
|
199 |
return "Üzgünüm, bu işlemi gerçekleştiremiyorum."
|
200 |
|
201 |
# Set intent in session
|
202 |
session.last_intent = intent_name
|
|
|
|
|
|
|
|
|
203 |
|
204 |
# Extract parameters
|
205 |
return await _extract_parameters(session, intent_config, user_input)
|
@@ -207,23 +225,29 @@ async def _handle_new_message(session: Session, user_input: str, version) -> str
|
|
207 |
async def _handle_parameter_followup(session: Session, user_input: str, version) -> str:
|
208 |
"""Handle parameter collection followup"""
|
209 |
if not session.last_intent:
|
|
|
210 |
session.reset_flow()
|
211 |
return "Üzgünüm, hangi işlem için bilgi istediğimi unuttum. Baştan başlayalım."
|
212 |
|
213 |
# Get intent config
|
214 |
intent_config = next((i for i in version.intents if i.name == session.last_intent), None)
|
215 |
if not intent_config:
|
|
|
216 |
session.reset_flow()
|
217 |
return "Bir hata oluştu. Lütfen tekrar deneyin."
|
218 |
|
219 |
# Try to extract missing parameters
|
220 |
missing = session.awaiting_parameters
|
|
|
|
|
221 |
prompt = build_parameter_prompt(intent_config, missing, user_input, session.chat_history)
|
222 |
raw = await spark_generate(session, prompt, user_input)
|
223 |
|
224 |
if not raw.startswith("#PARAMETERS:"):
|
225 |
# Increment miss count
|
226 |
session.missing_ask_count += 1
|
|
|
|
|
227 |
if session.missing_ask_count >= 3:
|
228 |
session.reset_flow()
|
229 |
return "Üzgünüm, istediğiniz bilgileri anlayamadım. Başka bir konuda yardımcı olabilir miyim?"
|
@@ -232,16 +256,20 @@ async def _handle_parameter_followup(session: Session, user_input: str, version)
|
|
232 |
# Process parameters
|
233 |
success = _process_parameters(session, intent_config, raw)
|
234 |
if not success:
|
|
|
235 |
return "Girdiğiniz bilgilerde bir hata var. Lütfen kontrol edip tekrar deneyin."
|
236 |
|
237 |
# Check if we have all required parameters
|
238 |
missing = _get_missing_parameters(session, intent_config)
|
|
|
|
|
239 |
if missing:
|
240 |
session.awaiting_parameters = missing
|
241 |
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
242 |
return f"{param.caption} bilgisini alabilir miyim?"
|
243 |
|
244 |
# All parameters collected, call API
|
|
|
245 |
session.state = "call_api"
|
246 |
return await _execute_api_call(session, intent_config)
|
247 |
|
@@ -249,8 +277,11 @@ async def _handle_parameter_followup(session: Session, user_input: str, version)
|
|
249 |
async def _extract_parameters(session: Session, intent_config, user_input: str) -> str:
|
250 |
"""Extract parameters from user input"""
|
251 |
missing = _get_missing_parameters(session, intent_config)
|
|
|
|
|
252 |
if not missing:
|
253 |
# All parameters already available
|
|
|
254 |
return await _execute_api_call(session, intent_config)
|
255 |
|
256 |
# Build parameter extraction prompt
|
@@ -261,6 +292,9 @@ async def _extract_parameters(session: Session, intent_config, user_input: str)
|
|
261 |
success = _process_parameters(session, intent_config, raw)
|
262 |
if success:
|
263 |
missing = _get_missing_parameters(session, intent_config)
|
|
|
|
|
|
|
264 |
|
265 |
if missing:
|
266 |
# Still missing parameters
|
@@ -268,25 +302,32 @@ async def _extract_parameters(session: Session, intent_config, user_input: str)
|
|
268 |
session.awaiting_parameters = missing
|
269 |
session.missing_ask_count = 0
|
270 |
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
|
|
271 |
return f"{param.caption} bilgisini alabilir miyim?"
|
272 |
|
273 |
# All parameters collected
|
|
|
274 |
return await _execute_api_call(session, intent_config)
|
275 |
|
276 |
def _get_missing_parameters(session: Session, intent_config) -> List[str]:
|
277 |
"""Get list of missing required parameters"""
|
278 |
-
|
279 |
p.name for p in intent_config.parameters
|
280 |
if p.required and p.variable_name not in session.variables
|
281 |
]
|
|
|
|
|
282 |
|
283 |
def _process_parameters(session: Session, intent_config, raw: str) -> bool:
|
284 |
"""Process parameter extraction response"""
|
285 |
try:
|
286 |
json_str = raw[len("#PARAMETERS:"):]
|
|
|
287 |
data = json.loads(json_str)
|
288 |
|
289 |
extracted = data.get("extracted", [])
|
|
|
|
|
290 |
any_valid = False
|
291 |
|
292 |
for param_data in extracted:
|
@@ -294,6 +335,7 @@ def _process_parameters(session: Session, intent_config, raw: str) -> bool:
|
|
294 |
param_value = param_data.get("value")
|
295 |
|
296 |
if not param_name or not param_value:
|
|
|
297 |
continue
|
298 |
|
299 |
# Find parameter config
|
@@ -302,13 +344,14 @@ def _process_parameters(session: Session, intent_config, raw: str) -> bool:
|
|
302 |
None
|
303 |
)
|
304 |
if not param_config:
|
|
|
305 |
continue
|
306 |
|
307 |
# Validate parameter
|
308 |
if validate(str(param_value), param_config):
|
309 |
session.variables[param_config.variable_name] = str(param_value)
|
310 |
any_valid = True
|
311 |
-
log(f"✅ Extracted {param_name}={param_value}")
|
312 |
else:
|
313 |
log(f"❌ Invalid {param_name}={param_value}")
|
314 |
|
@@ -327,14 +370,17 @@ async def _execute_api_call(session: Session, intent_config) -> str:
|
|
327 |
api_config = cfg.get_api(api_name)
|
328 |
|
329 |
if not api_config:
|
|
|
330 |
session.reset_flow()
|
331 |
return intent_config.fallback_error_prompt or "İşlem başarısız oldu."
|
332 |
|
333 |
log(f"📡 Calling API: {api_name}")
|
|
|
334 |
|
335 |
# Execute API call with session
|
336 |
-
response = execute_api(api_config, session)
|
337 |
api_json = response.json()
|
|
|
338 |
|
339 |
# Humanize response
|
340 |
session.state = "humanize"
|
@@ -351,6 +397,7 @@ async def _execute_api_call(session: Session, intent_config) -> str:
|
|
351 |
return f"İşlem tamamlandı: {api_json}"
|
352 |
|
353 |
except requests.exceptions.Timeout:
|
|
|
354 |
session.reset_flow()
|
355 |
return intent_config.fallback_timeout_prompt or "İşlem zaman aşımına uğradı."
|
356 |
except Exception as e:
|
|
|
1 |
"""
|
2 |
+
Flare – Chat Handler (v1.6 · detaylı log)
|
3 |
==========================================
|
4 |
"""
|
5 |
|
|
|
16 |
from config_provider import ConfigProvider
|
17 |
from validation_engine import validate
|
18 |
from session import session_store, Session
|
19 |
+
|
20 |
# ───────────────────────── HELPERS ───────────────────────── #
|
21 |
def _trim_response(raw: str) -> str:
|
22 |
"""
|
|
|
39 |
return "", raw
|
40 |
name = m.group(1)
|
41 |
tail = raw[m.end():]
|
42 |
+
log(f"🎯 Parsed intent: {name}")
|
43 |
return name, tail
|
44 |
|
45 |
# ───────────────────────── CONFIG ───────────────────────── #
|
|
|
67 |
}
|
68 |
|
69 |
log(f"🚀 Calling Spark for session {s.session_id[:8]}...")
|
70 |
+
log(f"📋 Prompt preview (first 200 chars): {prompt[:200]}...")
|
71 |
+
|
72 |
async with httpx.AsyncClient(timeout=60) as client:
|
73 |
response = await client.post(SPARK_URL + "/generate", json=payload)
|
74 |
response.raise_for_status()
|
|
|
134 |
if not user_input:
|
135 |
raise HTTPException(400, "Empty message")
|
136 |
|
137 |
+
log(f"💬 User input: {user_input}")
|
138 |
+
log(f"📊 Session state: {session.state}, last_intent: {session.last_intent}")
|
139 |
+
|
140 |
session.add_turn("user", user_input)
|
141 |
|
142 |
# Get project config
|
|
|
150 |
|
151 |
# Handle based on state
|
152 |
if session.state == "await_param":
|
153 |
+
log(f"🔄 Handling parameter followup for missing: {session.awaiting_parameters}")
|
154 |
answer = await _handle_parameter_followup(session, user_input, version)
|
155 |
else:
|
156 |
+
log("🆕 Handling new message")
|
157 |
answer = await _handle_new_message(session, user_input, version)
|
158 |
|
159 |
session.add_turn("assistant", answer)
|
|
|
184 |
|
185 |
# Empty response fallback
|
186 |
if not raw:
|
187 |
+
log("⚠️ Empty response from Spark")
|
188 |
return "Üzgünüm, mesajınızı anlayamadım. Lütfen tekrar dener misiniz?"
|
189 |
|
190 |
# Check for intent
|
191 |
if not raw.startswith("#DETECTED_INTENT"):
|
192 |
# Small talk response
|
193 |
+
log("💬 No intent detected, returning small talk")
|
194 |
return _trim_response(raw)
|
195 |
|
196 |
# Parse intent
|
|
|
198 |
|
199 |
# Validate intent
|
200 |
if intent_name not in ALLOWED_INTENTS:
|
201 |
+
log(f"⚠️ Invalid intent: {intent_name}")
|
202 |
return _trim_response(tail) if tail else "Size nasıl yardımcı olabilirim?"
|
203 |
|
204 |
# Short message guard (less than 3 words usually means incomplete request)
|
205 |
if len(user_input.split()) < 3 and intent_name != "flight-info":
|
206 |
+
log(f"⚠️ Message too short ({len(user_input.split())} words) for intent {intent_name}")
|
207 |
return _trim_response(tail) if tail else "Lütfen talebinizi biraz daha detaylandırır mısınız?"
|
208 |
|
209 |
# Find intent config
|
210 |
intent_config = next((i for i in version.intents if i.name == intent_name), None)
|
211 |
if not intent_config:
|
212 |
+
log(f"❌ Intent config not found for: {intent_name}")
|
213 |
return "Üzgünüm, bu işlemi gerçekleştiremiyorum."
|
214 |
|
215 |
# Set intent in session
|
216 |
session.last_intent = intent_name
|
217 |
+
log(f"✅ Intent set: {intent_name}")
|
218 |
+
|
219 |
+
# Log intent parameters
|
220 |
+
log(f"📋 Intent parameters: {[p.name for p in intent_config.parameters]}")
|
221 |
|
222 |
# Extract parameters
|
223 |
return await _extract_parameters(session, intent_config, user_input)
|
|
|
225 |
async def _handle_parameter_followup(session: Session, user_input: str, version) -> str:
|
226 |
"""Handle parameter collection followup"""
|
227 |
if not session.last_intent:
|
228 |
+
log("⚠️ No last intent in session")
|
229 |
session.reset_flow()
|
230 |
return "Üzgünüm, hangi işlem için bilgi istediğimi unuttum. Baştan başlayalım."
|
231 |
|
232 |
# Get intent config
|
233 |
intent_config = next((i for i in version.intents if i.name == session.last_intent), None)
|
234 |
if not intent_config:
|
235 |
+
log(f"❌ Intent config not found for: {session.last_intent}")
|
236 |
session.reset_flow()
|
237 |
return "Bir hata oluştu. Lütfen tekrar deneyin."
|
238 |
|
239 |
# Try to extract missing parameters
|
240 |
missing = session.awaiting_parameters
|
241 |
+
log(f"🔍 Trying to extract missing params: {missing}")
|
242 |
+
|
243 |
prompt = build_parameter_prompt(intent_config, missing, user_input, session.chat_history)
|
244 |
raw = await spark_generate(session, prompt, user_input)
|
245 |
|
246 |
if not raw.startswith("#PARAMETERS:"):
|
247 |
# Increment miss count
|
248 |
session.missing_ask_count += 1
|
249 |
+
log(f"⚠️ No parameters extracted, miss count: {session.missing_ask_count}")
|
250 |
+
|
251 |
if session.missing_ask_count >= 3:
|
252 |
session.reset_flow()
|
253 |
return "Üzgünüm, istediğiniz bilgileri anlayamadım. Başka bir konuda yardımcı olabilir miyim?"
|
|
|
256 |
# Process parameters
|
257 |
success = _process_parameters(session, intent_config, raw)
|
258 |
if not success:
|
259 |
+
log("❌ Parameter processing failed")
|
260 |
return "Girdiğiniz bilgilerde bir hata var. Lütfen kontrol edip tekrar deneyin."
|
261 |
|
262 |
# Check if we have all required parameters
|
263 |
missing = _get_missing_parameters(session, intent_config)
|
264 |
+
log(f"📊 Still missing params: {missing}")
|
265 |
+
|
266 |
if missing:
|
267 |
session.awaiting_parameters = missing
|
268 |
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
269 |
return f"{param.caption} bilgisini alabilir miyim?"
|
270 |
|
271 |
# All parameters collected, call API
|
272 |
+
log("✅ All parameters collected, calling API")
|
273 |
session.state = "call_api"
|
274 |
return await _execute_api_call(session, intent_config)
|
275 |
|
|
|
277 |
async def _extract_parameters(session: Session, intent_config, user_input: str) -> str:
|
278 |
"""Extract parameters from user input"""
|
279 |
missing = _get_missing_parameters(session, intent_config)
|
280 |
+
log(f"🔍 Missing parameters: {missing}")
|
281 |
+
|
282 |
if not missing:
|
283 |
# All parameters already available
|
284 |
+
log("✅ All parameters already available")
|
285 |
return await _execute_api_call(session, intent_config)
|
286 |
|
287 |
# Build parameter extraction prompt
|
|
|
292 |
success = _process_parameters(session, intent_config, raw)
|
293 |
if success:
|
294 |
missing = _get_missing_parameters(session, intent_config)
|
295 |
+
log(f"📊 After extraction, missing: {missing}")
|
296 |
+
else:
|
297 |
+
log("⚠️ No #PARAMETERS tag in response")
|
298 |
|
299 |
if missing:
|
300 |
# Still missing parameters
|
|
|
302 |
session.awaiting_parameters = missing
|
303 |
session.missing_ask_count = 0
|
304 |
param = next(p for p in intent_config.parameters if p.name == missing[0])
|
305 |
+
log(f"❓ Asking for parameter: {param.name} ({param.caption})")
|
306 |
return f"{param.caption} bilgisini alabilir miyim?"
|
307 |
|
308 |
# All parameters collected
|
309 |
+
log("✅ All parameters collected after extraction")
|
310 |
return await _execute_api_call(session, intent_config)
|
311 |
|
312 |
def _get_missing_parameters(session: Session, intent_config) -> List[str]:
|
313 |
"""Get list of missing required parameters"""
|
314 |
+
missing = [
|
315 |
p.name for p in intent_config.parameters
|
316 |
if p.required and p.variable_name not in session.variables
|
317 |
]
|
318 |
+
log(f"📊 Session variables: {list(session.variables.keys())}")
|
319 |
+
return missing
|
320 |
|
321 |
def _process_parameters(session: Session, intent_config, raw: str) -> bool:
|
322 |
"""Process parameter extraction response"""
|
323 |
try:
|
324 |
json_str = raw[len("#PARAMETERS:"):]
|
325 |
+
log(f"🔍 Parsing parameters JSON: {json_str[:200]}")
|
326 |
data = json.loads(json_str)
|
327 |
|
328 |
extracted = data.get("extracted", [])
|
329 |
+
log(f"📦 Extracted data: {extracted}")
|
330 |
+
|
331 |
any_valid = False
|
332 |
|
333 |
for param_data in extracted:
|
|
|
335 |
param_value = param_data.get("value")
|
336 |
|
337 |
if not param_name or not param_value:
|
338 |
+
log(f"⚠️ Invalid param data: {param_data}")
|
339 |
continue
|
340 |
|
341 |
# Find parameter config
|
|
|
344 |
None
|
345 |
)
|
346 |
if not param_config:
|
347 |
+
log(f"⚠️ Parameter config not found for: {param_name}")
|
348 |
continue
|
349 |
|
350 |
# Validate parameter
|
351 |
if validate(str(param_value), param_config):
|
352 |
session.variables[param_config.variable_name] = str(param_value)
|
353 |
any_valid = True
|
354 |
+
log(f"✅ Extracted {param_name}={param_value} → {param_config.variable_name}")
|
355 |
else:
|
356 |
log(f"❌ Invalid {param_name}={param_value}")
|
357 |
|
|
|
370 |
api_config = cfg.get_api(api_name)
|
371 |
|
372 |
if not api_config:
|
373 |
+
log(f"❌ API config not found: {api_name}")
|
374 |
session.reset_flow()
|
375 |
return intent_config.fallback_error_prompt or "İşlem başarısız oldu."
|
376 |
|
377 |
log(f"📡 Calling API: {api_name}")
|
378 |
+
log(f"📦 API variables: {session.variables}")
|
379 |
|
380 |
# Execute API call with session
|
381 |
+
response = execute_api(api_config, session)
|
382 |
api_json = response.json()
|
383 |
+
log(f"✅ API response: {api_json}")
|
384 |
|
385 |
# Humanize response
|
386 |
session.state = "humanize"
|
|
|
397 |
return f"İşlem tamamlandı: {api_json}"
|
398 |
|
399 |
except requests.exceptions.Timeout:
|
400 |
+
log(f"⏱️ API timeout: {api_name}")
|
401 |
session.reset_flow()
|
402 |
return intent_config.fallback_timeout_prompt or "İşlem zaman aşımına uğradı."
|
403 |
except Exception as e:
|