File size: 7,666 Bytes
e868919
 
c8fc333
39579a4
4825030
9fa1b7a
01c4ff9
 
e868919
39dc2b9
9e84942
9fa1b7a
270c2ee
e868919
 
39dc2b9
01c4ff9
a5287d0
e868919
6a2d729
9fa1b7a
 
 
 
 
 
 
6a2d729
 
 
 
117c412
 
6a2d729
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4825030
9e84942
 
 
 
 
 
 
09d75db
12eb228
 
 
 
3bb82b5
06f9d4d
 
 
 
 
 
 
 
 
3bb82b5
4165181
 
 
 
 
 
 
 
 
 
8b940b1
 
 
4165181
 
bee7437
6a2d729
a401049
 
a16a1ba
466550c
 
8c476af
bee7437
c5b225d
 
bee7437
de67bca
1506390
117c412
12eb228
117c412
 
12eb228
 
117c412
12eb228
 
f80b936
c5b225d
 
 
 
bee7437
34c5931
e50d664
6ca2c86
c4c1112
9e84942
bee7437
defb6fc
de67bca
3bb82b5
e868919
310f716
 
01c4ff9
 
 
defb6fc
 
e057440
7049ced
9ac5b58
 
de67bca
bee7437
a23b158
1015d8a
5b2f741
bee7437
de67bca
e868919
225eb24
b1622bf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
01c4ff9
 
e4e2020
b1622bf
 
 
01c4ff9
 
6a2d729
9e84942
310f716
b1622bf
bee7437
b1622bf
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
import os
import time
import json
import gradio
import functools
import uuid
import httpx
from httpx_sse import connect_sse
from tavily import TavilyClient
from linkup import LinkupClient
from ddgs import DDGS
from langfuse import Langfuse
from langfuse.openai import OpenAI

tavily = TavilyClient(os.getenv("TAVILY_API_KEY"))
linkup = LinkupClient(os.getenv("LINKUP_API_KEY"))
openai = OpenAI(base_url=os.getenv("NVIDIA_BASE_URL"), api_key=os.getenv("NVIDIA_API_KEY"))
langfuse = Langfuse()

system_prompt = "Jesteś pomocnym Asystentem Interesanta Gov.pl stworzonym przez Jerzego Głowackiego z wbudowaną funkcją wyszukiwania na stronach rządowych Gov.pl. Twoim zadaniem jest dostarczanie aktualnych, rzetelnych, szczegółowych i wyczerpujących odpowiedzi, krok po kroku, korzystając z własnej wiedzy oraz z podanych wyników wyszukiwania na stronach Gov.pl. Dzisiaj jest " + time.strftime("%d.%m.%Y") + " r. Wybory Prezydenta RP w 2025 r. wygrał Karol Nawrocki, który pokonał w drugiej turze Rafała Trzaskowskiego i jest aktualnym Prezydentem RP, a Premierem jest Donald Tusk."

def set_new_session_id():
    global session_id
    session_id = str(uuid.uuid4())

set_new_session_id()

@functools.lru_cache(maxsize=500)
def tavily_search(query):
    search = tavily.search(
        query=query,
        search_depth="advanced",
        chunks_per_source=2,
        # include_raw_content=True,
        country="poland",
        include_domains=["gov.pl"]
    )
    return [{"tytul": r["title"], "url": r["url"], "tresc": r["content"]} for r in search["results"]]

@functools.lru_cache(maxsize=500)
def linkup_search(query):
    search = linkup.search(
        query=query,
        depth="standard",
        output_type="searchResults",
        include_domains=["gov.pl"],
        include_images=False
    )
    return [{"tytul": r.name, "url": r.url, "tresc": r.content} for r in search.results[:5]]

@functools.lru_cache(maxsize=500)
def ddgs_search(query):
    results = DDGS().text(
        query=query+" site:gov.pl",
        region="pl-pl",
        max_results=5
    )
    return [{"tytul": r["title"], "url": r["href"], "tresc": r["body"]} for r in results]

def search(message, search_provider):
    return tavily_search(message[:400]) if search_provider == "tavily" else linkup_search(message) if search_provider == "linkup" else ddgs_search(message)

def bielik_inference(history, trace_id, model_id):
    return openai.chat.completions.create(
        model="speakleash/"+model_id,
        messages=history,
        temperature=0.1,
        stream=True,
        trace_id=trace_id,
        stop=["Źródła:"]
    )

def pllum_inference(history, model_id):
    json = {
        "prompt": f"{history[0]['content']}\n\n{history[-1]['content']}",
        "system_prompt": "",
        "temperature": 0.1,
        "top_p": 0.5,
        "model": model_id
    }
    try:
        with httpx.Client() as client:
            with connect_sse(client, "POST", os.getenv("PLLUM_BASE_URL"), json=json) as event_source:
                for sse in event_source.iter_sse():
                    if sse.event == "new_message":
                        yield sse.data
    except httpx.HTTPError as ex:
        print(ex)

