File size: 5,128 Bytes
be82a8a
4971496
2c21af2
 
be82a8a
4971496
d6fd3b7
2c21af2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cdfe590
2c21af2
cdfe590
2c21af2
 
 
 
 
 
 
b8aee7e
2c21af2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4971496
e0d2fc3
 
 
 
 
2c21af2
cdfe590
2c21af2
 
 
 
 
 
 
 
 
 
 
 
d6fd3b7
 
 
b8aee7e
2c21af2
 
 
 
 
be82a8a
4971496
2c21af2
 
 
 
4bd3834
 
 
2c21af2
4bd3834
d6fd3b7
2c21af2
 
4bd3834
d6fd3b7
 
 
2c21af2
 
 
4bd3834
2c21af2
 
 
 
 
 
 
 
 
 
 
 
 
 
4bd3834
d6fd3b7
 
 
4971496
be82a8a
2c21af2
 
b8aee7e
2c21af2
b8aee7e
2c21af2
b8aee7e
2c21af2
b8aee7e
 
d6fd3b7
b8aee7e
 
2c21af2
 
85e06dd
2c21af2
 
 
b8aee7e
d6fd3b7
b8aee7e
2c21af2
 
 
 
 
 
 
 
 
 
d6fd3b7
 
2c21af2
e0d2fc3
 
2c21af2
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
import gradio as gr
from huggingface_hub import InferenceClient
from duckduckgo_search import DDGS
import re

client = InferenceClient("Pinkstack/Superthoughts-lite-v1")

def format_search_results(query, results, result_type):
    formatted = f"{result_type} search results for '{query}':\n"
    for i, result in enumerate(results):
        title = result.get('title', 'No title')
        description = result.get('body', '') or result.get('snippet', '') or 'No description'
        url = result.get('href', '') or result.get('url', '') or 'No URL'
        formatted += f"{i+1}. [{title}]({url})\n{description}\n\n"
    return formatted

def extract_key_phrases(message):
    words = re.split(r'[,.!?;:\s]+', message.strip())
    phrases = [message]
    for i in range(len(words) - 1):
        if len(words[i]) > 3 and len(words[i+1]) > 3:
            phrases.append(f"{words[i]} {words[i+1]}")
    return phrases[:3]

def respond(
    message,
    history: list[tuple[str, str]],
    system_message,
    max_tokens,
    temperature,
    top_p,
    search_option,
):
    search_text = ""
    if search_option != "No search":
        with DDGS() as ddgs:
            if search_option == "Normal search":
                web_results = ddgs.text(message, max_results=3)
                search_text = format_search_results(message, web_results, "Web")
            elif search_option == "Deep research":
                queries = extract_key_phrases(message)
                search_texts = []
                for query in queries:
                    web_results = ddgs.text(query, max_results=3)
                    news_results = ddgs.news(query, max_results=2)
                    search_texts.append(format_search_results(query, web_results, "Web"))
                    search_texts.append(format_search_results(query, news_results, "News"))
                search_text = "\n".join(search_texts)
            message += "\n\n**Search Results:**\n" + search_text

    messages = [{"role": "system", "content": system_message}]
    for user_msg, assistant_msg in history:
        if user_msg:
            messages.append({"role": "user", "content": user_msg})
        if assistant_msg:
            messages.append({"role": "assistant", "content": assistant_msg})
    messages.append({"role": "user", "content": message})

    response = ""
    for msg in client.chat_completion(
        messages,
        max_tokens=max_tokens,
        stream=True,
        temperature=temperature,
        top_p=top_p,
    ):
        token = msg.choices[0].delta.content
        if token:
            response += token

    # Clean response: normalize line breaks to prevent extra <p> tags
    response = re.sub(r'\n\s*\n+', '\n', response.strip())

    # Process response to convert tags to HTML divs
    formatted_response = response
    formatted_response = formatted_response.replace("<think>", '<div class="thinking">').replace("</think>", "</div>")
    formatted_response = formatted_response.replace("<output>", '<div class="output">').replace("</output>", "</div>")

    yield formatted_response

css = """
.thinking {
    background-color: #333;
    color: white;
    padding: 15px;
    border-radius: 8px;
    margin-bottom: 10px;
}
.output {
    background-color: #f0f0f0;
    color: black !important;
    padding: 15px;
    border-radius: 8px;
}
.output * {
    color: black !important;
}
.gr-chatbot {
    background-color: #f5f5f5;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.gr-chatbot .message {
    padding: 12px;
    border-radius: 12px;
    margin-bottom: 12px;
    max-width: 80%;
}
.gr-chatbot .message.user {
    background-color: #d1d5db;
    align-self: flex-end;
}
.gr-chatbot .message.assistant {
    background-color: #ffffff;
    align-self: flex-start;
    box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.gr-chatbot .message.assistant * {
    color: inherit;
}
"""

system_message_default = """
Respond in the following format:

<think>
Your thought process here...
</think>

<output>
Your final answer here...

If search results are provided, you must cite relevant sources at the end of this section as a numbered list in the format:
1. [Title](URL)
2. [Title](URL)
</output>
"""

demo = gr.ChatInterface(
    respond,
    additional_inputs=[
        gr.Textbox(value=system_message_default, label="System message", lines=8),
        gr.Slider(minimum=1, maximum=4096, value=2048, step=1, label="Max new tokens"),
        gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
        gr.Slider(minimum=0.1, maximum=1.0, value=0.95, step=0.05, label="Top-p"),
        gr.Dropdown(
            choices=["No search", "Normal search", "Deep research"],
            value="No search",
            label="Web search",
            info="Choose 'Deep research' for comprehensive web and news results."
        ),
    ],
    css=css,
    title="Pinkchat - Superthoughts lite v1 (Just 1.7B parameters!)",
    description="""Chat with an AI that thinks step-by-step and has web search.
    BETA:Added deep research."""
)

if __name__ == "__main__":
    demo.launch()