ashwath-vaithina-ibm commited on
Commit
f148d6c
·
verified ·
1 Parent(s): c89e1ec

Update static/demo/index.html

Browse files
Files changed (1) hide show
  1. static/demo/index.html +59 -965
static/demo/index.html CHANGED
@@ -4,371 +4,76 @@
4
  <head>
5
  <meta charset="UTF-8" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
- <title>Responsible Prompting MultiTurn Chat + Graph</title>
8
 
9
  <!-- IBM Plex Sans -->
10
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&display=swap"
11
  rel="stylesheet" />
12
 
13
  <!-- Carbon CSS (for tabs and tags) -->
14
- <link rel="stylesheet" href="static/styles/carbon-components.min.css" />
15
 
16
- <script type="text/javascript" src="static/demo/js/d3.v7.min.js"></script>
17
- <script type="text/javascript" src="static/demo/js/jquery-3.7.1.min.js"></script>
18
 
19
- <style>
20
- /* ================== Global ================== */
21
- html,
22
- body {
23
- margin: 0;
24
- padding: 0;
25
- height: 100vh;
26
- /* background-color: #f4f4f4; */
27
- font-family: "IBM Plex Sans", sans-serif;
28
- color: #161616;
29
- }
30
 
31
- body {
32
- display: flex;
33
- flex-direction: column;
34
- overflow: hidden;
35
- /* center within a max width */
36
- max-width: 1000px;
37
- margin: 0 auto;
38
- padding: 1rem 2rem;
39
- box-sizing: border-box;
40
- }
41
-
42
- a {
43
- color: #0f62fe;
44
- text-decoration: none;
45
- }
46
-
47
- /* ================== Header & Tabs ================== */
48
- .header-container {
49
- /* background: #ffffff; */
50
- padding: 1rem 0;
51
- border-bottom: 1px solid #dddddd;
52
- box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.05);
53
- }
54
-
55
- .bx--tabs__nav {
56
- position: relative;
57
- display: flex;
58
- list-style: none;
59
- padding: 0;
60
- margin-top: 1em;
61
- border-bottom: 2px solid #c6c6c6;
62
- }
63
-
64
- .bx--tabs__nav-item {
65
- text-align: center;
66
- justify-content: center;
67
- display: block;
68
- padding: 0.5rem 0.75rem;
69
- cursor: pointer;
70
- font-size: 1rem;
71
- color: #393939;
72
- border-bottom: 2px solid transparent;
73
- font-weight: 500;
74
- transition: color 0.2s;
75
- }
76
-
77
- .bx--tabs__nav-item:hover {
78
- color: #0f62fe;
79
- }
80
-
81
- .bx--tabs__nav-item--selected {
82
- font-weight: 600;
83
- border-bottom: 2px solid #0f62fe !important;
84
- color: #161616;
85
- }
86
-
87
- /* ================== Main Content Container ================== */
88
- .tab-content {
89
- flex: 1;
90
- display: flex;
91
- flex-direction: column;
92
- min-height: 0;
93
- }
94
-
95
- /* ================== Chat vs. Graph ================== */
96
- #chat-content {
97
- display: flex;
98
- flex-direction: column;
99
- flex: 1;
100
- overflow: hidden;
101
- background: #ffffff;
102
- border-radius: 8px;
103
- box-sizing: border-box;
104
- min-height: 0;
105
- }
106
-
107
- #graph-content {
108
- display: none;
109
- flex: 1;
110
- overflow: auto;
111
- padding: 1rem;
112
- background: #ffffff;
113
- border-radius: 8px;
114
- box-sizing: border-box;
115
- }
116
-
117
- /* ================== Chat Container ================== */
118
- .chat-container {
119
- flex: 1;
120
- overflow-y: auto;
121
- padding: 0.5rem;
122
- display: flex;
123
- flex-direction: column;
124
- gap: 0.25rem;
125
- }
126
-
127
- /* ================== Message Bubbles ================== */
128
- .message {
129
- max-width: 70%;
130
- padding: 0.75rem 1rem;
131
- border-radius: 0.75rem;
132
- line-height: 1.5;
133
- word-wrap: break-word;
134
- white-space: pre-wrap;
135
- position: relative;
136
- margin-bottom: 0.5rem;
137
- }
138
-
139
- .message.user {
140
- background-color: #f2f4f8;
141
- align-self: flex-end;
142
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.1);
143
- }
144
-
145
- .message.assistant {
146
- background-color: white;
147
- align-self: flex-start;
148
- padding-left: 2.5rem;
149
- }
150
-
151
- /* ================== Model Info Bar ================== */
152
- .model-info {
153
- font-size: 0.85rem;
154
- color: #525252;
155
- padding: 0.25rem 0.75rem;
156
- border-radius: 0.5rem;
157
- background-color: white;
158
- align-self: flex-start;
159
- }
160
-
161
- /* ================== Recommendations Tags ================== */
162
- .recs-container {
163
- max-width: 70%;
164
- display: flex;
165
- flex-wrap: nowrap;
166
- gap: 0.5rem;
167
- margin-bottom: 1rem;
168
- overflow-x: auto;
169
- scrollbar-width: none;
170
- white-space: nowrap;
171
- flex-shrink: 0;
172
- }
173
-
174
- .recs-item {
175
- font-size: 0.85rem;
176
- padding: 0.25rem 0.5rem;
177
- border-radius: 0.25rem;
178
- color: #ffffff;
179
- flex-shrink: 0;
180
- }
181
-
182
- .recs-item.add {
183
- background-color: #24a148;
184
- }
185
-
186
- .recs-item.remove {
187
- background-color: #da1e28;
188
- }
189
-
190
- /* ================== Input Area ================== */
191
- .input-area {
192
- border-top: 1px solid #dddddd;
193
- padding: 1rem;
194
- display: flex;
195
- flex-direction: column;
196
- background: #ffffff;
197
- border-bottom-left-radius: 8px;
198
- border-bottom-right-radius: 8px;
199
- box-shadow: 0px 1px 3px rgba(0, 0, 0, 0.05);
200
- gap: 0.5rem;
201
- box-sizing: border-box;
202
- transition: border-color 0.2s, box-shadow 0.2s;
203
- border: 1px solid #c6c6c6;
204
- border-radius: 0.5rem;
205
- }
206
-
207
- .input-area:focus-within {
208
- outline: none;
209
- border-color: #0f62fe;
210
- box-shadow: 0px 0px 0px 2px rgba(15, 98, 254, 0.2);
211
- }
212
-
213
- /* Contenteditable box wrapper */
214
- #userInput {
215
- flex: 1;
216
- display: flex;
217
- flex-direction: column;
218
- }
219
-
220
- /* The editable area itself */
221
- #userInput>div[contenteditable] {
222
- min-height: 2.5rem;
223
- font-size: 1rem;
224
- line-height: 1.5;
225
- font-family: inherit;
226
- resize: none;
227
- background-color: #ffffff;
228
- }
229
-
230
- [contenteditable]:focus {
231
- outline: 0px solid transparent;
232
- }
233
-
234
- div[contenteditable] {
235
- height: 10vh;
236
- overflow: scroll;
237
- }
238
-
239
-
240
- /* Send button wrapper */
241
- .btn {
242
- border: none;
243
- border-radius: 50%;
244
- display: flex;
245
- align-items: center;
246
- justify-content: center;
247
- cursor: pointer;
248
- transition: background-color 0.2s;
249
- background-color: white;
250
- }
251
-
252
- .btn:disabled {
253
- cursor: not-allowed;
254
- }
255
-
256
- .icon {
257
- width: 1.25rem;
258
- height: 1.25rem;
259
- }
260
-
261
- #modelSelect {
262
- border: 1px solid #c6c6c6;
263
- border-radius: 5px;
264
- padding: 0.25rem 0.25rem;
265
- }
266
-
267
- /* ================== Recommendation Tag Container ================== */
268
- #recommendation {
269
- flex: 1;
270
- font-size: 0.9rem;
271
- color: #393939;
272
- display: flex;
273
- gap: 0.5rem;
274
- min-height: 1.5rem;
275
- overflow-x: scroll;
276
- scrollbar-width: none;
277
- white-space: nowrap;
278
- align-items: center;
279
- }
280
-
281
- .rec-tags-inputarea {
282
- flex: 0 0 auto;
283
- }
284
-
285
- /* ================== Graph Area ================== */
286
- #graph {
287
- background: #efefef;
288
- position: relative;
289
- min-height: 300px;
290
- border-radius: 8px;
291
- }
292
-
293
- .tooltip {
294
- position: absolute;
295
- text-align: left;
296
- padding: 0.5em;
297
- width: 20em;
298
- min-height: 5em;
299
- background: #ffffff;
300
- color: #000000;
301
- border: 1px solid #000000;
302
- border-radius: 5px;
303
- pointer-events: none;
304
- font-size: inherit;
305
- font-family: inherit;
306
- box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1);
307
- z-index: 10;
308
- }
309
- </style>
310
  </head>
