ciyidogan commited on
Commit
e1ef72d
Β·
verified Β·
1 Parent(s): fc0299f

Update prompt_builder.py

Browse files
Files changed (1) hide show
  1. prompt_builder.py +166 -306
prompt_builder.py CHANGED
@@ -1,44 +1,40 @@
1
  """
2
- Flare – Prompt Builder (v6 Β· date handling)
3
- ==============================================================
4
  """
5
 
6
- from typing import List, Dict
7
- from datetime import datetime, timedelta
8
- from utils import log
 
 
9
  from config_provider import ConfigProvider
10
  from locale_manager import LocaleManager
 
11
 
12
- # Date helper for date expressions
13
- def _get_date_context(locale_code: str = "tr-TR") -> Dict[str, str]:
14
- """Generate date context based on locale"""
15
- now = datetime.now()
 
 
16
 
17
- # Locale verilerini yΓΌkle
18
- locale_data = LocaleManager.get_locale(locale_code)
19
 
20
- # Weekday names from locale
21
- days = locale_data.get("days", {})
22
- weekdays = [days.get(str(i), f"Day{i}") for i in range(7)]
 
 
23
 
24
- # Monday is 0 in Python, Sunday is 6, but in locale Sunday is 0
25
- python_weekday = now.weekday() # 0=Monday, 6=Sunday
26
- locale_weekday = (python_weekday + 1) % 7 # Convert to locale format where 0=Sunday
27
- today_weekday = days.get(str(locale_weekday), "")
28
 
29
- # Calculate various dates
30
  dates = {
31
- "today": now.strftime("%Y-%m-%d"),
32
- "tomorrow": (now + timedelta(days=1)).strftime("%Y-%m-%d"),
33
- "day_after_tomorrow": (now + timedelta(days=2)).strftime("%Y-%m-%d"),
34
- "this_weekend_saturday": (now + timedelta(days=(5-now.weekday())%7)).strftime("%Y-%m-%d"),
35
- "this_weekend_sunday": (now + timedelta(days=(6-now.weekday())%7)).strftime("%Y-%m-%d"),
36
- "next_week_same_day": (now + timedelta(days=7)).strftime("%Y-%m-%d"),
37
- "two_weeks_later": (now + timedelta(days=14)).strftime("%Y-%m-%d"),
38
- "today_weekday": today_weekday,
39
- "today_day": now.day,
40
- "today_month": now.month,
41
- "today_year": now.year,
42
  "locale_code": locale_code
43
  }
44
 
@@ -51,40 +47,32 @@ def build_intent_prompt(general_prompt: str,
51
  conversation: List[Dict[str, str]],
52
  user_input: str,
53
  intents: List,
54
- project_name: str = None) -> str:
 
55
 
56
  # Get config when needed
57
  cfg = ConfigProvider.get()
58
 
59
- # Get internal prompt from config
60
- internal_prompt = cfg.global_config.internal_prompt or ""
 
 
61
 
62
  # Extract intent names and captions
63
  intent_names = [it.name for it in intents]
64
  intent_captions = [it.caption or it.name for it in intents]
65
 
66
- # Get project language
67
- project_language = "Turkish" # Default
68
- if project_name:
69
- project = next((p for p in cfg.projects if p.name == project_name), None)
70
- if project:
71
- # Language code'u language name'e Γ§evir
72
- lang_map = {
73
- "tr": "Turkish",
74
- "en": "English",
75
- "de": "German",
76
- "fr": "French",
77
- "es": "Spanish"
78
- }
79
- project_language = lang_map.get(project.default_language, "Turkish")
80
 
81
  # Replace placeholders in internal prompt
82
  if internal_prompt:
83
- # Intent names - tırnak içinde ve virgülle ayrılmış
84
  intent_names_str = ', '.join([f'"{name}"' for name in intent_names])
85
  internal_prompt = internal_prompt.replace("<intent names>", intent_names_str)
86
 
87
- # Intent captions - tırnak içinde ve virgülle ayrılmış
88
  intent_captions_str = ', '.join([f'"{caption}"' for caption in intent_captions])
