File size: 7,362 Bytes
c69a8c3
76fb66f
c69a8c3
9fae0c6
bc2ac28
9fae0c6
 
bc2ac28
9fae0c6
 
 
9e0330e
9fae0c6
 
9e0330e
c69a8c3
9fae0c6
 
1b711ea
 
 
9e0330e
9fae0c6
2598bd9
9fae0c6
 
 
2598bd9
9fae0c6
 
2598bd9
9fae0c6
2598bd9
9fae0c6
 
2598bd9
 
9fae0c6
 
 
 
 
 
 
 
 
 
 
 
7e37a85
9fae0c6
 
7e37a85
9fae0c6
 
7e37a85
9e0330e
 
9fae0c6
 
 
9e0330e
 
9fae0c6
 
 
 
 
 
7e37a85
9e0330e
 
9fae0c6
7a7801e
9fae0c6
 
 
 
 
 
 
4426eaf
 
9fae0c6
 
 
 
 
 
 
 
 
 
4426eaf
76fb66f
9fae0c6
 
 
 
7e37a85
76fb66f
 
9fae0c6
 
 
e6a2928
9fae0c6
d759883
9fae0c6
 
 
 
 
e6a2928
 
 
9fae0c6
 
d759883
9fae0c6
d759883
9fae0c6
 
 
 
d759883
9fae0c6
d759883
9fae0c6
 
 
d759883
9fae0c6
d759883
9fae0c6
 
 
 
d759883
 
 
9fae0c6
 
 
d759883
 
9fae0c6
 
c69a8c3
76fb66f
c69a8c3
 
76fb66f
9fae0c6
c69a8c3
 
 
 
 
 
76fb66f
c69a8c3
 
 
9fae0c6
 
c69a8c3
 
 
 
 
9fae0c6
c69a8c3
 
9fae0c6
c69a8c3
 
 
9fae0c6
 
 
 
 
 
 
c69a8c3
9fae0c6
 
c69a8c3
 
9fae0c6
 
 
c69a8c3
 
9fae0c6
bc2ac28
c69a8c3
8d24498
44667fe
7e37a85
c69a8c3
 
 
 
 
8d24498
c69a8c3
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
import os
import time
import gradio as gr
from neo_rest import start_server
from logica import (
    find_custom_response, generate_game_explanation,
    detect_roblox, calculator_mode_active, extract_text_content,
)
from buscador import search as web_search, ERROR_NETWORK, ERROR_RATE_LIMIT, ERROR_LICENSE
from resumidor import summarize
from roblox_api import search_player, search_game, format_player, format_game
from matematicas import (
    is_calculator_request, is_math_operation,
    solve_operation, format_result, extract_username,
)

def add_turn(history, user_msg, bot_msg=""):
    return history + [
        {"role": "user", "content": user_msg},
        {"role": "assistant", "content": bot_msg},
    ]

def stream_tokens(text, history):
    """
    Emits the response token by token (word by word),
    simulating the generation process of a real language model.
    Longer tokens = slightly longer pauses (more semantic weight).
    """
    tokens = text.split(" ")
    accumulated = ""
    for i, token in enumerate(tokens):
        accumulated += token
        if i < len(tokens) - 1:
            accumulated += " "
        history[-1]["content"] = accumulated
        delay = 0.055 if len(token) > 4 else 0.030
        time.sleep(delay)
        yield history

