kvyb commited on
Commit
ec12f4a
·
verified ·
1 Parent(s): 3b100db

Add system prompt and new chat controls

Browse files
Files changed (1) hide show
  1. index.html +77 -11
index.html CHANGED
@@ -20,10 +20,25 @@
20
  margin: auto;
21
  }
22
  header { padding: 14px 16px; border-bottom: 1px solid #e5e7eb; }
 
23
  h1 { margin: 0; font-size: 15px; font-weight: 600; }
24
  header p { margin: 4px 0 0; color: #6b7280; font-size: 12px; }
25
  header a { color: #4f46e5; }
26
  code { color: #374151; overflow-wrap: anywhere; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  #chat { padding: 20px 16px; overflow-y: auto; }
28
  .message {
29
  max-width: 80%;
@@ -62,10 +77,23 @@
62
  font: inherit;
63
  font-weight: 600;
64
  cursor: pointer;
65
- align-self: end;
66
  }
67
  button:disabled { opacity: 0.5; cursor: wait; }
68
- textarea:focus, button:focus-visible { outline: 2px solid #6366f1; outline-offset: 2px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  #status { min-height: 32px; margin: 0; padding: 0 16px 12px; color: #6b7280; font-size: 12px; }
70
  #status.busy::before {
71
  content: "";
@@ -84,33 +112,42 @@
84
  :root { color-scheme: dark; }
85
  body { color: #f3f4f6; background: #111827; }
86
  header, form { border-color: #374151; }
87
- header p, #status, .pending { color: #9ca3af; }
 
88
  code { color: #d1d5db; }
 
89
  .assistant { background: #1f2937; }
90
  .error { color: #fecaca; background: #450a0a; }
91
- textarea { color: #f3f4f6; background: #1f2937; border-color: #4b5563; }
 
92
  #status.busy::before { border-color: #4b5563; border-top-color: #818cf8; }
93
  }
94
  @media (max-width: 620px) {
95
  .message { max-width: 92%; }
96
  form { grid-template-columns: 1fr; }
97
- button { width: 100%; }
98
  }
99
  </style>
100
  </head>
101
  <body>
102
  <main>
103
  <header>
104
- <h1>Qwen3.8-27B Humanlike Chat</h1>
 
 
 
105
  <p>
106
  Live demo of the hosted endpoint <code>https://api.lessthanthreeai.com/v1</code>.
107
  <a href="https://huggingface.co/LessThanThreeAI/Qwen3.8-27B-Humanlike-Chat-GGUF">Model card</a>
108
  </p>
 
 
 
 
 
109
  </header>
110
 
111
- <div id="chat" aria-live="polite">
112
- <div class="message assistant">Say something. The first reply can take a couple of minutes while the inference worker starts.</div>
113
- </div>
114
 
115
  <form id="form">
116
  <textarea id="prompt" required aria-label="Message" placeholder="Type a message"></textarea>
@@ -123,12 +160,22 @@
123
  <script>
124
  const API = "https://api.lessthanthreeai.com/v1";
125
  const MODEL = "qwen3.8-27b-humanlike-chat";
126
- const messages = [];
 
127
  const chat = document.querySelector("#chat");
128
  const form = document.querySelector("#form");
129
  const prompt = document.querySelector("#prompt");
130
  const send = document.querySelector("#send");
131
  const status = document.querySelector("#status");
 
 
 
 
 
 
 
 
 
132
 
133
  function addMessage(role, text) {
134
  const element = document.createElement("div");
@@ -139,6 +186,16 @@
139
  return element;
140
  }
141
 
 
 
 
 
 
 
 
 
 
 
142
  // Server-sent events arrive as "data: {...}" chunks. The endpoint may also send
143
  // ": status={...}" comment lines while a cold worker starts.
144
  function applyEvent(line, output) {
@@ -166,6 +223,8 @@
166
  }
167
  }
168
 
 
 
169
  form.addEventListener("submit", async (event) => {
170
  event.preventDefault();
171
  const text = prompt.value.trim();
@@ -182,13 +241,18 @@
182
  output.classList.add("pending");
183
  output.textContent = "waiting for the first token";
184
 
 
 
 
 
 
185
  try {
186
  const response = await fetch(`${API}/chat/completions`, {
187
  method: "POST",
188
  headers: { "content-type": "application/json" },
189
  body: JSON.stringify({
190
  model: MODEL,
191
- messages,
192
  stream: true,
193
  max_tokens: 512,
194
  }),
@@ -230,6 +294,8 @@
230
  prompt.focus();
231
  }
232
  });
 
 
233
  </script>
234
  </body>
235
  </html>
 
20
  margin: auto;
21
  }
22
  header { padding: 14px 16px; border-bottom: 1px solid #e5e7eb; }
23
+ .row { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
24
  h1 { margin: 0; font-size: 15px; font-weight: 600; }
25
  header p { margin: 4px 0 0; color: #6b7280; font-size: 12px; }
26
  header a { color: #4f46e5; }
27
  code { color: #374151; overflow-wrap: anywhere; }
28
+ details { margin-top: 10px; }
29
+ summary { color: #6b7280; font-size: 12px; cursor: pointer; }
30
+ summary:hover { color: #4f46e5; }
31
+ #system {
32
+ width: 100%;
33
+ min-height: 76px;
34
+ margin-top: 8px;
35
+ padding: 8px 10px;
36
+ border: 1px solid #d1d5db;
37
+ border-radius: 8px;
38
+ font: inherit;
39
+ font-size: 13px;
40
+ resize: vertical;
41
+ }
42
  #chat { padding: 20px 16px; overflow-y: auto; }
43
  .message {
44
  max-width: 80%;
 
77
  font: inherit;
78
  font-weight: 600;
79
  cursor: pointer;
 
80
  }
81
  button:disabled { opacity: 0.5; cursor: wait; }
82
+ #new {
83
+ padding: 6px 12px;
84
+ color: #4f46e5;
85
+ background: transparent;
86
+ border: 1px solid #d1d5db;
87
+ border-radius: 8px;
88
+ font-size: 13px;
89
+ font-weight: 500;
90
+ }
91
+ #new:hover { background: #f3f4f6; }
92
+ #send { align-self: end; }
93
+ textarea:focus, button:focus-visible, summary:focus-visible {
94
+ outline: 2px solid #6366f1;
95
+ outline-offset: 2px;
96
+ }
97
  #status { min-height: 32px; margin: 0; padding: 0 16px 12px; color: #6b7280; font-size: 12px; }
98
  #status.busy::before {
99
  content: "";
 
112
  :root { color-scheme: dark; }
113
  body { color: #f3f4f6; background: #111827; }
114
  header, form { border-color: #374151; }
115
+ header p, #status, .pending, summary { color: #9ca3af; }
116
+ summary:hover { color: #818cf8; }
117
  code { color: #d1d5db; }
118
+ #system, textarea { color: #f3f4f6; background: #1f2937; border-color: #4b5563; }
119
  .assistant { background: #1f2937; }
120
  .error { color: #fecaca; background: #450a0a; }
121
+ #new { color: #a5b4fc; border-color: #4b5563; }
122
+ #new:hover { background: #1f2937; }
123
  #status.busy::before { border-color: #4b5563; border-top-color: #818cf8; }
124
  }
125
  @media (max-width: 620px) {
126
  .message { max-width: 92%; }
127
  form { grid-template-columns: 1fr; }
128
+ #send { width: 100%; }
129
  }
130
  </style>
131
  </head>
132
  <body>
133
  <main>
134
  <header>
135
+ <div class="row">
136
+ <h1>Qwen3.8-27B Humanlike Chat</h1>
137
+ <button id="new" type="button">New chat</button>
138
+ </div>
139
  <p>
140
  Live demo of the hosted endpoint <code>https://api.lessthanthreeai.com/v1</code>.
141
  <a href="https://huggingface.co/LessThanThreeAI/Qwen3.8-27B-Humanlike-Chat-GGUF">Model card</a>
142
  </p>
143
+ <details>
144
+ <summary>System prompt</summary>
145
+ <textarea id="system" aria-label="System prompt"
146
+ placeholder="Optional. Sets identity, setting, or relationship for the whole conversation."></textarea>
147
+ </details>
148
  </header>
149
 
150
+ <div id="chat" aria-live="polite"></div>
 
 
151
 
152
  <form id="form">
153
  <textarea id="prompt" required aria-label="Message" placeholder="Type a message"></textarea>
 
160
  <script>
161
  const API = "https://api.lessthanthreeai.com/v1";
162
  const MODEL = "qwen3.8-27b-humanlike-chat";
163
+ const INTRO = "Say something. The first reply can take a couple of minutes while the inference worker starts.";
164
+
165
  const chat = document.querySelector("#chat");
166
  const form = document.querySelector("#form");
167
  const prompt = document.querySelector("#prompt");
168
  const send = document.querySelector("#send");
169
  const status = document.querySelector("#status");
170
+ const system = document.querySelector("#system");
171
+ const newChat = document.querySelector("#new");
172
+
173
+ let messages = [];
174
+
175
+ system.value = localStorage.getItem("systemPrompt") || "";
176
+ system.addEventListener("input", () => {
177
+ localStorage.setItem("systemPrompt", system.value);
178
+ });
179
 
180
  function addMessage(role, text) {
181
  const element = document.createElement("div");
 
186
  return element;
187
  }
188
 
189
+ function startSession(message) {
190
+ messages = [];
191
+ chat.replaceChildren();
192
+ addMessage("assistant", message);
193
+ status.textContent = "";
194
+ status.classList.remove("busy");
195
+ prompt.value = "";
196
+ prompt.focus();
197
+ }
198
+
199
  // Server-sent events arrive as "data: {...}" chunks. The endpoint may also send
200
  // ": status={...}" comment lines while a cold worker starts.
201
  function applyEvent(line, output) {
 
223
  }
224
  }
225
 
226
+ newChat.addEventListener("click", () => startSession(INTRO));
227
+
228
  form.addEventListener("submit", async (event) => {
229
  event.preventDefault();
230
  const text = prompt.value.trim();
 
241
  output.classList.add("pending");
242
  output.textContent = "waiting for the first token";
243
 
244
+ const systemPrompt = system.value.trim();
245
+ const request = systemPrompt
246
+ ? [{ role: "system", content: systemPrompt }, ...messages]
247
+ : messages;
248
+
249
  try {
250
  const response = await fetch(`${API}/chat/completions`, {
251
  method: "POST",
252
  headers: { "content-type": "application/json" },
253
  body: JSON.stringify({
254
  model: MODEL,
255
+ messages: request,
256
  stream: true,
257
  max_tokens: 512,
258
  }),
 
294
  prompt.focus();
295
  }
296
  });
297
+
298
+ startSession(INTRO);
299
  </script>
300
  </body>
301
  </html>