1
1
<template >
2
2
<DataBlockBase :item_id =" item_id" :block_id =" block_id" >
3
3
<FileSelectDropdown
4
- v-show =" blockInfo.attributes.accepted_file_extensions.length > 0"
4
+ v-if =" blockInfo.attributes.accepted_file_extensions? .length > 0"
5
5
v-model =" file_id"
6
6
:item_id =" item_id"
7
7
:block_id =" block_id"
206
206
<div v-if =" haveBokehPlot" >
207
207
<div class =" row" >
208
208
<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 >
210
213
</div >
211
214
<div v-if =" detailsShown" class =" col-xl-4 col-lg-4 ml-0" >
212
215
<table class =" table table-sm" >
271
274
/>
272
275
<video v-if =" isVideo" :src =" media_url" controls class =" mx-auto" />
273
276
</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 >
274
372
</DataBlockBase >
275
373
</template >
276
374
@@ -279,6 +377,7 @@ import DataBlockBase from "@/components/datablocks/DataBlockBase";
279
377
import FileSelectDropdown from " @/components/FileSelectDropdown" ;
280
378
import BokehPlot from " @/components/BokehPlot" ;
281
379
import Isotope from " @/components/Isotope" ;
380
+ import ChatWindow from " @/components/ChatWindow" ;
282
381
283
382
import { blockTypes , API_URL } from " @/resources.js" ;
284
383
import { createComputedSetterForBlockField } from " @/field_utils.js" ;
@@ -290,6 +389,7 @@ export default {
290
389
FileSelectDropdown,
291
390
BokehPlot,
292
391
Isotope,
392
+ ChatWindow,
293
393
},
294
394
props: {
295
395
item_id: {
@@ -315,6 +415,11 @@ export default {
315
415
// NMR
316
416
detailsShown: false ,
317
417
titleShown: false ,
418
+ // Chat
419
+ isLoading: false ,
420
+ isRegenerating: false ,
421
+ advancedHidden: true ,
422
+ prompt: " " ,
318
423
};
319
424
},
320
425
computed: {
@@ -376,6 +481,35 @@ export default {
376
481
haveProcessNumberProperties () {
377
482
return this .properties && " processNumber" in this .properties ;
378
483
},
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
+ },
379
513
file_id: createComputedSetterForBlockField (" file_id" ),
380
514
wavelength: createComputedSetterForBlockField (" wavelength" ),
381
515
all_cycles: createComputedSetterForBlockField (" cyclenumber" ),
@@ -384,6 +518,19 @@ export default {
384
518
derivative_mode: createComputedSetterForBlockField (" derivative_mode" ),
385
519
characteristic_mass: createComputedSetterForBlockField (" characteristic_mass" ),
386
520
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 (" #%#%#%#%#%#%#%%#%#%#%#%#%#%#" );
387
534
},
388
535
methods: {
389
536
parseWavelength () {
@@ -393,21 +540,49 @@ export default {
393
540
this .wavelengthParseError = " " ;
394
541
}
395
542
},
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
+ }
397
549
updateBlockFromServer (
398
550
this .item_id ,
399
551
this .block_id ,
400
552
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
+ });
407
568
},
408
569
lookup_file_field (field , file_id ) {
409
570
return this .all_files .find ((file ) => file .immutable_id === file_id)? .[field];
410
571
},
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
+ },
411
586
},
412
587
};
413
588
< / script>
@@ -459,4 +634,37 @@ th {
459
634
color: #454545 ;
460
635
font- weight: 500 ;
461
636
}
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
+ }
462
670
< / style>
0 commit comments