File size: 5,805 Bytes
1b38145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df0ccf3
 
1b38145
 
 
 
 
df0ccf3
 
 
 
 
1b38145
 
 
 
 
 
 
 
 
 
 
 
 
 
df0ccf3
 
 
 
 
 
 
 
 
1b38145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df0ccf3
 
 
 
1b38145
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Multimodal Assistant</title>
<style>
  body { font-family: 'Segoe UI', sans-serif; max-width: 750px; margin: 30px auto; background:#0f0f0f; color:#eee; }
  h2 { text-align:center; }
  #chat { border: 1px solid #333; border-radius: 8px; padding: 15px; height: 450px; overflow-y: auto; background:#1a1a1a; }
  .msg { margin: 10px 0; line-height:1.4; }
  .msg b { color:#7dd3fc; }
  .msg.user b { color:#fca5a5; }
  .msg img { max-width: 250px; border-radius:6px; display:block; margin-top:6px; }
  #controls { display:flex; gap:8px; margin-top:12px; flex-wrap:wrap; }
  #userInput { flex:1; padding:10px; border-radius:6px; border:1px solid #333; background:#1a1a1a; color:#eee; min-width:200px; }
  button, select { padding:10px 14px; border-radius:6px; border:none; background:#3b82f6; color:white; cursor:pointer; }
  button:hover { background:#2563eb; }
  #status { font-size:12px; color:#888; margin-top:6px; }
  #tokenBar { display:flex; gap:8px; margin-bottom:12px; }
  #tokenInput { flex:1; padding:8px; border-radius:6px; border:1px solid #333; background:#1a1a1a; color:#eee; }
</style>
</head>
<body>
  <h2>🧠 My Multimodal Assistant</h2>

  <div id="tokenBar">
    <input type="password" id="tokenInput" placeholder="Paste your HF token here (not saved, session only)">
    <button onclick="saveToken()">Set Token</button>
  </div>

  <div id="chat"></div>

  <div id="controls">
    <select id="mode">
      <option value="chat">💬 Chat / Ask about image</option>
      <option value="image">🎨 Generate an image</option>
    </select>
    <input type="text" id="userInput" placeholder="Type your message...">
    <input type="file" id="imageInput" accept="image/*">
    <button onclick="send()">Send</button>
  </div>
  <div id="status"></div>

<script>
  let HF_TOKEN = ""; // set at runtime, never committed to the file

  function saveToken() {
    HF_TOKEN = document.getElementById("tokenInput").value.trim();
    document.getElementById("tokenInput").value = "";
    document.getElementById("tokenInput").placeholder = HF_TOKEN
      ? "✅ Token set for this session"
      : "Paste your HF token here";
  }

  const chatBox = document.getElementById("chat");
  const statusBox = document.getElementById("status");
  let history = [];

  function addMessage(role, html) {
    const div = document.createElement("div");
    div.className = "msg" + (role === "You" ? " user" : "");
    div.innerHTML = `<b>${role}:</b> ${html}`;
    chatBox.appendChild(div);
    chatBox.scrollTop = chatBox.scrollHeight;
  }

  function toBase64(file) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onload = () => resolve(reader.result);
      reader.onerror = reject;
      reader.readAsDataURL(file);
    });
  }

  async function send() {
    if (!HF_TOKEN) {
      alert("Please set your Hugging Face token first (top of the page).");
      return;
    }
    const text = document.getElementById("userInput").value.trim();
    const file = document.getElementById("imageInput").files[0];
    const mode = document.getElementById("mode").value;
    if (!text && !file) return;

    if (mode === "image") {
      await generateImage(text);
    } else {
      await chatWithModel(text, file);
    }
    document.getElementById("userInput").value = "";
    document.getElementById("imageInput").value = "";
  }

  async function chatWithModel(text, file) {
    let content = [{ type: "text", text: text || "Describe this image." }];
    let userDisplay = text;

    if (file) {
      const base64 = await toBase64(file);
      content.push({ type: "image_url", image_url: { url: base64 } });
      userDisplay += `<img src="${base64}">`;
    }
    addMessage("You", userDisplay);
    statusBox.textContent = "Thinking...";

    const model = file
      ? "Qwen/Qwen2.5-VL-7B-Instruct"
      : "meta-llama/Llama-3.3-70B-Instruct";

    history.push({ role: "user", content: content });

    try {
      const res = await fetch("https://router.huggingface.co/v1/chat/completions", {
        method: "POST",
        headers: {
          "Authorization": `Bearer ${HF_TOKEN}`,
          "Content-Type": "application/json"
        },
        body: JSON.stringify({ model: model, messages: history })
      });
      const data = await res.json();
      if (data.error) throw new Error(data.error.message || JSON.stringify(data.error));

      const reply = data.choices[0].message.content;
      addMessage("AI", reply);
      history.push({ role: "assistant", content: reply });
      statusBox.textContent = "";
    } catch (err) {
      addMessage("AI", "⚠️ Error: " + err.message);
      statusBox.textContent = "";
    }
  }

  async function generateImage(prompt) {
    addMessage("You", prompt);
    statusBox.textContent = "Generating image (can take 10–30s)...";

    try {
      const res = await fetch(
        "https://router.huggingface.co/hf-inference/models/black-forest-labs/FLUX.1-schnell",
        {
          method: "POST",
          headers: {
            "Authorization": `Bearer ${HF_TOKEN}`,
            "Content-Type": "application/json"
          },
          body: JSON.stringify({ inputs: prompt })
        }
      );
      if (!res.ok) {
        const errText = await res.text();
        throw new Error(errText);
      }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      addMessage("AI", `<img src="${url}">`);
      statusBox.textContent = "";
    } catch (err) {
      addMessage("AI", "⚠️ Error: " + err.message);
      statusBox.textContent = "";
    }
  }

  document.getElementById("userInput").addEventListener("keydown", e => {
    if (e.key === "Enter") send();
  });
</script>
</body>
</html>