def respond(message, history):
    text = message.strip().lower()

    if is_calculator_request(message):
        name = extract_username(history)
        greeting = (
            f"Sure! ๐Ÿ˜€ {name}, here's our calculator:\n\n"
            "๐Ÿงฎ **NEO-1 Virtual Calculator**\n"
            "Type any math operation and I'll solve it instantly.\n\n"
            "**Examples:**\n"
            "- `5 + 3`\n- `12 * 7`\n- `100 / 4`\n"
            "- `2 ** 8` (power)\n- `144 ** 0.5` (square root)\n\n"
            "_Type your operation or say 'exit calculator' to go back._"
        )
        history = add_turn(history, message)
        for h in stream_tokens(greeting, history):
            yield h, ""
        return

    if text in ("exit calculator", "close calculator", "quit calculator", "back to chat"):
        history = add_turn(history, message, "Alright, back to normal chat. Ask me anything! ๐Ÿ˜Š")
        yield history, ""
        return

    if calculator_mode_active(history) or is_math_operation(message):
        result = solve_operation(message)
        if result is not None:
            response = format_result(message, result)
            history = add_turn(history, message)
            for h in stream_tokens(response, history):
                yield h, ""
            return

    roblox_type, roblox_name = detect_roblox(message)

    if roblox_type == "player":
        history = add_turn(history, message, "๐Ÿ” Searching for player on Roblox...")
        yield history, ""
        data = search_player(roblox_name)
        result = format_player(data)
        history[-1]["content"] = result
        yield history, ""
        return

    if roblox_type == "game":
        history = add_turn(history, message, "๐Ÿ” Searching for game on Roblox...")
        yield history, ""
        data = search_game(roblox_name)
        result = format_game(data)
        if data and "error" not in data:
            explanation = generate_game_explanation(data)
            result = result + "\n\n๐Ÿ’ก **What is this game about?**\n" + explanation
        history[-1]["content"] = result
        yield history, ""
        return

    custom_response = find_custom_response(message)
    if custom_response:
        history = add_turn(history, message)
        for h in stream_tokens(custom_response, history):
            yield h, ""
        return

    # โ”€โ”€ NEO-2: web search fallback โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    history = add_turn(history, message, "๐ŸŒ Searching the web...")
    yield history, ""

    web_result = web_search(message)

    if web_result.get("found"):
        summary = summarize(message, web_result)
        if summary:
            history[-1]["content"] = ""
            for h in stream_tokens(summary, history):
                yield h, ""
            return

    # โ”€โ”€ User-facing error messages based on failure type โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
    error_type = web_result.get("error_type", "")

    if error_type == ERROR_NETWORK:
        msg = (
            "โš ๏ธ **No internet connection**\n\n"
            "I couldn't reach the web search engine right now. "
            "I also checked my knowledge base but found nothing on that topic.\n\n"
            "_Please try again in a few seconds or rephrase your question._"
        )
    elif error_type == ERROR_RATE_LIMIT:
        msg = (
            "โณ **Too many searches in a short time**\n\n"
            "The search engine asked me to slow down for a moment. "
            "Please wait a few seconds and try again. ๐Ÿ˜Š"
        )
    elif error_type == ERROR_LICENSE:
        msg = (
            "๐Ÿ”’ **Sources unavailable**\n\n"
            "I found results online, but all sources use restrictive licenses "
            "(All Rights Reserved, CC BY-ND, etc.) that don't allow me to use their content.\n\n"
            "_Try rephrasing your question to find open-licensed sources._"
        )
    else:
        msg = (
            "๐Ÿค– **No results found**\n\n"
            "I couldn't find information on that topic in my knowledge base "
            "or on the web. Try rephrasing your question."
        )

    history[-1]["content"] = msg
    yield history, ""

with gr.Blocks(title="mdfjbots-neo-1") as demo:
    gr.Markdown(
        """
        # ๐Ÿค– mdfjbots-neo-1
        ### Conversational AI assistant
        """
    )

    chatbot = gr.Chatbot(
        show_label=False,
        height=500,
        avatar_images=(None, "https://api.dicebear.com/7.x/bottts/svg?seed=mdfjbots"),
    )

    with gr.Row():
        input_box = gr.Textbox(
            placeholder="Type your message here... (e.g. 'search player Builderman')",
            show_label=False,
            scale=9,
            container=False,
            autofocus=True,
        )
        btn_send = gr.Button("Send", scale=1, variant="primary")

    with gr.Row():
        btn_clear = gr.Button("๐Ÿ—‘๏ธ Clear conversation", size="sm")

    gr.Examples(
        examples=[
            "Hello, who are you?",
            "search player Builderman",
            "search game Adopt Me",
            "definition of history",
            "calculator",
            "what is linux",
            "korean war",
        ],
        inputs=input_box,
        label="Example questions",
    )

    input_box.submit(fn=respond, inputs=[input_box, chatbot], outputs=[chatbot, input_box])
    btn_send.click(fn=respond, inputs=[input_box, chatbot], outputs=[chatbot, input_box])
    btn_clear.click(fn=lambda: ([], ""), outputs=[chatbot, input_box])

if __name__ == "__main__":
    start_server()

    port = int(os.environ.get("PORT", 5000))
    dev_domain = os.environ.get("REPLIT_DEV_DOMAIN", "")
    root_path = f"https://{dev_domain}/__neo1" if dev_domain else ""
    demo.queue()
    demo.launch(
        server_name="0.0.0.0",
        server_port=port,
        share=False,
        theme=gr.themes.Soft(primary_hue="blue"),
        root_path=root_path,
    )