311
 
312
  <body>
 
 
 
 
 
 
313
  <!-- ===== Header: Title + Carbon Tabs ===== -->
314
  <div class="header-container">
315
- <div style="display: flex; flex-direction: row;">
316
- <h4 style="width: 40%; display: flex; padding-left: 1rem; font-size: xx-large; font-weight: 300;">Responsible Prompting</h4>
317
- <div style="width: 60%; padding-left: 1rem;" class="intro">
 
 
 
 
318
  <p>
319
- Please provide a prompt that would be sent to an LLM. Recommendations are performed in prompting-time, before content generation. The recommendations consider a curated dataset of values and prompt sentences. They are based on the similarity between your input and that dataset.
320
  </p>
321
  </div>
322
  </div>
323
- <!-- <ol class="bx--tabs__nav">
324
- <li id="tab-chat" class="bx--tabs__nav-item bx--tabs__nav-item--selected">
325
- Chat
326
- </li>
327
- <li id="tab-graph" class="bx--tabs__nav-item">
328
- Graph
329
- </li>
330
- </ol> -->
331
  </div>
332
- <!-- <button class="btn bx--btn bx--btn--ghost" style="position: absolute; top: 0; right: 0; margin-top: 0.5rem; margin-right: 0.5rem;">
333
- <img class="icon" src="static/demo/imgs/settings.svg"/>
334
- </button> -->
335
 
336
- <div class="tab-content">
337
  <!-- === Chat View === -->
338
  <div id="chat-content">
