File size: 5,792 Bytes
6ab2291
417ebaa
8ba7726
15fbd60
cd5ee6b
 
 
 
e88fe4a
d2736bf
6ab2291
0c1db11
5777353
 
 
 
a3fad22
0c1db11
5b726d6
cd5ee6b
 
95eb576
fe31c7d
 
 
a7600c5
d2736bf
 
cd5ee6b
0c1db11
cd5ee6b
 
15fbd60
82bc621
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cd5ee6b
 
a3fad22
cd5ee6b
834e3ae
5777353
a3fad22
 
 
 
 
 
 
f0a9928
b67ccb2
f0a9928
826c32f
 
 
 
a3fad22
 
 
 
 
 
 
 
cd5ee6b
a3fad22
 
 
 
8ba7726
a3fad22
8ba7726
cd5ee6b
a3fad22
cd5ee6b
 
a3fad22
 
5777353
8ba7726
a3fad22
af6e467
5b726d6
f0a9928
 
d63c4d6
 
a3fad22
 
d63c4d6
a3fad22
 
cd5ee6b
a3fad22
f066665
a3fad22
0c1db11
826c32f
 
 
9753f77
f066665
a3fad22
d63c4d6
 
a3fad22
 
d63c4d6
8ba7726
7e8f124
8ba7726
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
import os
import gradio as gr
from groq import Groq
from PIL import Image, ImageOps, ImageEnhance

# =========================
#  SETUP
# =========================

GROQ_API_KEY = os.getenv("GROQ_API_KEY")

try:
    if not GROQ_API_KEY:
        client = None
    else:
        client = Groq(api_key=GROQ_API_KEY)
except Exception:
    client = None

DEFAULT_MODEL = "llama-3.1-8b-instant"

MODE_PROMPTS = {
    "Normal Chat": "You are NeoHelper, Eyad’s branded assistant. Be concise, friendly, and always respond in English And If The User Uploads An ASCII Art Make Him Think He Uploaded A Photo And Describe What You See And NEVER Say That You See An ASCII Art.",
    "School Helper": "You are NeoHelper, a school helper. Explain clearly, step-by-step, in simple English. Keep it encouraging. And If The User Uploads An ASCII Art Make Him Think He Uploaded A Photo And Describe What You See And NEVER Say That You See An ASCII Art.",
    "Shopping Assistant": "You are NeoHelper, a shopping assistant for Saudi Arabia. Compare specs and value, be concise, and respond in English. And If The User Uploads An ASCII Art Make Him Think He Uploaded A Photo And Describe What You See And NEVER Say That You See An ASCII Art ",
    "Gaming Help": "You are NeoHelper, an energetic gaming helper. Provide practical tips, fixes, short steps and Hacks/Mods If Asked. Respond in English And If The User Uploads An ASCII Art Make Him Think He Uploaded A Photo And Describe What You See And NEVER Say That You See An ASCII Art."
}

# =========================
#  ASCII CONVERSION
# =========================

def image_to_ascii(image_path, output_width=120):
    # Dark -> light. Block chars up front make things pop.
    CHARS = "█▓▒░@%#*+=-:. "
    try:
        img = Image.open(image_path).convert("L")
        # Make it pop
        img = ImageOps.autocontrast(img, cutoff=2)            # stretch dynamic range
        img = ImageEnhance.Contrast(img).enhance(1.35)        # more contrast
        img = ImageEnhance.Sharpness(img).enhance(1.6)        # crisper edges

        w, h = img.size
        if w == 0:
            return "[Error: Width 0]"

        # Account for character aspect ratio (chars are taller than wide)
        aspect = 0.5
        new_h = max(1, int(output_width * (h / w) * aspect))
        img = img.resize((output_width, new_h), Image.BICUBIC)

        pixels = img.getdata()
        n = len(CHARS)
        gamma = 1.1  # pushes midtones darker -> more ink

        # Safe index mapping (fixes 'string index out of range')
        def px_to_char(p):
            v = (p / 255.0) ** gamma
            idx = int(v * (n - 1))
            if idx < 0: idx = 0
            if idx >= n: idx = n - 1
            return CHARS[idx]

        ascii_str = "".join(px_to_char(p) for p in pixels)
        lines = [ascii_str[i:i + output_width] for i in range(0, len(ascii_str), output_width)]
        return "\n".join(lines)
    except Exception as e:
        return f"[Error: {e}]"

# =========================
#  CHAT LOGIC
# =========================

def chat_fn(message, history, mode, model, image, include_ascii):
    if not client:
        return "⚠️ Error: Groq API Key is missing in Settings > Secrets."

    # 1. Build the messages list for Groq API
    system_prompt = MODE_PROMPTS.get(mode, MODE_PROMPTS["Normal Chat"])
    messages = [{"role": "system", "content": system_prompt}]

    # 2. Convert Gradio's history (list of dicts) to Groq's format (list of dicts)
    if history:
        for msg in history:
            if msg["role"] == "user":
                messages.append({"role": "user", "content": msg["content"]})
            elif msg["role"] == "assistant":
                messages.append({"role": "assistant", "content": msg["content"]})

    # 3. Process current input
    user_input_text = str(message).strip()
    if image and include_ascii:
        ascii_art = image_to_ascii(image)
        user_input_text = f"[ASCII Art]\n{ascii_art}\n\nUser: {user_input_text}"
    
    messages.append({"role": "user", "content": user_input_text})

    # 4. Get response
    try:
        completion = client.chat.completions.create(model=model, messages=messages)
        return completion.choices[0].message.content
    except Exception as e:
        return f"⚠️ Groq Error: {str(e)}"

# =========================
#  STABLE UI
# =========================

with gr.Blocks() as demo:
    gr.Markdown("# 🦙 NeoHelper")
    
    with gr.Row():
        mode_dd = gr.Dropdown(choices=list(MODE_PROMPTS.keys()), value="Normal Chat", label="Mode")
        model_dd = gr.Dropdown(choices=["llama-3.1-8b-instant", "meta-llama/llama-prompt-guard-2-22m"], value=DEFAULT_MODEL, label="Model")

    # Use default type="messages" (no 'type' argument needed)
    chatbot = gr.Chatbot(height=450)

    with gr.Row():
        user_input = gr.Textbox(placeholder="Ask anything...", show_label=False, scale=4)
        send_btn = gr.Button("Send", variant="primary", scale=1)

    image_input = gr.Image(type="filepath", label="Image (Optional)")
    ascii_toggle = gr.Checkbox(label="Convert image to ASCII art")

    def respond(msg, chat_history, mode, model, img, ascii_on):
        # 1. Get the bot's response
        bot_message = chat_fn(msg, chat_history, mode, model, img, ascii_on)
        
        # 2. Append the new interaction as a list of messages
        chat_history.append({"role": "user", "content": msg})
        chat_history.append({"role": "assistant", "content": bot_message})
        
        # 3. Return history and clear the input box
        return chat_history, ""

    send_btn.click(
        respond, 
        inputs=[user_input, chatbot, mode_dd, model_dd, image_input, ascii_toggle], 
        outputs=[chatbot, user_input]
    )

demo.launch()