89
  internal_prompt = internal_prompt.replace("<intent captions>", intent_captions_str)
90
 
@@ -94,11 +82,16 @@ def build_intent_prompt(general_prompt: str,
94
  # === INTENT INDEX ===
95
  lines = ["### INTENT INDEX ###"]
96
  for it in intents:
97
- # IntentConfig object attribute access
 
 
98
  det = it.detection_prompt.strip() if it.detection_prompt else ""
99
  det_part = f' β€’ detection_prompt β†’ "{det}"' if det else ""
100
- exs = " | ".join(it.examples) if it.examples else ""
101
- ex_part = f" β€’ examples β†’ {exs}" if exs else ""
 
 
 
102
 
103
  newline_between = "\n" if det_part and ex_part else ""
104
  lines.append(f"{it.name}:{det_part}{newline_between}{ex_part}")
@@ -122,65 +115,6 @@ def build_intent_prompt(general_prompt: str,
122
  log("βœ… Intent prompt built (with internal prompt)")
123
  return prompt
124
 
125
- # ─────────────────────────────────────────────────────────────────────────────
126
- # RESPONSE PROMPT
127
- # ─────────────────────────────────────────────────────────────────────────────
128
- def build_api_response_prompt(api_config, api_response: Dict) -> str:
129
- """Build prompt for API response with mappings"""
130
-
131
- response_prompt = api_config.response_prompt or "API yanΔ±tΔ±nΔ± kullanΔ±cΔ±ya aΓ§Δ±kla:"
132
-
133
- # Response mappings varsa, mapping bilgilerini ekle
134
- if api_config.response_mappings:
135
- mapping_info = []
136
-
137
- for mapping in api_config.response_mappings:
138
- # JSON path'e gâre değeri bul
139
- value = extract_value_from_json_path(api_response, mapping.json_path)
140
- if value is not None:
141
- # Type'a gΓΆre formatlama
142
- if mapping.type == "date":
143
- # ISO date'i TΓΌrkΓ§e formata Γ§evir
144
- try:
145
- dt = datetime.fromisoformat(value.replace('Z', '+00:00'))
146
- value = dt.strftime("%d %B %Y %H:%M")
147
- except:
148
- pass
149
- elif mapping.type == "float":
150
- try:
151
- value = f"{float(value):,.2f}"
152
- except:
153
- pass
154
-
155
- mapping_info.append(f"{mapping.caption}: {value}")
156
-
157
- if mapping_info:
158
- # Response prompt'a mapping bilgilerini ekle
159
- mapping_text = "\n\nΓ–nemli Bilgiler:\n" + "\n".join(f"β€’ {info}" for info in mapping_info)
160
- response_prompt = response_prompt.replace("{{api_response}}", f"{{{{api_response}}}}{mapping_text}")
161
-
162
- # API response'u JSON string olarak ekle
163
- response_json = json.dumps(api_response, ensure_ascii=False, indent=2)
164
- final_prompt = response_prompt.replace("{{api_response}}", response_json)
165
-
166
- return final_prompt
167
-
168
- def extract_value_from_json_path(data: Dict, path: str):
169
- """Extract value from JSON using dot notation path"""
170
- try:
171
- parts = path.split('.')
172
- value = data
173
- for part in parts:
174
- if isinstance(value, dict):
175
- value = value.get(part)
176
- elif isinstance(value, list) and part.isdigit():
177
- value = value[int(part)]
178
- else:
179
- return None
180
- return value
181
- except:
182
- return None
183
-
184
  # ─────────────────────────────────────────────────────────────────────────────
185
  # PARAMETER PROMPT
186
  # ─────────────────────────────────────────────────────────────────────────────
@@ -190,13 +124,13 @@ def build_parameter_prompt(intent_cfg,
190
  missing_params: List[str],
191
  user_input: str,
192
  conversation: List[Dict[str, str]],
193
- locale_code: str = None) -> str:
194
- # Intent'in locale'ini kullan, yoksa default
 
195
  if not locale_code:
196
- locale_code = getattr(intent_cfg, 'locale', 'tr-TR')
197
-
198
- date_ctx = _get_date_context()
199
 
 
200
  locale_data = LocaleManager.get_locale(locale_code)
201
 
202
  parts: List[str] = [
@@ -215,10 +149,13 @@ def build_parameter_prompt(intent_cfg,
215
  ""
216
  ]
217
 
218
- # Add parameter descriptions
219
  parts.append("Parameters to extract:")
220
  for p in intent_cfg.parameters:
221
  if p.name in missing_params:
 
 
 
222
  # Special handling for date type parameters
223
  if p.type == "date":
224
  date_prompt = _build_locale_aware_date_prompt(
@@ -226,229 +163,152 @@ def build_parameter_prompt(intent_cfg,
226
  )
227
  parts.append(date_prompt)
228
  else:
229
- parts.append(f"β€’ {p.name}: {p.extraction_prompt}")
 
230
 
231
  # Add format instruction
232
  parts.append("")
233
  parts.append("IMPORTANT: Your response must start with '#PARAMETERS:' followed by the JSON.")
234
- parts.append("Return ONLY this format with no extra text before or after:")
235
- parts.append(_FMT)
236
 
237
  # Add conversation history
238
- history_block = "\n".join(
239
- f"{m['role'].upper()}: {m['content']}" for m in conversation[-10:]
240
- )
 
241
 
242
- prompt = (
243
- "\n".join(parts) +
244
- "\n\nConversation so far:\n" + history_block +
245
- "\n\nUSER: " + user_input.strip()
246
- )
247
 
248
- log(f"πŸ“ Parameter prompt built for missing: {missing_params}")
249
- return prompt
250
 
251
  def _build_locale_aware_date_prompt(param, date_ctx: Dict, locale_data: Dict, locale_code: str) -> str:
252
- """Build date extraction prompt based on locale"""
253
-
254
- # Locale'e ΓΆzel date expressions
255
- date_expressions = locale_data.get("date_expressions", {})
256
- months = locale_data.get("months", {})
257
-
258
- prompt_parts = [
259
- f"β€’ {param.name}: {param.extraction_prompt}",
260
- " IMPORTANT DATE RULES:"
 
 
 
 
 
 
261
  ]
262
 
263
- # Common date expressions based on locale
264
- if locale_code.startswith("tr"):
265
- # Turkish specific
266
- prompt_parts.extend([
267
- f" - '{date_expressions.get('today', 'bugΓΌn')}' = {date_ctx['today']}",
268
- f" - '{date_expressions.get('tomorrow', 'yarΔ±n')}' = {date_ctx['tomorrow']}",
269
- f" - '{' or '.join(date_expressions.get('day_after_tomorrow', ['ΓΆbΓΌr gΓΌn']))}' = {date_ctx['day_after_tomorrow']}",
270
- f" - '{date_expressions.get('this_weekend', 'bu hafta sonu')}' = {date_ctx['this_weekend_saturday']} or {date_ctx['this_weekend_sunday']}",
271
- f" - '{date_expressions.get('this_saturday', 'bu cumartesi')}' = {date_ctx['this_weekend_saturday']}",
272
- f" - '{date_expressions.get('this_sunday', 'bu pazar')}' = {date_ctx['this_weekend_sunday']}",
273
- f" - '{' or '.join(date_expressions.get('next_week', ['gelecek hafta', 'haftaya']))}' = add 7 days to current date",
274
- f" - 'X gΓΌn sonra' = add X days to today",
275
- f" - 'X gΓΌn ΓΆnce' = subtract X days from today"
276
- ])
277
- elif locale_code.startswith("en"):
278
- # English specific
279
- prompt_parts.extend([
280
- f" - '{date_expressions.get('today', 'today')}' = {date_ctx['today']}",
281
- f" - '{date_expressions.get('tomorrow', 'tomorrow')}' = {date_ctx['tomorrow']}",
282
- f" - '{' or '.join(date_expressions.get('day_after_tomorrow', ['day after tomorrow']))}' = {date_ctx['day_after_tomorrow']}",
283
- f" - '{date_expressions.get('this_weekend', 'this weekend')}' = {date_ctx['this_weekend_saturday']} or {date_ctx['this_weekend_sunday']}",
284
- f" - '{date_expressions.get('this_saturday', 'this Saturday')}' = {date_ctx['this_weekend_saturday']}",
285
- f" - '{date_expressions.get('this_sunday', 'this Sunday')}' = {date_ctx['this_weekend_sunday']}",
286
- f" - '{date_expressions.get('next_week', 'next week')}' = add 7 days to current date",
287
- f" - 'in X days' = add X days to today",
288
- f" - 'X days ago' = subtract X days from today"
289
- ])
290
- # Diğer diller için de eklenebilir
291
-
292
- # Month names
293
- if months:
294
- month_list = ", ".join([
295
- f"{name}={num}" for num, name in sorted(months.items())
296
- ])
297
- prompt_parts.append(f" - Month names: {month_list}")
298
-
299
- # Date format hint
300
- date_format = locale_data.get("date_format", "YYYY-MM-DD")
301
- prompt_parts.append(f" - Expected date format: YYYY-MM-DD (convert from {date_format})")
302
-
303
- # Example based on locale
304
- if locale_code.startswith("tr"):
305
- prompt_parts.append(f" - Example: '10 Temmuz' = {date_ctx['today_year']}-07-10")
306
- elif locale_code.startswith("en"):
307
- prompt_parts.append(f" - Example: 'July 10' = {date_ctx['today_year']}-07-10")
308
-
309
- return "\n".join(prompt_parts)
310
 
 
 
 
 
 
311
  def build_smart_parameter_question_prompt(
312
- collection_config, # ParameterCollectionConfig
313
- intent_config, # IntentConfig
314
  missing_params: List[str],
315
- session, # Session object
316
- project_language: str = "Turkish",
317
- locale_code: str = None
 
318
  ) -> str:
319
- """Akıllı parametre sorusu üretmek için prompt oluştur"""
320
 
321
- # Use intent's locale if not specified
322
- if not locale_code:
323
- locale_code = getattr(intent_config, 'locale', 'tr-TR')
324
-
325
- # Config'den template'i al
326
- template = collection_config.collection_prompt
327
 
328
- # Conversation history'yi formatla
329
- conversation_history = _format_conversation_history(session.chat_history)
 
 
330
 
331
- # Toplanan parametreleri formatla
332
- collected_params = _format_collected_params(session.variables, intent_config)
 
 
 
 
 
 
 
 
333
 
334
- # Eksik parametreleri formatla
335
- missing_params_str = _format_missing_params(missing_params, intent_config)
 
336
 
337
- # Cevaplanmayan parametreleri formatla
338
- unanswered_params_str = _format_unanswered_params(
339
- session.unanswered_parameters,
340
- intent_config,
341
- session.asked_parameters
342
- )
 
343
 
344
- # Kaç parametre sorulacağını belirle
345
- params_to_ask_count = min(
346
- len(missing_params),
347
- collection_config.max_params_per_question
348
- )
 
 
 
 
 
349
 
350
- # Template'i doldur
351
- prompt = template.replace("{{conversation_history}}", conversation_history)
 
352
  prompt = prompt.replace("{{intent_name}}", intent_config.name)
353
- prompt = prompt.replace("{{intent_caption}}", intent_config.caption)
354
- prompt = prompt.replace("{{collected_params}}", collected_params)
355
- prompt = prompt.replace("{{missing_params}}", missing_params_str)
356
- prompt = prompt.replace("{{unanswered_params}}", unanswered_params_str)
357
- prompt = prompt.replace("{{max_params}}", str(params_to_ask_count))
358
  prompt = prompt.replace("{{project_language}}", project_language)
359
 
360
- log(f"πŸ“ Smart parameter question prompt built for {params_to_ask_count} params in {locale_code}")
361
  return prompt
362
 
363
  # ─────────────────────────────────────────────────────────────────────────────
364
- # HELPER FUNCTIONS FOR SMART PARAMETER COLLECTION
365
  # ─────────────────────────────────────────────────────────────────────────────
366
- def _format_conversation_history(chat_history: List[Dict[str, str]]) -> str:
367
- """Format conversation history for prompt"""
368
- # Son 5 mesajΔ± al
369
- recent_history = chat_history[-5:] if len(chat_history) > 5 else chat_history
370
-
371
- formatted = []
372
- for msg in recent_history:
373
- role = "User" if msg["role"] == "user" else "Assistant"
374
- formatted.append(f"{role}: {msg['content']}")
375
-
376
- return "\n".join(formatted) if formatted else "No previous conversation"
377
-
378
-
379
- def _format_collected_params(variables: Dict[str, str], intent_config) -> str:
380
- """Format collected parameters"""
381
- collected = []
382
 
 
383
  for param in intent_config.parameters:
384
- if param.variable_name in variables:
385
- value = variables[param.variable_name]
386
- collected.append(f"- {param.caption}: {value}")
387
-
388
- return "\n".join(collected) if collected else "None collected yet"
389
-
390
-
391
- def _format_missing_params(missing_params: List[str], intent_config) -> str:
392
- """Format missing parameters with their captions"""
393
- missing = []
394
-
395
- for param_name in missing_params:
396
- param = next((p for p in intent_config.parameters if p.name == param_name), None)
397
- if param:
398
- missing.append(f"- {param.caption} ({param.name})")
399
 
400
- return "\n".join(missing) if missing else "None"
401
-
402
 
403
- def _format_unanswered_params(unanswered_params: List[str], intent_config, asked_params: Dict[str, int]) -> str:
404
- """Format unanswered parameters with ask count"""
405
- unanswered = []
 
 
406
 
407
- for param_name in unanswered_params:
408
- param = next((p for p in intent_config.parameters if p.name == param_name), None)
409
- if param:
410
- ask_count = asked_params.get(param_name, 0)
411
- unanswered.append(f"- {param.caption} (asked {ask_count} time{'s' if ask_count > 1 else ''})")
412
 
413
- return "\n".join(unanswered) if unanswered else "None"
414
-
415
-
416
- def extract_params_from_question(question: str, all_params: List[str], intent_config) -> List[str]:
417
- """
418
- Sorulan sorudan hangi parametrelerin sorulduğunu tahmin et
419
- (LLM'in hangi parametreleri sorduğunu anlamak için basit bir yaklaşım)
420
- """
421
- found_params = []
422
- question_lower = question.lower()
423
 
424
- for param_name in all_params:
425
- param = next((p for p in intent_config.parameters if p.name == param_name), None)
426
- if param:
427
- # Parametre caption'ının veya keywords'ünün soruda geçip geçmediğini kontrol et
428
- caption_words = param.caption.lower().split()
429
-
430
- # Caption kelimelerinden herhangi biri soruda geΓ§iyorsa
431
- if any(word in question_lower for word in caption_words if len(word) > 3):
432
- found_params.append(param_name)
433
- continue
434
-
435
- # Parametre tipine gΓΆre ΓΆzel kontroller
436
- if param.type == "date":
437
- date_keywords = ["tarih", "zaman", "gΓΌn", "hafta", "ay", "yΔ±l", "ne zaman", "hangi gΓΌn"]
438
- if any(keyword in question_lower for keyword in date_keywords):
439
- found_params.append(param_name)
440
- elif param.type == "city":
441
- city_keywords = ["şehir", "nereden", "nereye", "hangi il", "il", "yer"]
442
- if any(keyword in question_lower for keyword in city_keywords):
443
- found_params.append(param_name)
444
- elif param.type == "number":
445
- number_keywords = ["kaç", "sayı", "miktar", "adet", "kişi", "yolcu"]
446
- if any(keyword in question_lower for keyword in number_keywords):
447
- found_params.append(param_name)
448
-
449
- # Eğer hiçbir parametre bulunamadıysa, en az âncelikli olanı dândür
450
- if not found_params and all_params:
451
- found_params = [all_params[0]]
452
-
453
- log(f"πŸ” Extracted params from question: {found_params}")
454
- return found_params
 
1
  """
2
+ Flare – Prompt Builder
 
3
  """
4
 
5
+ from typing import Dict, List, Optional
6
+ from datetime import datetime
7
+ import json
8
+ import re
9
+
10
  from config_provider import ConfigProvider
11
  from locale_manager import LocaleManager
12
+ from utils import log
13
 
14
+ # ─────────────────────────────────────────────────────────────────────────────
15
+ # DATE CONTEXT
16
+ # ─────────────────────────────────────────────────────────────────────────────
17
+ def _get_date_context(locale_code: str = "tr") -> Dict[str, str]:
18
+ """Get today/tomorrow dates with weekday names in target locale"""
19
+ from datetime import timedelta
20
 
21
+ today = datetime.now()
22
+ tomorrow = today + timedelta(days=1)
23
 
24
+ locale_data = LocaleManager.get_locale(locale_code)
25
+ weekday_names = locale_data.get("weekdays", {
26
+ "0": "Monday", "1": "Tuesday", "2": "Wednesday", "3": "Thursday",
27
+ "4": "Friday", "5": "Saturday", "6": "Sunday"
28
+ })
29
 
30
+ # Get localized date format
31
+ date_format = locale_data.get("date_format", "%Y-%m-%d")
 
 
32
 
 
33
  dates = {
34
+ "today": today.strftime(date_format),
35
+ "tomorrow": tomorrow.strftime(date_format),
36
+ "today_weekday": weekday_names.get(str(today.weekday()), ""),
37
+ "tomorrow_weekday": weekday_names.get(str(tomorrow.weekday()), ""),
 
 
 
 
 
 
 
38
  "locale_code": locale_code
39
  }
40
 
 
47
  conversation: List[Dict[str, str]],
48
  user_input: str,
49
  intents: List,
50
+ project_name: str = None,
51
+ project_locale: str = "tr") -> str:
52
 
53
  # Get config when needed
54
  cfg = ConfigProvider.get()
55
 
56
+ # Get internal prompt from LLM provider settings
57
+ internal_prompt = ""
58
+ if cfg.global_config.llm_provider and cfg.global_config.llm_provider.settings:
59
+ internal_prompt = cfg.global_config.llm_provider.settings.get("internal_prompt", "")
60
 
61
  # Extract intent names and captions
62
  intent_names = [it.name for it in intents]
63
  intent_captions = [it.caption or it.name for it in intents]
64
 
65
+ # Get project language name from locale
66
+ locale_info = LocaleManager.get_locale(project_locale)
67
+ project_language = locale_info.get("name", "Turkish")
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  # Replace placeholders in internal prompt
70
  if internal_prompt:
71
+ # Intent names - quoted and comma-separated
72
  intent_names_str = ', '.join([f'"{name}"' for name in intent_names])
73
  internal_prompt = internal_prompt.replace("<intent names>", intent_names_str)
74
 
75
+ # Intent captions - quoted and comma-separated
76
  intent_captions_str = ', '.join([f'"{caption}"' for caption in intent_captions])
77
  internal_prompt = internal_prompt.replace("<intent captions>", intent_captions_str)
78
 
 
82
  # === INTENT INDEX ===
83
  lines = ["### INTENT INDEX ###"]
84
  for it in intents:
85
+ # Get examples for project locale
86
+ locale_examples = it.get_examples_for_locale(project_locale)
87
+
88
  det = it.detection_prompt.strip() if it.detection_prompt else ""
89
  det_part = f' β€’ detection_prompt β†’ "{det}"' if det else ""
90
+
91
+ ex_part = ""
92
+ if locale_examples:
93
+ exs = " | ".join(locale_examples)
94
+ ex_part = f" β€’ examples β†’ {exs}"
95
 
96
  newline_between = "\n" if det_part and ex_part else ""
97
  lines.append(f"{it.name}:{det_part}{newline_between}{ex_part}")
 
115
  log("βœ… Intent prompt built (with internal prompt)")
116
  return prompt
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  # ─────────────────────────────────────────────────────────────────────────────
119
  # PARAMETER PROMPT
120
  # ─────────────────────────────────────────────────────────────────────────────
 
124
  missing_params: List[str],
125
  user_input: str,
126
  conversation: List[Dict[str, str]],
127
+ locale_code: str = None,
128
+ project_locale: str = "tr") -> str:
129
+ # Use project locale if not specified
130
  if not locale_code:
131
+ locale_code = project_locale
 
 
132
 
133
+ date_ctx = _get_date_context(locale_code)
134
  locale_data = LocaleManager.get_locale(locale_code)
135
 
136
  parts: List[str] = [
 
149
  ""
150
  ]
151
 
152
+ # Add parameter descriptions with localized captions
153
  parts.append("Parameters to extract:")
154
  for p in intent_cfg.parameters:
155
  if p.name in missing_params:
156
+ # Get localized caption
157
+ caption = p.get_caption_for_locale(locale_code, project_locale)
158
+
159
  # Special handling for date type parameters
160
  if p.type == "date":
161
  date_prompt = _build_locale_aware_date_prompt(
 
163
  )
164
  parts.append(date_prompt)
165
  else:
166
+ extraction = p.extraction_prompt or f"Extract {p.name}"
167
+ parts.append(f"β€’ {p.name} ({caption}): {extraction}")
168
 
169
  # Add format instruction
170
  parts.append("")
171
  parts.append("IMPORTANT: Your response must start with '#PARAMETERS:' followed by the JSON.")
172
+ parts.append(f"Format: {_FMT}")
173
+ parts.append("No other text before or after.")
174
 
175
  # Add conversation history
176
+ parts.append("")
177
+ parts.append("Recent conversation:")
178
+ for msg in conversation[-5:]:
179
+ parts.append(f"{msg['role'].upper()}: {msg['content']}")
180
 
181
+ # Add current input
182
+ parts.append(f"USER: {user_input}")
 
 
 
183
 
184
+ return "\n".join(parts)
 
185
 
186
  def _build_locale_aware_date_prompt(param, date_ctx: Dict, locale_data: Dict, locale_code: str) -> str:
187
+ """Build date extraction prompt with locale awareness"""
188
+ caption = param.get_caption_for_locale(locale_code)
189
+
190
+ # Get locale-specific date info
191
+ month_names = locale_data.get("months", {})
192
+ relative_dates = locale_data.get("relative_dates", {
193
+ "today": "today", "tomorrow": "tomorrow",
194
+ "yesterday": "yesterday", "this_week": "this week"
195
+ })
196
+
197
+ parts = [
198
+ f"β€’ {param.name} ({caption}): Extract date in YYYY-MM-DD format.",
199
+ f" - Today is {date_ctx['today']} ({date_ctx['today_weekday']})",
200
+ f" - '{relative_dates.get('today', 'today')}' β†’ {date_ctx['today']}",
201
+ f" - '{relative_dates.get('tomorrow', 'tomorrow')}' β†’ {date_ctx['tomorrow']}"
202
  ]
203
 
204
+ if param.extraction_prompt:
205
+ parts.append(f" - {param.extraction_prompt}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
+ return "\n".join(parts)
208
+
209
+ # ─────────────────────────────────────────────────────────────────────────────
210
+ # SMART PARAMETER QUESTION
211
+ # ─────────────────────────────────────────────────────────────────────────────
212
  def build_smart_parameter_question_prompt(
213
+ intent_config,
 
214
  missing_params: List[str],
215
+ collected_params: Dict[str, str],
216
+ conversation: List[Dict[str, str]],
217
+ project_locale: str = "tr",
218
+ unanswered_params: List[str] = None
219
  ) -> str:
220
+ """Build prompt for smart parameter collection"""
221
 
222
+ cfg = ConfigProvider.get()
 
 
 
 
 
223
 
224
+ # Get parameter collection config from LLM provider settings
225
+ collection_config = {}
226
+ if cfg.global_config.llm_provider and cfg.global_config.llm_provider.settings:
227
+ collection_config = cfg.global_config.llm_provider.settings.get("parameter_collection_config", {})
228
 
229
+ # Get collection prompt template
230
+ collection_prompt = collection_config.get("collection_prompt", """
231
+ You are a helpful assistant collecting information from the user.
232
+
233
+ Intent: {{intent_name}} - {{intent_caption}}
234
+ Still needed: {{missing_params}}
235
+
236
+ Ask for the missing parameters in a natural, conversational way in {{project_language}}.
237
+ Generate ONLY the question, nothing else.
238
+ """)
239
 
240
+ # Get locale info
241
+ locale_info = LocaleManager.get_locale(project_locale)
242
+ project_language = locale_info.get("name", "Turkish")
243
 
244
+ # Build missing params description with localized captions
245
+ missing_param_descriptions = []
246
+ for param_name in missing_params:
247
+ param = next((p for p in intent_config.parameters if p.name == param_name), None)
248
+ if param:
249
+ caption = param.get_caption_for_locale(project_locale)
250
+ missing_param_descriptions.append(f"{param_name} ({caption})")
251
 
252
+ # Build collected params description
253
+ collected_descriptions = []
254
+ for param_name, value in collected_params.items():
255
+ param = next((p for p in intent_config.parameters if p.name == param_name), None)
256
+ if param:
257
+ caption = param.get_caption_for_locale(project_locale)
258
+ collected_descriptions.append(f"{param_name} ({caption}): {value}")
259
+
260
+ # Build conversation history
261
+ conv_history = "\n".join([f"{msg['role']}: {msg['content']}" for msg in conversation[-5:]])
262
 
263
+ # Replace placeholders
264
+ prompt = collection_prompt
265
+ prompt = prompt.replace("{{conversation_history}}", conv_history)
266
  prompt = prompt.replace("{{intent_name}}", intent_config.name)
267
+ prompt = prompt.replace("{{intent_caption}}", intent_config.caption or intent_config.name)
268
+ prompt = prompt.replace("{{collected_params}}", "\n".join(collected_descriptions) if collected_descriptions else "None")
269
+ prompt = prompt.replace("{{missing_params}}", ", ".join(missing_param_descriptions))
270
+ prompt = prompt.replace("{{unanswered_params}}", ", ".join(unanswered_params) if unanswered_params else "None")
271
+ prompt = prompt.replace("{{max_params}}", str(collection_config.get("max_params_per_question", 2)))
272
  prompt = prompt.replace("{{project_language}}", project_language)
273
 
 
274
  return prompt
275
 
276
  # ─────────────────────────────────────────────────────────────────────────────
277
+ # PARAMETER EXTRACTION FROM QUESTION
278
  # ─────────────────────────────────────────────────────────────────────────────
279
+ def extract_params_from_question(question: str, intent_config, project_locale: str = "tr") -> List[str]:
280
+ """Extract which parameters are being asked in the question"""
281
+ asked_params = []
282
+ question_lower = question.lower()
 
 
 
 
 
 
 
 
 
 
 
 
283
 
284
+ # Check each missing parameter
285
  for param in intent_config.parameters:
286
+ # Check all locale captions
287
+ for caption_obj in param.caption:
288
+ caption = caption_obj.caption.lower()
289
+ # Check if caption appears in question
290
+ if caption in question_lower:
291
+ asked_params.append(param.name)
292
+ break
293
+
294
+ # Also check parameter name
295
+ if param.name.lower() in question_lower:
296
+ if param.name not in asked_params:
297
+ asked_params.append(param.name)
 
 
 
298
 
299
+ return asked_params
 
300
 
301
+ # ─────────────────────────────────────────────────────────────────────────────
302
+ # API RESPONSE PROMPT
303
+ # ───────────────────────────────────────────────────────────────────��─────────
304
+ def build_api_response_prompt(api_config, api_response: Dict) -> str:
305
+ """Build prompt for API response humanization"""
306
 
307
+ response_prompt = api_config.response_prompt
308
+ if not response_prompt:
309
+ response_prompt = "Convert this API response to a friendly message: {{api_response}}"
 
 
310
 
311
+ # Replace placeholders
312
+ response_prompt = response_prompt.replace("{{api_response}}", json.dumps(api_response, ensure_ascii=False))
 
 
 
 
 
 
 
 
313
 
314
+ return response_prompt