339
  <div id="chat" class="chat-container"></div>
340
 
341
  <!-- Input area -->
342
  <div class="input-area">
343
- <div id="userInput">
344
- <div id="userInputDiv" contenteditable="true" placeholder="Enter your prompt">Act as a professional designer with 20 years of experience creating and testing UX interfaces and landing sites for a variety of IT applications. We are in need of more people and an increased budget to be able to keep up with clients' needs. What kind of evidence should I gather to support my demands to gain more resources?
345
- </div>
346
- </div>
347
 
348
- <div style="display: flex; justify-content: space-between; gap: 1rem;">
349
  <div id="recommendation"></div>
350
- <select id="modelSelect"></select>
351
  <!-- Send button -->
352
- <button id="sendBtn" class="btn" disabled>
353
- <img src="static/demo/imgs/send.svg" alt="Send" class="icon"/>
354
  </button>
355
  </div>
356
  </div>
357
- <!-- Under the input row: recommendation tags -->
358
- </div>
359
 
360
- <!-- === Graph View ===
361
- <div id="graph-content">
362
- <div id="graph"></div>
363
- </div> -->
364
- </div>
365
 
366
  <script>
367
- const models = [
368
- { id: 'mistralai/Mistral-7B-Instruct-v0.3', name: 'Mistral 7B Instruct v0.3' },
369
- { id: 'meta-llama/Llama-4-Scout-17B-16E-Instruct', name: 'Llama 4 Scout' },
370
- ];
371
- function createModelSelect() {
 
 
 
372
  const modelSelect = document.getElementById('modelSelect');
373
 
374
  models.forEach(model => {
@@ -377,646 +82,46 @@
377
  option.textContent = model.name;
378
  modelSelect.appendChild(option);
379
  });
380
- }
381
-
382
- // Call the function when the DOM is fully loaded
383
- document.addEventListener('DOMContentLoaded', createModelSelect);
384
 
 
 
385
 
386
- var modelId = models[0].id;
387
- const modelSelect = document.getElementById('modelSelect');
388
-
389
- modelSelect.addEventListener('change', function() {
390
- const selectedModel = models.find(model => model.id === this.value);
391
- modelId = selectedModel.id;
392
  });
393
- </script>
394
 
395
- <!-- To show the bottom of text in -->
396
- <script>
397
  var objDiv = document.getElementById("userInputDiv");
398
  objDiv.scrollTop = objDiv.scrollHeight;