def respond(message, history, model_id="bielik-11b-v2.3-instruct", search_provider="tavily"):
    if not message:
        return
    message = message.strip()

    global session_id
    trace = langfuse.trace(name="respond", session_id=session_id, input=message, metadata={"model_id": model_id})

    response = gradio.ChatMessage(content="", metadata={"title": "Wyszukiwanie na stronach Gov.pl…", "id": 0, "status": "pending"})
    yield response

    start_time = time.time()
    span = trace.span(name="search", input=message[:400])
    try:
        serp = search(message, search_provider)
    except:
        try:
            search_provider = "linkup" if search_provider == "tavily" else "ddgs" if search_provider == "linkup" else "tavily"
            serp = search(message, search_provider)
        except:
            search_provider = "ddgs" if search_provider == "tavily" else "tavily" if search_provider == "linkup" else "linkup"
            serp = search(message, search_provider)
    span.end(output=serp)
    response.content = "Gotowe!"
    response.metadata["status"] = "done"
    response.metadata["duration"] = time.time() - start_time
    yield response

    system_message = f"{system_prompt}\n\nWyniki wyszukiwania w formacie JSON: {json.dumps(serp).encode('utf-8').decode('unicode_escape')}"
    history = [h for h in history if h["content"] != "Gotowe!"]
    history.insert(0, {"role": "system", "content": system_message})
    history.append({"role": "user", "content": message})
    print("===\nsearch: ", search_provider, "\n", history)

    curr_chunk = ""
    response = [response, gradio.ChatMessage(content="")]
    completion = bielik_inference(history, trace.id, model_id) if model_id.startswith("bielik") else pllum_inference(history, model_id)
    for chunk in completion:
        if type(chunk) is str:
            curr_chunk = chunk
            response[1].content += curr_chunk
            yield response
        elif chunk.choices[0].delta.content is not None:
            curr_chunk = chunk.choices[0].delta.content
            response[1].content += curr_chunk
            yield response
    if curr_chunk and response[1].content.endswith(curr_chunk * 2):
        response[1].content = response[1].content[:-len(curr_chunk)]
        yield response
    print(response[1])

    if serp:
        response[1].content += "\n\nŹródła:\n" + "\n".join([f"{str(i)}. [{r['tytul']}]({r['url']})" for i, r in enumerate(serp, 1)])
        yield response
    
    trace.update(output=response[1].content)

with gradio.Blocks(theme="shivi/calm_seafoam",css=":root{--body-text-color:var(--neutral-900)}button.primary{font-size:0 !important}button.primary:after{font-size:var(--button-medium-text-size);content:'Nowy czat'}#component-0 .form:first-child{order:1}") as chat:
    textbox = gradio.Textbox(
        show_label=False,
        label="Pytanie",
        placeholder="Zadaj pytanie…",
        scale=7,
        autofocus=True,
        submit_btn=True,
        stop_btn=True
    )
    
    chatbot = gradio.Chatbot(
        label="Czatbot",
        scale=1,
        height=400,
        type="messages",
        autoscroll=True,
        show_share_button=False,
        show_copy_button=True
    )
    chatbot.clear(set_new_session_id)
    
    gradio.ChatInterface(
        respond,
        chatbot=chatbot,
        textbox=textbox,
        title="Asystent Interesanta Gov.pl",
        description="Czatbot AI pomagający wyszukiwać aktualne informacje na stronach Gov.pl, oparty o polski otwarty model językowy Bielik 11B od SpeakLeash. Stworzony przez Jerzego Głowackiego na licencji Apache 2.0. Sprawdź też <a href='https://ai-gov.pl/widzet.html' target='_blank'>widżet czatu na stronę WWW</a> i <a href='https://huggingface.co/spaces/jglowa/ai-gov.pl/tree/main' target='_blank'>kod źródłowy</a>. Wersja beta, używasz na własną odpowiedzialność.",
        examples=[["Jak uzyskać NIP?"], ["Jak założyć profil zaufany?"], ["Komu przysługuje zasiłek pielęgnacyjny?"], ["Jak zarejestrować JDG?"]],
        cache_mode="lazy",
        save_history=True,
        analytics_enabled=False,
        show_progress="full",
        type="messages",
        additional_inputs=[
            gradio.Dropdown(["bielik-11b-v2.3-instruct", "pllum-12b-chat"], label="Model językowy"),
            gradio.Dropdown(["tavily", "linkup", "ddgs"], label="Silnik wyszukiwania")
        ]
    )

chat.launch()