Skip to content

Commit 487d199

Browse files
DataBlockUI now support Whinchat block
1 parent 71c6981 commit 487d199

File tree

2 files changed

+218
-9
lines changed

2 files changed

+218
-9
lines changed

webapp/src/components/DataBlockUI.vue

Lines changed: 217 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<DataBlockBase :item_id="item_id" :block_id="block_id">
33
<FileSelectDropdown
4-
v-show="blockInfo.attributes.accepted_file_extensions.length > 0"
4+
v-if="blockInfo.attributes.accepted_file_extensions?.length > 0"
55
v-model="file_id"
66
:item_id="item_id"
77
:block_id="block_id"
@@ -206,7 +206,10 @@
206206
<div v-if="haveBokehPlot">
207207
<div class="row">
208208
<div id="bokehPlotContainer" class="col-xl-9 col-lg-10 col-md-11 mx-auto">
209-
<BokehPlot :bokeh-plot-data="bokehPlotData" />
209+
<BokehPlot v-if="bokehPlotData" :bokeh-plot-data="bokehPlotData" />
210+
<div v-else class="alert alert-secondary">
211+
Plotting currently not available for data with dimension > 1
212+
</div>
210213
</div>
211214
<div v-if="detailsShown" class="col-xl-4 col-lg-4 ml-0">
212215
<table class="table table-sm">
@@ -271,6 +274,101 @@
271274
/>
272275
<video v-if="isVideo" :src="media_url" controls class="mx-auto" />
273276
</div>
277+
<div v-if="haveChatProperties">
278+
<div v-if="advancedHidden" class="context-button" @click="advancedHidden = !advancedHidden">
279+
[show advanced]
280+
</div>
281+
<div v-if="!advancedHidden" class="context-button" @click="advancedHidden = !advancedHidden">
282+
[hide advanced]
283+
</div>
284+
285+
<div class="row">
286+
<div id="chatWindowContainer" class="col-xl-9 col-lg-10 col-md-12 mx-auto">
287+
<div v-if="!advancedHidden" class="advanced-information">
288+
<div class="input-group">
289+
<label class="mr-2">Model:</label>
290+
<select v-model="modelName" class="form-control">
291+
<option v-for="model in Object.keys(availableModels)" :key="model">
292+
{{ model }}
293+
</option>
294+
</select>
295+
</div>
296+
<br />
297+
<div class="input-group">
298+
<label>Current conversation token count:</label>
299+
<span class="pl-1">{{ tokenCount }}/ {{ modelObj.context_window }}</span>
300+
</div>
301+
<div class="form-row input-group">
302+
<label>est. cost for next message:</label>
303+
<span class="pl-1">${{ estimatedCost.toPrecision(2) }}</span>
304+
</div>
305+
306+
<div class="form-row input-group">
307+
<label for="temperatureInput" class="mr-2"><b>temperature:</b></label>
308+
<input
309+
id="temperatureInput"
310+
v-model="temperature"
311+
type="number"
312+
min="0"
313+
max="1"
314+
step="0.1"
315+
class="form-control-sm"
316+
:class="{ 'red-border': tempInvalid }"
317+
/>
318+
<small v-show="tempInvalid" class="text-danger">
319+
Temperature must must be a number between 0 and 1
320+
</small>
321+
</div>
322+
</div>
323+
<ChatWindow
324+
:chat-messages="messages.slice(advancedHidden ? 2 : 0)"
325+
:is-loading="isLoading"
326+
/>
327+
<div class="d-flex justify-content-center">
328+
<button
329+
class="btn btn-default btn-sm regenerate-button"
330+
:disabled="messages[messages.length - 1]['role'] != 'assistant'"
331+
@click="regenerateLastResponse"
332+
>
333+
<font-awesome-icon
334+
:icon="['fa', 'sync']"
335+
class="pr-1 text-muted"
336+
:spin="isRegenerating"
337+
/>
338+
regenerate response
339+
</button>
340+
</div>
341+
</div>
342+
</div>
343+
<div v-if="errorMessage" style="white-space: pre-line" class="alert alert-warning">
344+
{{ errorMessage }}
345+
</div>
346+
<div v-show="estimatedCost > 0.1" class="alert alert-info col-lg-6 col-md-8 mt-3 mx-auto">
347+
<font-awesome-icon icon="exclamation-circle" /> sending a message is estimated to cost: ${{
348+
estimatedCost.toPrecision(2)
349+
}}
350+
</div>
351+
352+
<div class="input-group form-inline col-md-10 mx-auto align-items-end">
353+
<textarea
354+
v-model="prompt"
355+
rows="3"
356+
type="text"
357+
class="form-control"
358+
:disabled="isLoading"
359+
placeholder="Type your message to send to the LLM, then press enter or hit send (shift-enter for newline)."
360+
@keydown.enter.exact.prevent="updateBlock"
361+
/>
362+
<button
363+
type="button"
364+
class="btn btn-default send-button"
365+
:disabled="!prompt || /^\s*$/.test(prompt) || isLoading"
366+
@click="updateBlock()"
367+
>
368+
Send
369+
</button>
370+
</div>
371+
</div>
274372
</DataBlockBase>
275373
</template>
276374