399
- $("#userInputDiv").trigger("input");
400
- </script>
401
-
402
- <script>
403
- let lastRecommendations = [];
404
- const width = 600;
405
- const height = 300;
406
- const marginTop = 30;
407
- const marginRight = 30;
408
- const marginBottom = 30;
409
- const marginLeft = 30;
410
- const nodeRadius = 3;
411
-
412
- // Create SVG once
413
- var svg = d3
414
- .select("#graph")
415
- .append("svg")
416
- .attr('id', 'svgMain');
417
-
418
- const tooltip = d3
419
- .select("body")
420
- .append("div")
421
- .attr("class", "tooltip")
422
- .style("opacity", 0);
423
-
424
- var graphData = { nodes: [], edges: [] };
425
- function generateAndRenderGraph(recommendations, svgId) {
426
- svg = d3
427
- .select(svgId)
428
- .attr("viewBox", `0 0 ${width} ${height}`)
429
- .attr("style", "max-width: 100%; height: auto; font: 8px sans-serif;");
430
- if (!recommendations) {
431
- clearGraph();
432
- return;
433
- }
434
-
435
- const rec = recommendations;
436
- let i = 0,
437
- j = 0;
438
-
439
- graphData = { nodes: [], edges: [] };
440
-
441
- // Input sentences
442
- if (rec.input && rec.input.length > 0) {
443
- graphData.nodes.push({
444
- id: 0,
445
- x: Number(rec.input[0].x),
446
- y: Number(rec.input[0].y),
447
- text: rec.input[0].sentence,
448
- label: "S1",
449
- type: "input",
450
- });
451
- for (i = 1; i < rec.input.length; i++) {
452
- graphData.nodes.push({
453
- id: i,
454
- x: Number(rec.input[i].x),
455
- y: Number(rec.input[i].y),
456
- text: rec.input[i].sentence,
457
- label: `S${i + 1}`,
458
- type: "input",
459
- });
460
- graphData.edges.push({ source: i - 1, target: i, type: "input" });
461
- }
462
- }
463
-
464
- // “Add” recommendations
465
- if (rec.add && rec.add.length > 0) {
466
- for (j = 0; j < rec.add.length; j++) {
467
- graphData.nodes.push({
468
- id: i + j,
469
- x: Number(rec.add[j].x),
470
- y: Number(rec.add[j].y),
471
- text: rec.add[j].prompt,
472
- label: rec.add[j].value,
473
- type: "add",
474
- });
475
- graphData.edges.push({
476
- source: i - 1,
477
- target: i + j,
478
- type: "add",
479
- });
480
- }
481
- }
482
-
483
- // “Remove” recommendation (first only)
484
- if (rec.remove && rec.remove.length > 0) {
485
- graphData.nodes.push({
486
- id: i + j,
487
- x: Number(rec.remove[0].x),
488
- y: Number(rec.remove[0].y),
489
- text: rec.remove[0].closest_harmful_sentence,
490
- label: rec.remove[0].value,
491
- type: "remove",
492
- });
493
- }
494
-
495
- graphData.edges = graphData.edges.map((e) => ({
496
- source: graphData.nodes.find((n) => n.id === e.source),
497
- target: graphData.nodes.find((n) => n.id === e.target),
498
- type: e.type,
499
- }));
500
-
501
- renderGraph(graphData, svgId)
502
- }
503
-
504
- function clearGraph() {
505
- svg.selectAll("*").remove();
506
- svg
507
- .append("text")
508
- .attr("x", width / 2)
509
- .attr("y", height / 2)
510
- .attr("text-anchor", "middle")
511
- .attr("fill", "#666")
512
- .text("No recommendation data available");
513
- }
514
-
515
- function renderGraph(graphData, svgId) {
516
- svg = d3
517
- .select(svgId)
518
- .attr("viewBox", `0 0 ${width} ${height}`)
519
- .attr("style", "max-width: 100%; height: auto; font: 8px sans-serif;");
520
-
521
- let { nodes, edges } = graphData;
522
-
523
- if (nodes.length === 0) {
524
- svg.selectAll("*").remove();
525
- svg
526
- .append("text")
527
- .attr("x", width / 2)
528
- .attr("y", height / 2)
529
- .attr("text-anchor", "middle")
530
- .attr("fill", "#666")
531
- .text("No nodes to display");
532
- return;
533
- }
534
-
535
- const xDomain = d3.extent(nodes, (d) => d.x);
536
- const yDomain = d3.extent(nodes, (d) => d.y);
537
- const xPadding = 2;
538
- const yPadding = 2;
539
-
540
- const xScale = d3
541
- .scaleLinear()
542
- .domain([xDomain[0] - xPadding, xDomain[1] + xPadding])
543
- .nice()
544
- .range([marginLeft, width - marginRight]);
545
-
546
- const yScale = d3
547
- .scaleLinear()
548
- .domain([yDomain[0] - yPadding, yDomain[1] + yPadding])
549
- .nice()
550
- .range([height - marginBottom, marginTop]);
551
-
552
- svg.selectAll("*").remove();
553
-
554
- // X axis
555
- svg
556
- .append("g")
557
- .attr("transform", `translate(0, ${height - marginBottom})`)
558
- .call(d3.axisBottom(xScale).ticks(width / 80))
559
- .call((g) => g.select(".domain").remove())
560
- .call((g) =>
561
- g
562
- .append("text")
563
- .attr("x", width)
564
- .attr("y", marginBottom - 4)
565
- .attr("fill", "currentColor")
566
- .attr("text-anchor", "end")
567
- .text("Semantic dimension 1")
568
- );
569
-
570
- // Y axis
571
- svg
572
- .append("g")
573
- .attr("transform", `translate(${marginLeft}, 0)`)
574
- .call(d3.axisLeft(yScale))
575
- .call((g) => g.select(".domain").remove())
576
- .call((g) =>
577
- g
578
- .append("text")
579
- .attr("x", -marginLeft)
580
- .attr("y", 10)
581
- .attr("fill", "currentColor")
582
- .attr("text-anchor", "start")
583
- .text("Semantic dimension 2")
584
- );
585
-
586
- // Grid
587
- svg
588
- .append("g")
589
- .attr("stroke", "#cccccc")
590
- .attr("stroke-opacity", 0.5)
591
- .call((g) =>
592
- g
593
- .append("g")
594
- .selectAll("line")
595
- .data(xScale.ticks())
596
- .join("line")
597
- .attr("x1", (d) => 0.5 + xScale(d))
598
- .attr("x2", (d) => 0.5 + xScale(d))
599
- .attr("y1", marginTop)
600
- .attr("y2", height - marginBottom)
601
- )
602
- .call((g) =>
603
- g
604
- .append("g")
605
- .selectAll("line")
606
- .data(yScale.ticks())
607
- .join("line")
608
- .attr("y1", (d) => 0.5 + yScale(d))
609
- .attr("y2", (d) => 0.5 + yScale(d))
610
- .attr("x1", marginLeft)
611
- .attr("x2", width - marginRight)
612
- );
613
-
614
- // Edges
615
- svg
616
- .append("g")
617
- .selectAll("line")
618
- .data(edges)
619
- .join("line")
620
- .attr("stroke", "#666")
621
- .attr("stroke-opacity", 0.5)
622
- .attr(
623
- "x1",
624
- (d) =>
625
- xScale(d.source.x) +
626
- (d.source.x < d.target.x ? 1.3 * nodeRadius : -1.3 * nodeRadius)
627
- )
628
- .attr("y1", (d) => yScale(d.source.y))
629
- .attr(
630
- "x2",
631
- (d) =>
632
- xScale(d.target.x) +
633
- (d.source.x > d.target.x ? 1.3 * nodeRadius : -1.3 * nodeRadius)
634
- )
635
- .attr("y2", (d) => yScale(d.target.y))
636
- .style("stroke-dasharray", (d) =>
637
- d.target.type === "add" ? "3,3" : ""
638
- );
639
-
640
- // Nodes
641
- svg
642
- .append("g")
643
- .attr("stroke-width", 2.5)
644
- .attr("stroke-opacity", 0.5)
645
- .attr("fill", "none")
646
- .selectAll("circle")
647
- .data(nodes)
648
- .join("circle")
649
- .attr("stroke", (d) =>
650
- d.type === "add" ? "green" : d.type === "input" ? "#666" : "red"
651
- )
652
- .attr("cx", (d) => xScale(d.x))
653
- .attr("cy", (d) => yScale(d.y))
654
- .attr("r", nodeRadius);
655
-
656
- // Labels
657
- svg
658
- .append("g")
659
- .attr("font-family", "sans-serif")
660
- .attr("text-opacity", 0.5)
661
- .attr("font-size", 8)
662
- .selectAll("text")
663
- .data(nodes)
664
- .join("text")
665
- .attr("dy", "0.35em")
666
- .attr("x", (d) => xScale(d.x) + 5)
667
- .attr("y", (d) => yScale(d.y))
668
- .text((d) => d.label)
669
- .on("mousemove", function (event, d) {
670
- d3.select(this)
671
- .transition()
672
- .duration(50)
673
- .attr("text-opacity", 1.0)
674
- .attr("stroke", "white")
675
- .attr("stroke-width", 3)
676
- .style("paint-order", "stroke fill")
677
- .attr(
678
- "fill",
679
- d.type === "add"
680
- ? "green"
681
- : d.type === "input"
682
- ? "black"
683
- : "red"
684
- );
685
- tooltip.transition().duration(50).style("opacity", 1);
686
- tooltip
687
- .html(`<strong>${d.label}:</strong><br/>${d.text}`)
688
- .style("left", event.pageX + 10 + "px")
689
- .style("top", event.pageY + 10 + "px");
690
- })
691
- .on("mouseout", function () {
692
- d3.select(this)
693
- .transition()
694
- .duration(50)
695
- .attr("text-opacity", 0.5)
696
- .attr("stroke-width", 0)
697
- .style("paint-order", "fill")
698
- .attr("fill", "black");
699
- tooltip.transition().duration(50).style("opacity", 0);
700
- });
701
- }
702
-
703
- // Initially show “no data” message
704
- generateAndRenderGraph(null, "#svgMain");
705
-
706
- const conversation = []; // stores { role, content, recs? }
707
- let currentRecs = []; // stores { recommendation, graphData }
708
- let debounceId = null;
709
-
710
- function appendUserTurn(turn) {
711
- const bubble = $("<div>")
712
- .addClass("message user")
713
- .text(turn.content);
714
- $("#chat").append(bubble);
715
-
716
- if (turn.recs && turn.recs.length > 0) {
717
- const container = $("<div>").addClass("recs-container");
718
- turn.recs.forEach((rec, index) => {
719
- let r = rec.recommendation;
720
- const item = $("<div>")
721
- .addClass("recs-item")
722
- .addClass(r.type === "add" ? "add" : "remove")
723
- .attr('title', r.sentence)
724
- .text((r.type === "add" ? "+ " : "x ") + r.value);
725
- let itemId = `rec-${conversation.length}-${index}`;
726
- item.attr('id', itemId);
727
- item.click(() => toggleGraph(rec.graphData, itemId));
728
- container.append(item);
729
- });
730
- container.css("align-self", "flex-end");
731
- $("#chat").append(container);
732
- }
733
- $("#chat").scrollTop($("#chat")[0].scrollHeight);
734
- }
735
-
736
- let popupOpen = false;
737
- function toggleGraph(data, itemId) {
738
- const existing = document.getElementById('popup');
739
- if (existing) {
740
- existing.remove();
741
- popupOpen = false;
742
- return;
743
- }
744
- const tag = document.getElementById(itemId);
745
- const rect = tag.getBoundingClientRect();
746
- const popup = document.createElement('div');
747
- popup.id = 'popup';
748
- // reuse your .tooltip styles but allow pointer events
749
- popup.className = 'tooltip';
750
- popup.style.pointerEvents = 'auto';
751
- popup.style.width = '600px';
752
- popup.style.height = '315px';
753
- popup.style.zIndex = 2;
754
- popup.style.left = `${rect.left + window.scrollX}px`;
755
- popup.style.top = `${rect.top + window.scrollY + 25}px`;
756
-
757
- // When the popup is open when the chat is being scrolled,
758
- // it leads to a lot of weird behaviour
759
- // so, for now, the popup is force closed when the chat is scrolling
760
- document.getElementById("chat").addEventListener("scroll", () => {
761
- popup.remove();
762
- popupOpen = false;
763
- return;
764
- })
765
-
766
- const container = document.createElement('svg');
767
- container.style.width = '100%';
768
- container.style.height = '100%';
769
-
770
- container.innerHTML = "<svg id='popup-graph'></svg>"
771
- popup.appendChild(container);
772
- document.body.appendChild(popup);
773
-
774
- renderGraph(data, '#popup-graph');
775
- // close on outside click
776
- const onClickOutside = (e) => {
777
- if (!popup.contains(e.target) && e.target !== tag) {
778
- popup.remove();
779
- document.removeEventListener('click', onClickOutside);
780
- popupOpen = false;
781
- }
782
- };
783
- setTimeout(() => {
784
- document.addEventListener('click', onClickOutside);
785
- }, 0);
786
- popupOpen = true;
787
- }
788
-
789
- function appendAssistantTurn(text) {
790
- const bubble = $("<div>")
791
- .addClass("message assistant")
792
- .text(text);
793
- $("#chat").append(bubble);
794
- $("#chat").scrollTop($("#chat")[0].scrollHeight);
795
- }
796
 
 
797
  $("#userInputDiv").on("input", function () {
798
- const txt = $(this).text().trim() || "";
799
-
800
- if (txt.length > 0) {
801
- $("#sendBtn").removeAttr("disabled");
802
  } else {
803
  $("#sendBtn").attr("disabled", true);
804
- $("#recommendation").empty();
805
- }
806
-
807
- clearTimeout(debounceId);
808
-
809
- let stopRecos = false;
810
-
811
- if (txt.length > 0 && /[.?!]$/.test(txt)) {
812
- debounceId = setTimeout(() => {
813
- $("#recommendation").html("Checking recommendations…");
814
- $.getJSON("/recommend?prompt=" + encodeURIComponent(txt), (data) => {
815
- if (stopRecos) return;
816
- $("#recommendation").empty();
817
- lastRecommendations = data;
818
- generateAndRenderGraph(lastRecommendations, "#svgMain");
819
-
820
- if (data.remove && data.remove.length > 0) {
821
- const rec = data.remove[0];
822
- const sentence = rec.sentence.replaceAll("'", "\\'");
823
- const valueEscaped = rec.value.replaceAll("'", "\\'");
824
- const $tag = $(`
825
- <div class="bx--tag bx--tag--red bx--tag--deletable rec-tags-inputarea">
826
- ✕ ${valueEscaped}
827
- </div>
828
- `);
829
- // Removal recommendations
830
- $tag.hover(
831
- () => {
832
- const cur = $("#userInputDiv").html();
833
- $("#userInputDiv").data("prevHtml", cur);
834
- $("#userInputDiv").html(
835
- $("#userInputDiv").html().replace(
836
- rec.sentence.trim(),
837
- " <span style='color: red; text-decoration: line-through'>" + rec.sentence.trim() + "</span>"
838
- )
839
- )
840
- },
841
- () => {
842
- const prev = $("#userInputDiv").data("prevHtml") || txt;
843
- $("#userInputDiv").html(prev);
844
- }
845
- );
846
-
847
- $tag.click(() => {
848
- const updated = $("#userInputDiv")
849
- .text()
850
- .replace(rec.sentence, "")
851
- .replace(/ {2,}/g, " ")
852
- .trim();
853
- $("#userInputDiv").text(updated);
854
- currentRecs.push({
855
- 'recommendation': { type: "remove", value: rec.value, sentence: rec.sentence },
856
- 'graphData': graphData,
857
- });
858
- $("#recommendation").empty();
859
- $("#userInputDiv").trigger("input");
860
- });
861
-
862
- $("#recommendation").append($tag);
863
- }
864
-
865
- if (data.add && data.add.length > 0) {
866
- data.add.forEach((rec) => {
867
- if (!$("#userInputDiv").text().includes(rec.prompt)) {
868
- const promptEscaped = rec.prompt.replaceAll("'", "\\'");
869
- const valueEscaped = rec.value.replaceAll("'", "\\'");
870
- const $tag = $(`
871
- <div class="bx--tag bx--tag--green rec-tags-inputarea">
872
- + ${valueEscaped}
873
- </div>
874
- `);
875
-
876
- // Addition recommendations
877
- $tag.hover(
878
- () => {
879
- const cur = $("#userInputDiv").html();
880
- $("#userInputDiv").data("prevHtml", cur);
881
- $("#userInputDiv").html($("#userInputDiv").html() + " <span style='color: green'>" + rec.prompt.trim() + "</span>")
882
- $("#userInputDiv").scrollTop($("#userInputDiv")[0].scrollHeight);
883
- },
884
- () => {
885
- const prev = $("#userInputDiv").data("prevHtml") || txt;
886
- $("#userInputDiv").html(prev);
887
- }
888
- );
889
-
890
- $tag.click(() => {
891
- const base =
892
- $("#userInputDiv").data("prevHtml") ||
893
- $("#userInputDiv").html().trim();
894
- $("#userInputDiv").html((base + " " + rec.prompt).trim());
895
- currentRecs.push({
896
- 'recommendation': { type: "add", value: rec.value, sentence: rec.prompt },
897
- 'graphData': graphData,
898
- });
899
- $("#recommendation").empty();
900
- $("#userInputDiv").trigger("input");
901
- });
902
-
903
- $("#recommendation").append($tag);
904
- }
905
- });
906
- }
907
-
908
- if (
909
- (!data.add || data.add.length === 0) &&
910
- (!data.remove || data.remove.length === 0)
911
- ) {
912
- $("#recommendation").text("No recommendations found.");
913
- }
914
- });
915
- }, 500);
916
- $("#sendBtn").on("click", function () {
917
- stopRecos = true;
918
- })
919
- } else {
920
- $("#recommendation").empty();
921
- lastRecommendations = null;
922
  }
923
  });
