File size: 4,016 Bytes
7edb85f
 
 
 
 
 
 
 
 
 
 
 
 
849c819
7edb85f
 
 
 
 
 
819cf97
 
 
 
 
7edb85f
819cf97
 
 
849c819
819cf97
7edb85f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
819cf97
 
 
 
 
849c819
 
819cf97
 
 
 
849c819
 
 
819cf97
 
849c819
819cf97
 
 
849c819
819cf97
 
 
 
 
 
849c819
 
 
 
819cf97
 
 
 
849c819
819cf97
 
849c819
 
819cf97
 
7edb85f
 
 
 
 
 
 
 
 
819cf97
 
7edb85f
 
 
 
 
 
849c819
819cf97
7edb85f
 
819cf97
 
 
7edb85f
849c819
 
 
7edb85f
819cf97
 
7edb85f
849c819
 
 
 
 
 
 
 
 
 
 
 
 
7edb85f
819cf97
 
7edb85f
849c819
 
 
 
 
 
 
7edb85f
849c819
7edb85f
 
 
 
 
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
from types import SimpleNamespace
from typing import Any

from huggingface_hub import InferenceClient

from config import HF_MODEL, HF_PROVIDER, HF_TOKEN


class LLMClient:
    """
    Hugging Face Inference Providers üzerinden sohbet tamamlama
    ve structured output üretir.

    Dönen veri yapısı eski Ollama istemcisiyle uyumludur:

        response.message.content
        response.message.thinking
    """

    def __init__(self) -> None:
        if not HF_TOKEN:
            raise RuntimeError(
                "HF_TOKEN bulunamadı. Hugging Face Space ayarlarında "
                "HF_TOKEN adında bir Secret oluşturun."
            )

        self.client = InferenceClient(
            provider=HF_PROVIDER,
            api_key=HF_TOKEN,
            timeout=90,
        )

        self.model = HF_MODEL

    @staticmethod
    def _create_response(
        content: str,
        thinking: str = "",
    ) -> SimpleNamespace:
        return SimpleNamespace(
            message=SimpleNamespace(
                content=content,
                thinking=thinking,
            )
        )

    def _chat_completion(
        self,
        request_kwargs: dict[str, Any],
    ) -> Any:
        """
        Planner için JSON Schema kullanır.
        Responder için standart chat completion çağrısı yapar.
        """

        schema = request_kwargs.pop("_schema", None)

        try:
            if schema is not None:
                request_kwargs["response_format"] = {
                    "type": "json_schema",
                    "json_schema": {
                        "name": "ToolPlan",
                        "schema": schema,
                        "strict": True,
                    },
                }

            return self.client.chat_completion(
                **request_kwargs
            )

        except Exception as exc:
            call_type = (
                "structured output"
                if schema is not None
                else "standart chat completion"
            )

            raise RuntimeError(
                "Hugging Face LLM çağrısı başarısız oldu.\n"
                f"Çağrı türü: {call_type}\n"
                f"Model: {self.model}\n"
                f"Provider: {HF_PROVIDER}\n"
                f"Hata türü: {type(exc).__name__}\n"
                f"Hata: {repr(exc)}"
            ) from exc

    def generate(
        self,
        messages: list[dict[str, str]],
        schema: dict[str, Any] | None = None,
        think: bool = True,
    ) -> SimpleNamespace:
        """
        Modele mesaj gönderir.

        `think` parametresi Planner ve Responder kodlarıyla
        geriye dönük uyumluluk amacıyla korunmaktadır.
        """

        request_kwargs: dict[str, Any] = {
            "model": self.model,
            "messages": messages,
            "temperature": 0.0,
            "max_tokens": 1024,
            "_schema": schema,
        }

        response = self._chat_completion(
            request_kwargs=request_kwargs,
        )

        choices = getattr(response, "choices", None)

        if not choices:
            raise RuntimeError(
                "Hugging Face modeli boş bir choices listesi döndürdü."
            )

        message = getattr(choices[0], "message", None)

        if message is None:
            raise RuntimeError(
                "Hugging Face yanıtında message alanı bulunamadı."
            )

        content = getattr(message, "content", None)

        if content is None:
            raise RuntimeError(
                "Hugging Face yanıtında content alanı boş döndü."
            )

        if not isinstance(content, str):
            content = str(content)

        content = content.strip()

        if not content:
            raise RuntimeError(
                "Hugging Face modeli boş metin döndürdü."
            )

        return self._create_response(
            content=content,
            thinking="",
        )


llm = LLMClient()