@@ -279,6 +377,7 @@ import DataBlockBase from "@/components/datablocks/DataBlockBase";
279377
import FileSelectDropdown from "@/components/FileSelectDropdown";
280378
import BokehPlot from "@/components/BokehPlot";
281379
import Isotope from "@/components/Isotope";
380+
import ChatWindow from "@/components/ChatWindow";
282381
283382
import { blockTypes, API_URL } from "@/resources.js";
284383
import { createComputedSetterForBlockField } from "@/field_utils.js";
@@ -290,6 +389,7 @@ export default {
290389
FileSelectDropdown,
291390
BokehPlot,
292391
Isotope,
392+
ChatWindow,
293393
},
294394
props: {
295395
item_id: {
@@ -315,6 +415,11 @@ export default {
315415
// NMR
316416
detailsShown: false,
317417
titleShown: false,
418+
// Chat
419+
isLoading: false,
420+
isRegenerating: false,
421+
advancedHidden: true,
422+
prompt: "",
318423
};
319424
},
320425
computed: {
@@ -376,6 +481,35 @@ export default {
376481
haveProcessNumberProperties() {
377482
return this.properties && "processNumber" in this.properties;
378483
},
484+
haveChatProperties() {
485+
return this.properties && "chat" in this.properties;
486+
},
487+
modelObj() {
488+
return this.availableModels[this.modelName] || {};
489+
},
490+
tempInvalid() {
491+
return (
492+
this.temperature == null ||
493+
isNaN(this.temperature) ||
494+
this.temperature < 0 ||
495+
this.temperature > 1
496+
);
497+
},
498+
estimatedCost() {
499+
// a rough estimation of cost, assuming the next input will be about 50 tokens
500+
// and the output will be about 250.
501+
return (
502+
(this.modelObj["input_cost_usd_per_MTok"] * (this.tokenCount + 50)) / 1e6 +
503+
(this.modelObj["output_cost_usd_per_MTok"] * 250) / 1e6
504+
);
505+
},
506+
errorMessage() {
507+
return this.$store.state.all_item_data[this.item_id]["blocks_obj"][this.block_id]
508+
.error_message;
509+
},
510+
tokenCount() {
511+
return this.$store.state.all_item_data[this.item_id]["blocks_obj"][this.block_id].token_count;
512+
},
379513
file_id: createComputedSetterForBlockField("file_id"),
380514
wavelength: createComputedSetterForBlockField("wavelength"),
381515
all_cycles: createComputedSetterForBlockField("cyclenumber"),
@@ -384,6 +518,19 @@ export default {
384518
derivative_mode: createComputedSetterForBlockField("derivative_mode"),
385519
characteristic_mass: createComputedSetterForBlockField("characteristic_mass"),
386520
selected_process: createComputedSetterForBlockField("selected_process"),
521+
messages: createComputedSetterForBlockField("messages"),
522+
temperature: createComputedSetterForBlockField("temperature"),
523+
modelName: createComputedSetterForBlockField("model"),
524+
availableModels: createComputedSetterForBlockField("available_models"),
525+
},
526+
created() {
527+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
528+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
529+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
530+
console.log(this.haveChatProperties);
531+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
532+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
533+
console.log("#%#%#%#%#%#%#%%#%#%#%#%#%#%#");
387534
},
388535
methods: {
389536
parseWavelength() {
@@ -393,21 +540,49 @@ export default {
393540
this.wavelengthParseError = "";
394541
}
395542
},
396-
updateBlock() {
543+
async updateBlock() {
544+
if (this.haveChatProperties) {
545+
this.isLoading = true;
546+
this.$store.state.all_item_data[this.item_id]["blocks_obj"][this.block_id].prompt =
547+
this.prompt;
548+
}
397549
updateBlockFromServer(
398550
this.item_id,
399551
this.block_id,
400552
this.$store.state.all_item_data[this.item_id]["blocks_obj"][this.block_id],
401-
).then(() => {
402-
if (this.haveCycleProperties) {
403-
this.bokehPlotLimitedWidth = this.derivative_mode != "dQ/dV";
404-
this.isReplotButtonDisplayed = false;
405-
}
406-
});
553+
)
554+
.then(() => {
555+
if (this.haveCycleProperties) {
556+
this.bokehPlotLimitedWidth = this.derivative_mode != "dQ/dV";
557+
this.isReplotButtonDisplayed = false;
558+
}
559+
if (this.haveChatProperties) {
560+
this.prompt = "";
561+
}
562+
})
563+
.finally(() => {
564+
if (this.haveChatProperties) {
565+
this.isLoading = false;
566+
}
567+
});
407568
},
408569
lookup_file_field(field, file_id) {
409570
return this.all_files.find((file) => file.immutable_id === file_id)?.[field];
410571
},
572+
async regenerateLastResponse() {
573+
this.isLoading = true;
574+
this.isRegenerating = true;
575+
const last_message = this.messages.pop();
576+
await updateBlockFromServer(
577+
this.item_id,
578+
this.block_id,
579+
this.$store.state.all_item_data[this.item_id]["blocks_obj"][this.block_id],
580+
).catch(() => {
581+
this.messages.push(last_message);
582+
});
583+
this.isLoading = false;
584+
this.isRegenerating = false;
585+
},
411586
},
412587
};
413588
</script>
@@ -459,4 +634,37 @@ th {
459634
color: #454545;
460635
font-weight: 500;
461636
}
637+
638+
/* CHAT */
639+
.context-button {
640+
cursor: pointer;
641+
float: right;
642+
}
643+
644+
.send-button {
645+
height: 2.5rem;
646+
border-radius: 2rem;
647+
position: relative;
648+
left: -70px;
649+
top: -10px;
650+
z-index: 10;
651+
}
652+
653+
.regenerate-button {
654+
margin-top: -1rem;
655+
margin-bottom: 1rem;
656+
}
657+
658+
.advanced-information {
659+
margin-left: 20%;
660+
}
661+
662+
.advanced-information label {
663+
font-weight: 600;
664+
color: #2c3e50;
665+
}
666+
667+
#model-information-messages {
668+
font-style: italic;
669+
}
462670
</style>

webapp/src/resources.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export const blockTypes = {
116116
chat: {
117117
description: "Virtual assistant",
118118
component: DataBlockUI,
119+
properties: { chat: true },
119120
name: "Virtual Assistant",
120121
},
121122
};

0 commit comments

Comments
 (0)