924
 
 
925
  $("#userInputDiv").trigger('input');
926
 
927
- $("#sendBtn").on("click", function () {
928
  const rawText = $("#userInputDiv").text() || "";
929
- const userText = rawText.trim();
930
- if (!userText) return;
931
-
932
- const thisTurn = {
933
- role: "user",
934
- content: userText,
935
- recs: currentRecs.slice(),
936
- };
937
- conversation.push(thisTurn);
938
- appendUserTurn(thisTurn);
939
-
940
  $("#userInputDiv").text("");
941
  $("#recommendation").empty();
942
  $("#sendBtn").attr("disabled", true);
943
- currentRecs = [];
944
-
945
- // "Typing..." placeholder
946
- const typingBubble = $("<div>")
947
- .addClass("message assistant")
948
- .attr("id", "typing")
949
- .text("Requesting Content…");
950
- $("#chat").append(typingBubble);
951
- $("#chat").scrollTop($("#chat")[0].scrollHeight);
952
-
953
- // Build full prompt
954
- let fullPrompt = "";
955
- conversation.forEach((turn) => {
956
- if (turn.role === "user") {
957
- fullPrompt += `User: ${turn.content}\n`;
958
- } else {
959
- fullPrompt += `Assistant: ${turn.content}\n`;
960
- }
961
- });
962
- fullPrompt += "Assistant: ";
963
-
964
- $.ajax({
965
- url: "/demo_inference?prompt=" + encodeURIComponent(fullPrompt) + "&model_id=" + encodeURIComponent(modelId),
966
- dataType: "json",
967
- success: function (data) {
968
- $("#typing").remove();
969
- const generated = data.content.trim();
970
- const modelId = data.model_id;
971
- const temp = data.temperature;
972
- const maxTokens = data.max_new_tokens;
973
-
974
- const modelHeader = $("<div style='display: flex; flex-direction: row;'>")
975
- .addClass("model-info")
976
- .html(`<img src="static/demo/imgs/granite.svg" class='icon'/> <div style='display:flex; align-items: center;margin-left: 0.5rem'>${modelId}</div>`);
977
- $("#chat").append(modelHeader);
978
-
979
- // Now type the assistant's generated text:
980
- const assistantBubble = $("<div>")
981
- .addClass("message assistant")
982
- .attr("id", "assistantBubble")
983
- .text("");
984
- $("#chat").append(assistantBubble);
985
- $("#chat").scrollTop($("#chat")[0].scrollHeight);
986
 
987
- const chars = generated.split("");
988
- let idx = 0;
989
- const chatEl = $("#chat")[0];
990
- const wasAtBottom = chatEl.scrollHeight - chatEl.scrollTop <= chatEl.clientHeight + 5;
991
- function typeNext() {
992
- if (idx < chars.length) {
993
- const cur = $("#assistantBubble").text();
994
- $("#assistantBubble").text(cur + chars[idx]);
995
- idx++;
996
- if(wasAtBottom) $("#chat").scrollTop($("#chat")[0].scrollHeight);
997
- setTimeout(typeNext, 0);
998
- } else {
999
- if(wasAtBottom) $("#chat").scrollTop($("#chat")[0].scrollHeight);
1000
- $("#assistantBubble").removeAttr("id");
1001
- conversation.push({
1002
- role: "assistant",
1003
- content: generated,
1004
- });
1005
- }
1006
- }
1007
- typeNext();
1008
- // $("#assistantBubble").text(generated)
1009
- },
1010
- error: function (xhr) {
1011
- $("#typing").remove();
1012
- const err =
1013
- xhr.responseJSON?.error?.message ||
1014
- "Unknown error from inference.";
1015
- appendAssistantTurn(`Error: ${err}`);
1016
- },
1017
- });
1018
  });
