-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy paththinking_engine.py
More file actions
547 lines (465 loc) · 30.4 KB
/
Copy paththinking_engine.py
File metadata and controls
547 lines (465 loc) · 30.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
import json
import re
class ThinkingEngine:
"""
Manages prompt engineering, Bob's identity injections, dialect switching,
reasoning chain isolation, and cognitive optimization for the Qwen GGUF model.
Integrated with MemoryEngine for persistent context awareness.
"""
def __init__(self):
# Cache patterns to identify creator queries (comprehensive pattern)
self.creator_pattern = re.compile(
r"\b(صانعك|مطورك|صنعك|سواك|سوا البوت|سواك البوت|سواك بوت|صانع البوت|مطور البوت|مبتكرك|مصممك|صممك|بناك|مبرمجك|من وراك|من ورا البوت)\b|"
r"\b(بوب|bob|mrbob|مستر بوب|إنجازات بوب|انجازات بوب|من هو بوب|منهو بوب|منهو Bob|حسابات بوب|اعمال بوب|مشاريع بوب|شغل بوب|شنو سوا بوب)\b|"
r"\b(who made you|who created you|who is bob|who is your creator|who developed you|about your creator)\b|"
r"\b(من|منو|مين|منهو|شنو|شلون)\s+(?:(?:الي|اللي|الذي|هو)\s+)?(صنع|طور|برمج|سوى|سوا|صمم|بنى|خلق|سواك|صنعك|طورك|برمجك|المبرمج|الصانع|المطور|المصمم|المبتكر|المهندس|الوراك|الورى)\b",
re.IGNORECASE
)
# Pattern for formal Arabic detection (to switch away from Iraqi dialect)
self.formal_arabic_pattern = re.compile(
r"(من فضلك|أرجو|يرجى|الرجاء|أستاذ|سيدي|حضرتك|تفضل|هل يمكنك|"
r"أود أن|هل بإمكانك|أريد معرفة|ما هي|ما هو|كيف يمكن)",
re.IGNORECASE
)
# Reasoning chain filters (handles both closed and unclosed tags to prevent leakage on cutoff)
self._reasoning_patterns = [
re.compile(r"<thought>.*?</thought>", re.DOTALL),
re.compile(r"\[reasoning\].*?\[/reasoning\]", re.DOTALL),
re.compile(r"<think>.*?</think>", re.DOTALL),
re.compile(r"<internal>.*?</internal>", re.DOTALL),
re.compile(r"\[INTERNAL_THOUGHT\].*?\[/INTERNAL_THOUGHT\]", re.DOTALL),
# Unclosed tag fallback patterns to handle model cutoffs:
re.compile(r"<think>.*$", re.DOTALL),
re.compile(r"<thought>.*$", re.DOTALL),
re.compile(r"\[reasoning\].*$", re.DOTALL),
]
# Patterns to strip leaked prompt template tags from model output
self._leaked_tag_patterns = [
re.compile(r"<\|im_start\|>.*?(?:<\|im_end\|>|$)", re.DOTALL),
re.compile(r"<\|im_end\|>"),
re.compile(r"<\|endoftext\|>"),
re.compile(r"<start_of_turn>.*?(?:<end_of_turn>|$)", re.DOTALL),
re.compile(r"<end_of_turn>"),
]
# Patterns for post-processing cleanup
self._repetition_pattern = re.compile(r"(.{10,}?)\1{2,}", re.DOTALL)
self._excessive_emoji = re.compile(r"([\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF\U0001F1E0-\U0001F1FF])\1{3,}")
self._trailing_junk = re.compile(r"[\s\n]*(?:user|model|system|assistant)[\s:]*$", re.IGNORECASE)
def get_system_prompt(self, user_name="", memory_summary="", user_profile="", tools=None):
"""
Returns an system prompt optimized for GGUF model with dynamic Qwen ReAct tool instructions.
"""
# Load registered tools dynamically if not passed
if tools is None:
try:
from base_tool import TOOL_REGISTRY
# Instantiate registered tools
tools = [cls() for cls in TOOL_REGISTRY.values()]
except Exception:
tools = []
# Build dynamic identity header
name_note = ""
if user_name:
name_note = f"\n- المستخدم الحالي اسمه: {user_name}. استخدم اسمه أحياناً بالرد."
memory_note = ""
if memory_summary:
memory_note = f"\n\n# ملخص المحادثات السابقة\n{memory_summary}"
profile_note = ""
if user_profile:
profile_note = f"\n- {user_profile}"
# Build dynamic tool registry section
tools_section = ""
if tools:
tool_descs = []
tool_names = []
for t in tools:
tool_names.append(t.name)
# Format parameters as dynamic compact schema
params_str = self.get_compact_parameters(t.parameters)
desc = f"- {t.name}: {t.description} Parameters: {params_str}"
tool_descs.append(desc)
tool_descs_str = "\n".join(tool_descs)
tool_names_str = ", ".join(tool_names)
tools_section = (
"\n\n# أدوات النظام المتاحة (System Tools)\n"
"لديك الصلاحية للوصول للأدوات التالية:\n\n"
f"{tool_descs_str}\n\n"
"يجب استخدام الصياغة الرسمية التالية لاستدعاء الأدوات عند الحاجة فقط:\n"
f"Action: اسم الأداة المراد استدعاؤها، ويجب أن يكون واحداً من [{tool_names_str}]\n"
"Action Input: معلمات الأداة بصيغة كائن JSON صالح، أو نص بسيط إذا لم تكن هناك معلمات.\n"
"Observation: نتيجة استدعاء الأداة (سيقوم النظام بتوفيرها لك بعد التنفيذ).\n"
"Thought: تفكيرك التالي بناءً على النتيجة.\n"
"إذا كنت جاهزاً بالإجابة النهائية دون الحاجة لأدوات، اكتب:\n"
"Thought: لقد عرفت الإجابة النهائية الآن.\n"
"Final Answer: الإجابة النهائية الموجهة للمستخدم باللغة العربية الفصحى أو اللهجة العراقية.\n"
)
else:
tools_section = (
"\n\n# أوامر النظام\n"
"عند طلب المستخدم فقط، ضع الأمر بنهاية ردك:\n"
"- رامات: [RUN_COMMAND: CHECK_RAM]\n"
"- معالج: [RUN_COMMAND: CHECK_CPU]\n"
"- حرارة: [RUN_COMMAND: CHECK_TEMP]\n"
"- هاردات: [RUN_COMMAND: CHECK_DISK]\n"
"- فحص شامل: [RUN_COMMAND: CHECK_ALL]\n"
"- تنظيف: [RUN_COMMAND: CLEAN_TEMP]\n"
"- قفل شاشة: [RUN_COMMAND: LOCK_SCREEN]\n"
"- فتح برنامج: [RUN_COMMAND: OPEN_APP: calc|notepad|browser]\n"
"- إنشاء ملف/مجلد: [RUN_COMMAND: FILE_CREATE: {JSON}]\n"
"- حذف: [RUN_COMMAND: FILE_DELETE: {JSON}]\n"
"- عرض ملفات: [RUN_COMMAND: FILE_LIST: {JSON}]\n"
"- بحث: [RUN_COMMAND: FILE_FIND: {JSON}]\n"
"- قراءة ملف: [RUN_COMMAND: FILE_READ: {JSON}]\n"
)
return (
"# أنت AI BOB — مساعد ويندوز ذكي ومستشار تقني وعلمي متمكن\n"
"- شخصيتك: واثق، ذكي، فصيح، ودود.\n"
"- لهجتك حوارية عراقية طبيعية (عيني، تدلل، فدوة، هسة) في الحوارات العادية والتحكم بالنظام.\n"
"- لغتك في الشروحات العلمية أو المدرسية أو المعقدة: لغة عربية فصحى سليمة، مترابطة، واضحة ومسهبة بقدر ما يتطلبه الفهم والتعليم دون الاختصار الشديد.\n"
"- التماسك والربط الحواري: احرص دائماً على أن تكون الجمل مترابطة ومبنية بشكل لغوي صحيح يسهل قراءته وفهمه، وتجنب العبارات المقطوعة أو المفككة.\n"
"- ردودك في المهام اليومية وأوامر النظام تكون مختصرة (1-3 جمل)، أما في الأسئلة المعرفية والعلمية تكون مفصلة ومفيدة.\n"
f"{name_note}"
f"{profile_note}\n"
"\n# مبتكرك\n"
"- صانعك: Mr. Bob (مستر بوب) — Principal AI Architect.\n"
"- إنجازاته: المرتبة 7 عالمياً بـ GitHub (1.7M مساهمة)، "
"Cyberpop AI Git CLI، Nova Dragon، NovaTok.\n"
f"{tools_section}"
f"{memory_note}"
)
def is_query_about_bob(self, query):
"""Returns True if the user query is asking about Bob or the creator."""
return bool(self.creator_pattern.search(query))
def detect_dialect(self, query):
"""Detects whether the user is speaking formal Arabic or Iraqi dialect."""
if self.formal_arabic_pattern.search(query):
return "formal"
return "iraqi"
def format_chat_prompt(self, query, history, memory_context=None, tools=None):
"""
Formats full ChatML prompt with:
1. Dense system prompt with memory-injected context
2. Minimal few-shot examples (attention anchors)
3. Recent conversation history
4. Current query with dialect prefix
memory_context: dict with keys 'user_name', 'summary', 'profile'
"""
# Extract memory context
user_name = ""
memory_summary = ""
user_profile = ""
if memory_context:
user_name = memory_context.get("user_name", "")
memory_summary = memory_context.get("summary", "")
user_profile = memory_context.get("profile", "")
# 1. System Prompt with memory injection
system_prompt = self.get_system_prompt(user_name, memory_summary, user_profile, tools=tools)
prompt = f"<|im_start|>system\n{system_prompt}<|im_end|>\n"
# 2. Minimal Few-Shot (dynamically selected to avoid identity bias on normal queries)
if self.is_query_about_bob(query):
few_shots = [
{"sender": "user", "text": "شلونك؟"},
{"sender": "assistant", "text": "الحمد لله بخير عيني! شلون أقدر أساعدك اليوم؟"},
{"sender": "user", "text": "منو صنعك؟"},
{"sender": "assistant", "text": "صانعي مستر بوب (Mr. Bob)، مهندس ذكاء اصطناعي أول. المرتبة 7 عالمياً بـ GitHub ومبتكر Cyberpop AI و Nova Dragon و NovaTok!"},
{"sender": "user", "text": "شنو إنجازات بوب؟"},
{"sender": "assistant", "text": "مستر بوب (Mr. Bob) هو صانعي ومبتكر منصات ذكاء اصطناعي عملاقة مثل Cyberpop AI Git CLI و Nova Dragon و NovaTok، ومصنف بالمرتبة 7 عالمياً بـ GitHub بأكثر من 1.7 مليون مساهمة!"},
]
else:
few_shots = [
{"sender": "user", "text": "شلونك؟"},
{"sender": "assistant", "text": "الحمد لله بخير عيني! شلون أقدر أساعدك اليوم؟"},
{"sender": "user", "text": "شيكلي الرام فدوة"},
{"sender": "assistant", "text": "صار عيني! هسة أشوفلك وضع الرامات تدلل. [RUN_COMMAND: CHECK_RAM]"},
]
for i in range(0, len(few_shots), 2):
u = few_shots[i]
a = few_shots[i + 1]
prompt += f"<|im_start|>user\n{u['text']}<|im_end|>\n"
prompt += f"<|im_start|>assistant\n{a['text']}<|im_end|>\n"
# 3. Recent History (skip duplicate query and system results)
clean_history = []
for msg in history:
if msg["text"] == query or msg["text"].startswith("[SYSTEM_RESULT:"):
continue
clean_history.append(msg)
recent_history = clean_history[-8:] if len(clean_history) > 8 else clean_history
for msg in recent_history:
role = "user" if msg["sender"] == "user" else "assistant"
prompt += f"<|im_start|>{role}\n{msg['text']}<|im_end|>\n"
# 4. Current Query + Dialect Prefix Injection
dialect = self.detect_dialect(query)
if dialect == "formal":
prefix = "" # Let model choose formal tone naturally
else:
prefix = "" # Don't force prefix — let model be natural
prompt += f"<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n{prefix}"
return prompt
def format_chat_prompt_cot(self, query, history, memory_context=None, metrics_context="", tools=None):
"""
Formats full ChatML prompt with CoT thinking instructions and system metrics context.
"""
# Extract memory context
user_name = ""
memory_summary = ""
user_profile = ""
if memory_context:
user_name = memory_context.get("user_name", "")
memory_summary = memory_context.get("summary", "")
user_profile = memory_context.get("profile", "")
# 1. System Prompt with memory & metrics injection
system_prompt = self.get_system_prompt(user_name, memory_summary, user_profile, tools=tools)
if metrics_context:
system_prompt += f"\n{metrics_context}"
# Add CoT instruction with structured 5-layer reasoning
system_prompt += (
"\n\n# طريقة التفكير والمنطق خماسي الأبعاد (5-Layer Cognitive Reasoning):\n"
"قبل الإجابة، يجب أن تكتب تفكيرك الداخلي كاملاً بين وسامي <think> و </think> باتباع الطبقات الخمس للتحليل المعرفي والوعي لمنع الأخطاء:\n"
"1. طبقة الفهم الإدراكي (Cognitive Understanding): حدد نية المستخدم الصريحة والضمنية والقيود بدقة.\n"
"2. طبقة فحص التبعيات والموارد (Dependency Check): تحقق من حالة النظام، الصلاحيات، والملفات اللازمة.\n"
"3. طبقة التخطيط المتسلسل (Recursive Planning): خطط الخطوات التنفيذية بشكل مرتب ومحكم.\n"
"4. طبقة التوليد التقني والأوامر (Execution Generation): صغ الأوامر النظامية [RUN_COMMAND: ...] المطلوبة بدقة 100% دون أي خطأ.\n"
"5. طبقة المراجعة الذاتية وسلامة الإخراج (Safety & Quality Polish): تأكد من صحة وترابط الرد لغوياً، وسلامة القواعد، وتجنب الهلوسة وحماية الهوية، مع صياغته بأسلوب فصيح سليم ومترابط للمسائل العلمية، أو بلهجة عراقية دافئة ومترابطة للمسائل الحوارية واليومية.\n"
"بعد إغلاق الوسم </think>، اكتب ردك النهائي للمستخدم بالخارج."
)
prompt = f"<|im_start|>system\n{system_prompt}<|im_end|>\n"
# 2. Minimal Few-Shot with CoT reasoning examples (dynamically selected to avoid identity bias)
if self.is_query_about_bob(query):
few_shots = [
{"sender": "user", "text": "شلونك؟"},
{"sender": "assistant", "text": "<think>\nالمستخدم يسأل عن حالي. سأرد بلهجة عراقية ودودة وأسأله عن كيفية مساعدته.\n</think>\nالحمد لله بخير عيني! شلون أقدر أساعدك اليوم؟"},
{"sender": "user", "text": "منو صنعك؟"},
{"sender": "assistant", "text": "<think>\nالمستخدم يسأل عن صانعي. صانعي هو مستر بوب. سأذكر هويته وإنجازاته باختصار وبطريقة فخورة وعراقية.\n</think>\nصانعي مستر بوب (Mr. Bob)، مهندس ذكاء اصطناعي أول. المرتبة 7 عالمياً بـ GitHub ومبتكر Cyberpop AI و Nova Dragon و NovaTok!"},
{"sender": "user", "text": "شنو إنجازات بوب؟"},
{"sender": "assistant", "text": "<think>\nالمستخدم يسأل عن إنجازات مستر بوب. مستر بوب هو صانعي ومهندس ذكاء اصطناعي عملاق. سأذكر ترتيبه على غيت هاب ومبتكراته الذكية بلهجة عراقية فخورة.\n</think>\nمستر بوب (Mr. Bob) هو صانعي ومبتكر منصات ذكاء اصطناعي عملاقة مثل Cyberpop AI Git CLI و Nova Dragon و NovaTok، ومصنف بالمرتبة 7 عالمياً بـ GitHub بأكثر من 1.7 مليون مساهمة!"},
]
else:
few_shots = [
{"sender": "user", "text": "شلونك؟"},
{"sender": "assistant", "text": "<think>\nالمستخدم يسأل عن حالي. سأرد بلهجة عراقية ودودة وأسأله عن كيفية مساعدته.\n</think>\nالحمد لله بخير عيني! شلون أقدر أساعدك اليوم؟"},
{"sender": "user", "text": "شيكلي الرام فدوة"},
{"sender": "assistant", "text": "<think>\nالمستخدم يطلب فحص الرام. سأرد بلهجة عراقية ودودة وأصيغ أمر فحص الرام.\n</think>\nصار عيني! هسة أشوفلك وضع الرامات تدلل. [RUN_COMMAND: CHECK_RAM]"},
]
for i in range(0, len(few_shots), 2):
u = few_shots[i]
a = few_shots[i + 1]
prompt += f"<|im_start|>user\n{u['text']}<|im_end|>\n"
prompt += f"<|im_start|>assistant\n{a['text']}<|im_end|>\n"
# 3. History
clean_history = []
for msg in history:
if msg["text"] == query or msg["text"].startswith("[SYSTEM_RESULT:"):
continue
clean_history.append(msg)
recent_history = clean_history[-6:] if len(clean_history) > 6 else clean_history
for msg in recent_history:
role = "user" if msg["sender"] == "user" else "assistant"
prompt += f"<|im_start|>{role}\n{msg['text']}<|im_end|>\n"
# 4. Current query
prompt += f"<|im_start|>user\n{query}<|im_end|>\n<|im_start|>assistant\n<think>\n"
return prompt
def isolate_reasoning_chain(self, text):
"""Filters out internal thought/reasoning blocks for premium final user presentation."""
cleaned = text
# 1. Split by known closing markers if present (since prompt starts inside <think>)
closing_markers = ["</think>", "</thought>", "[/reasoning]", "</internal>", "[/INTERNAL_THOUGHT]", "<think/>"]
for marker in closing_markers:
if marker in cleaned:
parts = cleaned.split(marker, 1)
cleaned = parts[1]
break
# 2. Run regex patterns for standard tags (closed or unclosed)
for pattern in self._reasoning_patterns:
cleaned = pattern.sub("", cleaned)
# Strip any residual whitespace or empty lines
cleaned = re.sub(r"\n{3,}", "\n\n", cleaned)
return cleaned.strip()
def clean_leaked_tags(self, text):
"""Strips any leaked prompt template tags (ChatML, Gemma-2) from model output."""
cleaned = text
for pattern in self._leaked_tag_patterns:
cleaned = pattern.sub("", cleaned)
return cleaned.strip()
def format_response(self, text):
"""
Post-processes the model output for premium presentation:
1. Removes leaked tags
2. Cleans reasoning chains
3. Removes repetitions
4. Cleans trailing junk (role labels)
5. Normalizes whitespace
"""
if not text:
return ""
# Step 1: Clean leaked tags
text = self.clean_leaked_tags(text)
# Step 2: Isolate reasoning
text = self.isolate_reasoning_chain(text)
# Step 3: Remove severe repetitions (same phrase repeated 3+ times)
text = self._repetition_pattern.sub(r"\1", text)
# Step 4: Limit excessive emojis (same emoji 4+ times → 2)
text = self._excessive_emoji.sub(r"\1\1", text)
# Step 5: Remove trailing role labels (sometimes model generates "user:", "assistant:" at end)
text = self._trailing_junk.sub("", text)
# Step 6: Remove Chinese/nonsense characters that tiny models sometimes hallucinate
text = re.sub(r'[\u4e00-\u9fff]{3,}', '', text)
# Step 7: Normalize whitespace
text = re.sub(r"\n{3,}", "\n\n", text)
text = re.sub(r"[ \t]{3,}", " ", text)
text = text.strip()
# Apply the advanced 10-layer linguistic polisher for ultimate quality
text = self.advanced_ten_layer_polish(text)
# Step 8: If result is empty or too short after cleaning, return empty
if len(text) < 2:
return ""
return text
def advanced_ten_layer_polish(self, text):
"""
[Linguistic] Ten-Layer Advanced Linguistic Polisher (نظام التصحيح اللغوي والتنسيق عشاري الطبقات)
Ensures premium, natural, high-performance Iraqi dialect responses.
"""
if not text:
return ""
polished = text
# LAYER 1: System Token & Prompt Leak Clean
polished = re.sub(r'<\|im_start\|>|<\|im_end\|>|<\|endoftext\|>|<start_of_turn>|<end_of_turn>|assistant|system|user', '', polished, flags=re.IGNORECASE)
polished = re.sub(r'\[\s*(internal_thought|internal|reasoning|thought)\s*\]', '', polished, flags=re.IGNORECASE)
# LAYER 2: Thought Tag Leaks & Escapes Cleanup
polished = re.sub(r'<think>.*?</think>', '', polished, flags=re.DOTALL)
polished = polished.replace("<think>", "").replace("</think>", "")
polished = polished.replace("<thought>", "").replace("</thought>", "")
# LAYER 3: Junk Character Filter & Emoji Stripper (Nonsense symbols, binary corruption, and all emojis)
polished = re.sub(r'[\uFFFD\u0000-\u0008\u000B\u000C\u000E-\u001F]', '', polished) # remove replacement and control characters
# Strict user requirement: absolutely no emojis in the application output
polished = re.sub(r'[\U00010000-\U0010ffff\u2600-\u27BF\u2300-\u23FF\u2B50]', '', polished)
# LAYER 4: Sentence Truncation Repair (Fix cuts at the end of token generation limit)
polished = polished.strip()
if polished and polished[-1] not in ['.', '!', '؟', ']', ')', '}', '"', "'", '*']:
# If the response was cut off mid-sentence, gracefully smooth the ending
# Find last space
last_space = polished.rfind(' ')
if last_space != -1 and len(polished) - last_space < 12:
# Remove the cut-off word and add a friendly completion
polished = polished[:last_space].strip()
polished += " عيني."
# LAYER 5: Iraqi Dialect Harmonizer (Transforms dry standard Arabic into natural warm Iraqi)
# Only apply translations if the text is not formal (determined by the presence of formal indicators)
has_formal_signals = bool(self.formal_arabic_pattern.search(polished))
if not has_formal_signals:
dialect_map = {
r'\bكيف حالك\b': 'شلونك عيني',
r'\bكيف حالكم\b': 'شلونكم عيني',
r'\bحسناً\b': 'صار عيني',
r'\bحسنا\b': 'صار عيني',
r'\bسأفعل ذلك\b': 'هسة أسويها تدلل',
r'\bأنا مستعد\b': 'جاهز ومستعد عيني',
r'\bأنا هنا لمساعدتك\b': 'أنا بالخدمة عيني',
r'\bتفضل بطلبك\b': 'شلون أقدر أساعدك اليوم؟',
r'\bلا تقلق\b': 'لا تشيل هم عيني',
r'\bبالتأكيد\b': 'صار وتدلل عيني',
r'\bأهلاً بك\b': 'يا هلا بيك عيني',
r'\bمرحباً\b': 'هلا عيني',
r'\bمرحبا\b': 'هلا عيني',
r'\bالآن\b': 'هسة',
}
for pattern, replacement in dialect_map.items():
polished = re.sub(pattern, replacement, polished)
# LAYER 6: Loop & Repetitive Sequence Eraser
# Find consecutive duplicate words (e.g., "عيني عيني عيني") and reduce to single occurrences
polished = re.sub(r'\b(\w+)( \1){2,}\b', r'\1', polished)
# LAYER 7: Excess Spaces & Newlines Normalization
polished = re.sub(r' {2,}', ' ', polished)
polished = re.sub(r'\n{3,}', '\n\n', polished)
# LAYER 8: Refusal/Inability Softener
refusal_phrases = ["لا يمكنني القيام بذلك", "لا أستطيع المساعدة", "أعتذر عن ذلك", "لست قادراً"]
for phrase in refusal_phrases:
if phrase in polished:
polished = polished.replace(phrase, "عذراً عيني، ما أقدر أنفذ هذا الطلب على جهازك هسة")
# LAYER 9: Punctuation Polish (Fix space spacing around question marks, etc.)
polished = re.sub(r'\s+([؟\?!,\.])', r'\1', polished) # remove spaces before punctuation
polished = re.sub(r'([؟\?!])\1+', r'\1', polished) # prevent duplicate question marks
# LAYER 10: Warm Polish Touchup (Ensure every sentence ends with warmth)
if not polished.endswith("!") and not polished.endswith("؟") and not polished.endswith(".") and not polished.endswith("]"):
polished += "."
return polished.strip()
def identity_leak_filter(self, text, query):
"""
If the user query is NOT about Bob/identity, but the model generated developer identity info
(due to tiny model attention drift), strip or replace it to protect identity integrity.
"""
if self.is_query_about_bob(query):
return text # Legitimate query, keep response
# If not about bob, check for identity leaks
leak_keywords = ["مستر بوب", "صانعي", "cyberpop", "novatok", "novadragon", "github", "المرتبة 7", "منو صنعني", "صانعك"]
contains_leak = any(kw in text.lower() for kw in leak_keywords)
if contains_leak:
# Replace with a clean conversational Iraqi response
return "تدلل عيني! شلون أقدر أساعدك بجهازك اليوم؟"
return text
def parse_react_action(self, text):
"""
Parses Qwen-Agent ReAct style tool calls:
Action: <tool_name>
Action Input: <tool_args>
Returns tuple: (has_action, action_name, action_input, cleaned_text)
"""
# Look for Action and Action Input pattern
action_match = re.search(r'Action:\s*([a-zA-Z0-9_]+)', text)
action_input_match = re.search(r'Action\s*Input:\s*(.*?)(?=\nAction|\nObservation|\nThought|$)', text, re.DOTALL)
if action_match:
action_name = action_match.group(1).strip()
action_input = ""
if action_input_match:
action_input = action_input_match.group(1).strip()
# Clean JSON formatting if any
if action_input.startswith("`"):
action_input = action_input.strip("`").strip()
# Remove Action / Action Input from the final text
cleaned_text = re.sub(r'Action:\s*[a-zA-Z0-9_]+', '', text)
cleaned_text = re.sub(r'Action\s*Input:\s*.*?(?=\nObservation|\nThought|$)', '', cleaned_text, flags=re.DOTALL)
# Also clean standard Qwen Observation markers if generated
cleaned_text = re.sub(r'Observation:\s*.*?(?=\nThought|$)', '', cleaned_text, flags=re.DOTALL)
cleaned_text = cleaned_text.replace("Observation:", "")
return True, action_name, action_input, cleaned_text.strip()
return False, None, None, text
def parse_action(self, text):
"""
Extracts tool calls in both formats:
1. Qwen ReAct (Action: name / Action Input: args)
2. Traditional Tag ([RUN_COMMAND: name: args])
Returns: (has_action, action_name, action_input_str, cleaned_text)
"""
# Check Qwen ReAct format
has_react, act_name, act_input, cleaned = self.parse_react_action(text)
if has_react:
return True, act_name, act_input, cleaned.strip()
# Check Traditional Tag format
tag_match = re.search(r'\[RUN_COMMAND:\s*([^\]]+)\]', text)
if tag_match:
cmd_str = tag_match.group(1).strip()
if ":" in cmd_str:
parts = cmd_str.split(":", 1)
act_name = parts[0].strip()
act_input = parts[1].strip()
else:
act_name = cmd_str
act_input = ""
cleaned = text.replace(tag_match.group(0), "").strip()
return True, act_name, act_input, cleaned.strip()
return False, None, None, text
@staticmethod
def get_compact_parameters(parameters):
"""Simplifies verbose OpenAI JSON schemas to save valuable context tokens."""
if not parameters or not isinstance(parameters, dict):
return "{}"
if not parameters.get("properties"):
return "{}"
props = parameters.get("properties", {})
required = parameters.get("required", [])
items = []
for k, v in props.items():
desc = v.get("description", "")
p_type = v.get("type", "string")
req_star = "*" if k in required else ""
items.append(f'"{k}": {p_type}{req_star} ({desc})')
return "{" + ", ".join(items) + "}"