Spaces:
Building
Building
Update prompt_builder.py
Browse files- prompt_builder.py +166 -306
prompt_builder.py
CHANGED
@@ -1,44 +1,40 @@
|
|
1 |
"""
|
2 |
-
Flare β Prompt Builder
|
3 |
-
==============================================================
|
4 |
"""
|
5 |
|
6 |
-
from typing import List,
|
7 |
-
from datetime import datetime
|
8 |
-
|
|
|
|
|
9 |
from config_provider import ConfigProvider
|
10 |
from locale_manager import LocaleManager
|
|
|
11 |
|
12 |
-
#
|
13 |
-
|
14 |
-
|
15 |
-
|
|
|
|
|
16 |
|
17 |
-
|
18 |
-
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
|
|
|
|
|
23 |
|
24 |
-
#
|
25 |
-
|
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":
|
32 |
-
"tomorrow":
|
33 |
-
"
|
34 |
-
"
|
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
|
|
|
55 |
|
56 |
# Get config when needed
|
57 |
cfg = ConfigProvider.get()
|
58 |
|
59 |
-
# Get internal prompt from
|
60 |
-
internal_prompt =
|
|
|
|
|
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 |
-
|
68 |
-
|
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 -
|
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 -
|
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 |
-
#
|
|
|
|
|
98 |
det = it.detection_prompt.strip() if it.detection_prompt else ""
|
99 |
det_part = f' β’ detection_prompt β "{det}"' if det else ""
|
100 |
-
|
101 |
-
ex_part =
|
|
|
|
|
|
|
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
|
194 |
-
|
|
|
195 |
if not locale_code:
|
196 |
-
locale_code =
|
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 |
-
|
|
|
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("
|
235 |
-
parts.append(
|
236 |
|
237 |
# Add conversation history
|
238 |
-
|
239 |
-
|
240 |
-
|
|
|
241 |
|
242 |
-
|
243 |
-
|
244 |
-
"\n\nConversation so far:\n" + history_block +
|
245 |
-
"\n\nUSER: " + user_input.strip()
|
246 |
-
)
|
247 |
|
248 |
-
|
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
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
261 |
]
|
262 |
|
263 |
-
|
264 |
-
|
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 |
-
|
313 |
-
intent_config, # IntentConfig
|
314 |
missing_params: List[str],
|
315 |
-
|
316 |
-
|
317 |
-
|
|
|
318 |
) -> str:
|
319 |
-
"""
|
320 |
|
321 |
-
|
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 |
-
#
|
329 |
-
|
|
|
|
|
330 |
|
331 |
-
#
|
332 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
333 |
|
334 |
-
#
|
335 |
-
|
|
|
336 |
|
337 |
-
#
|
338 |
-
|
339 |
-
|
340 |
-
intent_config,
|
341 |
-
|
342 |
-
|
|
|
343 |
|
344 |
-
#
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
|
|
|
|
|
|
|
|
|
|
349 |
|
350 |
-
#
|
351 |
-
prompt =
|
|
|
352 |
prompt = prompt.replace("{{intent_name}}", intent_config.name)
|
353 |
-
prompt = prompt.replace("{{intent_caption}}", intent_config.caption)
|
354 |
-
prompt = prompt.replace("{{collected_params}}",
|
355 |
-
prompt = prompt.replace("{{missing_params}}",
|
356 |
-
prompt = prompt.replace("{{unanswered_params}}",
|
357 |
-
prompt = prompt.replace("{{max_params}}", str(
|
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 |
-
#
|
365 |
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
366 |
-
def
|
367 |
-
"""
|
368 |
-
|
369 |
-
|
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 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
|
395 |
-
|
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
|
401 |
-
|
402 |
|
403 |
-
|
404 |
-
|
405 |
-
|
|
|
|
|
406 |
|
407 |
-
|
408 |
-
|
409 |
-
|
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 |
-
|
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 |
-
|
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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|