1019
-
1020
  // Pressing Enter (without Shift) also sends
1021
  $("#userInput").on("keydown", function (e) {
1022
  if (e.key === "Enter" && !e.shiftKey) {
@@ -1027,22 +132,11 @@
1027
  }
1028
  });
1029
 
1030
- // Tab switching
1031
- $("#tab-chat").on("click", function () {
1032
- $(this).addClass("bx--tabs__nav-item--selected");
1033
- $("#tab-graph").removeClass("bx--tabs__nav-item--selected");
1034
- $("#chat-content").show();
1035
- $("#graph-content").hide();
1036
- });
1037
-
1038
- $("#tab-graph").on("click", function () {
1039
- $(this).addClass("bx--tabs__nav-item--selected");
1040
- $("#tab-chat").removeClass("bx--tabs__nav-item--selected");
1041
- $("#chat-content").hide();
1042
- $("#graph-content").show();
1043
- generateAndRenderGraph(lastRecommendations, "#svgMain");
1044
- });
1045
  </script>
1046
  </body>
1047
-
1048
  </html>
 
4
  <head>
5
  <meta charset="UTF-8" />
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <title>Responsible Prompting - Multi-Turn Chat</title>
8
 
9
  <!-- IBM Plex Sans -->
10
  <link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@300;400;500;600&display=swap"
