File size: 5,727 Bytes
38436fc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""chat_template.jinja için test scripti.

Colab'da veya yerelde çalıştır:
    pip install jinja2 transformers
    python test_template.py
"""

import json

from jinja2 import Environment


def raise_exception(msg):
    """Jinja içinden hata fırlatmak için (şablonda kullanılıyor)."""
    raise ValueError(msg)


def sablonu_yukle(dosya="chat_template.jinja"):
    """Şablonu, Hugging Face'in apply_chat_template'iyle aynı ayarlarla yükler."""
    env = Environment()
    env.globals["raise_exception"] = raise_exception
    # HF de bu ayarları kullanır: Unicode korunur, anahtar sırası bozulmaz
    env.policies["json.dumps_kwargs"] = {"ensure_ascii": False, "sort_keys": False}
    return env.from_string(open(dosya, encoding="utf-8").read())


ARACLAR = [
    {
        "name": "get_weather",
        "description": "Bir şehrin güncel hava durumunu döndürür.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string", "description": "Şehir adı"}},
            "required": ["city"],
        },
    },
    {
        "name": "convert_temperature",
        "description": "Sıcaklık birimini dönüştürür.",
        "parameters": {
            "type": "object",
            "properties": {
                "value": {"type": "number"},
                "to_unit": {"type": "string", "enum": ["C", "F", "K"]},
            },
            "required": ["value", "to_unit"],
        },
    },
]


def baslik(metin):
    print("\n" + "=" * 64)
    print(metin)
    print("=" * 64)


def main():
    t = sablonu_yukle()

    # ---------------------------------------------------------------
    baslik("TEST 1 — Basit konuşma (araçsız, eğitim biçimi)")
    print(t.render(
        messages=[
            {"role": "system", "content": "Sen yardımcı bir asistansın."},
            {"role": "user", "content": "Merhaba"},
            {"role": "assistant", "content": "Selam! Nasıl yardımcı olabilirim?"},
        ],
        add_generation_prompt=False,
    ))

    # ---------------------------------------------------------------
    baslik("TEST 2 — Üretim istemi (add_generation_prompt=True)")
    print(t.render(
        messages=[{"role": "user", "content": "Nasılsın?"}],
        add_generation_prompt=True,
    ))
    print("^ Son satır '<|im_start|>AI' ile açık kaldı: model buradan devam eder.")

    # ---------------------------------------------------------------
    baslik("TEST 3 — Tam tool calling akışı")
    konusma = [
        {"role": "system", "content": "Sen yardımcı bir asistansın."},
        {"role": "user", "content": "Ankara mı sıcak Londra mı? Fahrenheit olarak söyle."},
        # Model iki aracı aynı anda çağırıyor
        {"role": "assistant", "content": None, "tool_calls": [
            {"function": {"name": "get_weather", "arguments": {"city": "Ankara"}}},
            {"function": {"name": "get_weather", "arguments": {"city": "London"}}},
        ]},
        # Araç sonuçları
        {"role": "tool", "content": '{"sehir": "Ankara", "sicaklik_c": 27}'},
        {"role": "tool", "content": '{"sehir": "London", "sicaklik_c": 18}'},
        # İkinci tur: dönüşüm araçları
        {"role": "assistant", "content": None, "tool_calls": [
            {"function": {"name": "convert_temperature",
                          "arguments": {"value": 27, "to_unit": "F"}}},
        ]},
        {"role": "tool", "content": '{"deger": 80.6, "birim": "F"}'},
        # Nihai cevap
        {"role": "assistant", "content": "Ankara 80.6°F ile Londra'dan daha sıcak."},
    ]
    cikti = t.render(messages=konusma, tools=ARACLAR, add_generation_prompt=False)
    print(cikti)

    # ---------------------------------------------------------------
    baslik("TEST 4 — Sistem mesajı olmadan, araçlarla")
    print(t.render(
        messages=[{"role": "user", "content": "İstanbul'da hava nasıl?"}],
        tools=ARACLAR[:1],
        add_generation_prompt=True,
    ))

    # ---------------------------------------------------------------
    baslik("TEST 5 — Geçersiz rol (hata fırlatmalı)")
    try:
        t.render(messages=[{"role": "hacker", "content": "..."}])
        print("HATA: geçersiz rol kabul edildi!")
    except ValueError as e:
        print(f"Beklenen hata alındı: {e}")

    # ---------------------------------------------------------------
    baslik("DOĞRULAMALAR")
    kontroller = [
        ("HTML kaçışı yok (&#34;)", "&#34;" not in cikti),
        ("Türkçe karakterler korunmuş", "şehrin" in cikti),
        ("user -> mustafa dönüşümü", "<|im_start|>mustafa" in cikti),
        ("assistant -> AI dönüşümü", "<|im_start|>AI" in cikti),
        ("tool rolü sarmalanmış", "<tool_response>" in cikti),
        ("araç çağrıları sarmalanmış", cikti.count("<tool_call>") >= 3),
        ("araç tanımları gömülmüş", "<tools>" in cikti),
        ("JSON ayrıştırılabilir", _json_kontrol(cikti)),
    ]
    for ad, sonuc in kontroller:
        print(f"  {'OK  ' if sonuc else 'HATA'}  {ad}")

    print("\nTüm testler tamamlandı.")


def _json_kontrol(metin):
    """Üretilen tool_call bloklarının geçerli JSON olduğunu doğrular."""
    import re
    bloklar = re.findall(r"<tool_call>\n(.*?)\n</tool_call>", metin, re.S)
    for b in bloklar:
        if "araç_adı" in b:      # sistem mesajındaki örnek şablon, atla
            continue
        try:
            veri = json.loads(b)
            if "name" not in veri or "arguments" not in veri:
                return False
        except json.JSONDecodeError:
            return False
    return len(bloklar) > 0


if __name__ == "__main__":
    main()