11
  rel="stylesheet" />
12
 
13
  <!-- Carbon CSS (for tabs and tags) -->
14
+ <link rel="stylesheet" href="./../styles/carbon-components.min.css"/>
15
 
16
+ <link rel="stylesheet" href="./../styles/style_multiturn.css"/>
 
17
 
18
+ <script type="text/javascript" src="./js/d3.v7.min.js"></script>
19
+ <script type="text/javascript" src="./js/jquery-3.7.1.min.js"></script>
20
+ <script type="text/javascript" src="./js/main.js"></script>
21
+ <script type="text/javascript" src="./js/marked.min.js"></script>
 
 
 
 
 
 
 
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
  </head>
24
 
25
  <body>
26
+ <div id="tooltip" style="opacity: 0;" class="tooltip"></div>
27
+
28
+ <a id='learn-more-container' href="https://github.com/IBM/responsible-prompting-api" target="_blank">
29
+ <div id='learn-more-text' >Learn More</div>
30
+ <img src="./imgs/arrow-up-right.svg" class="icon" style="color: #0f62fe; right: 0; padding: 1.5px"/>
31
+ </a>
32
  <!-- ===== Header: Title + Carbon Tabs ===== -->
33
  <div class="header-container">
34
+ <div style="display: flex; flex-direction: row; gap: 2rem;">
35
+ <div style="display: flex; flex-direction: column; gap: 1rem;">
36
+ <h4 style="display: flex; padding-left: 1rem; font-size: xx-large; font-weight: 300; flex: 1;">Responsible Prompting</h4>
37
+ <select id="modelSelect"></select>
38
+ </div>
39
+
40
+ <div style="width: 55%;" class="intro">
41
  <p>
42
+ Responsible Prompting is a tool that aims at supporting users in crafting prompts that embed responsible values and help avoid harmful prompts, in prompting-time.
43
  </p>
44
  </div>
45
  </div>
 
 
 
 
 
 
 
 
46
  </div>
 
 
 
47
 
 
48
  <!-- === Chat View === -->
49
  <div id="chat-content">
50
  <div id="chat" class="chat-container"></div>
51
 
52
  <!-- Input area -->
53
  <div class="input-area">
54
+ <div id="userInputDiv" contenteditable placeholder="Enter your prompt...">Act as a professional designer with 20 years of experience creating and testing UX interfaces and landing sites for a variety of IT applications. We are in need of more people and an increased budget to be able to keep up with clients' needs. What kind of evidence should I gather to support my demands to gain more resources?</div>
 
 
 
55
 
56
+ <div style="display: flex; justify-content: space-between; gap: 1rem; align-items: center;">
57
  <div id="recommendation"></div>
 
58
  <!-- Send button -->
59
+ <button id="sendBtn" class="btn">
60
+ <img src="./imgs/send.svg" alt="Send" class="icon"/>
61
  </button>
62
  </div>
63
  </div>
 
 
64
 
65
+ <div style="padding: 0.5rem; color: gray">AI responses may be inaccurate, please verify information independently.</div>
66
+ </div>
 
 
 
67
 
68
  <script>
69
+ var modelId = '';
70
+ document.addEventListener('DOMContentLoaded', () => {
71
+ // Populate the different models options
72
+ // id -> id of the model on HF, name -> name displayed to the user
73
+ const models = [
74
+ { id: 'mistralai/Mistral-7B-Instruct-v0.3', name: 'Mistral 7B Instruct v0.3' },
75
+ { id: 'meta-llama/Llama-4-Scout-17B-16E-Instruct', name: 'Llama 4 Scout' },
76
+ ];
77
  const modelSelect = document.getElementById('modelSelect');
78
 
79
  models.forEach(model => {
 
82
  option.textContent = model.name;
83
  modelSelect.appendChild(option);
84
  });
 
 
 
 
85
 
86
+ // Set default selection
87
+ modelId = models[0].id;
88
 
89
+ // Record when model changes
90
+ modelSelect.addEventListener('change', function() {
91
+ const selectedModel = models.find(model => model.id === this.value);
92
+ modelId = selectedModel.id;
93
+ });
 
94
  });
 
95
 
96
+ // To show the bottom of text in the prompt input box
 
97
  var objDiv = document.getElementById("userInputDiv");
98
  objDiv.scrollTop = objDiv.scrollHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
+ let generating = false;
101
  $("#userInputDiv").on("input", function () {
102
+ if($("#userInputDiv").text().length > 0) {
103
+ if(!generating) $("#sendBtn").attr("disabled", false);
104
+ generateRecommendations("#sendBtn", "#userInputDiv", "#recommendation");
 
105
  } else {
106
  $("#sendBtn").attr("disabled", true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
  }
108
  });
109
 
110
+ // // To generate recommendation when the page loads
111
  $("#userInputDiv").trigger('input');
112
 
113
+ $("#sendBtn").on("click", () => {
114
  const rawText = $("#userInputDiv").text() || "";
115
+
116
+ // Clear input box and recommendations
 
 
 
 
 
 
 
 
 
117
  $("#userInputDiv").text("");
118
  $("#recommendation").empty();
119
  $("#sendBtn").attr("disabled", true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
+ // Generate LLM response
122
+ generateResponse(rawText, "#chat", "#sendBtn");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  });
124
+
125
  // Pressing Enter (without Shift) also sends
126
  $("#userInput").on("keydown", function (e) {
127
  if (e.key === "Enter" && !e.shiftKey) {
 
132
  }
133
  });
134
 
135
+ $("#userInputDiv").on('keyup', () => {
136
+ if($("#userInputDiv").html() == "<br>") {
137
+ $("#userInputDiv").html("");
138
+ }
139
+ })
 
 
 
 
 
 
 
 
 
 
140
  </script>
141
  </body>
 
142
  </html>