Message.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. <script setup>
  2. import {useEchoModel} from "@laravel/echo-vue";
  3. import {useInferenceResponse} from "@/Composables/useInferenceResponse.js";
  4. import {Spinner} from "@/Packages/Shadcn/Components/ui/spinner/index.js";
  5. import AnimatedDots from "@/Components/AnimatedDots.vue";
  6. import {computed, ref} from "vue";
  7. import {parse} from "marked";
  8. import {Badge} from "@/Packages/Shadcn/Components/ui/badge/index.js";
  9. const response = useInferenceResponse()
  10. const {message} = defineProps({
  11. message: {
  12. type: Object,
  13. required: true,
  14. }
  15. })
  16. response.setStatus(message.status)
  17. useEchoModel('App.Models.Message', message.id, '.Wisper', e => response.setStatus(e.status))
  18. useEchoModel('App.Models.Message', message.id, '.Streaming', e => response.append(e.chunk, e.index))
  19. const stream = computed(() => ({
  20. think: response.think.value ? parse(response.think.value) : null,
  21. content: response.content.value ? parse(response.content.value) : null,
  22. status: response.status.value,
  23. }))
  24. const thinking = computed(() => message.thinking ? parse(message.thinking) : null)
  25. const content = computed(() => message.content ? parse(message.content) : null)
  26. const spoiler = ref(false)
  27. </script>
  28. <template>
  29. <div>
  30. <div>
  31. <div v-if="stream.status === 0">
  32. <Badge class="text-sm" variant="outline">
  33. <Spinner class="!text-lg mr-1"/>
  34. <span>Загрузка<AnimatedDots/></span>
  35. </Badge>
  36. </div>
  37. <div v-if="[1, 2].includes(stream.status) || stream.think || thinking">
  38. <Badge class="cursor-pointer text-sm" variant="outline" @click="spoiler = !spoiler">
  39. <Spinner v-if="stream.status === 1" class="mr-1"/>
  40. <span>Размышления<AnimatedDots :animating="stream.status === 2"/></span>
  41. </Badge>
  42. </div>
  43. </div>
  44. <transition
  45. enter-active-class="transition-transform duration-300 ease-out"
  46. enter-from-class="scale-y-0"
  47. enter-to-class="scale-y-100"
  48. leave-active-class="transition-transform duration-300 ease-in"
  49. leave-from-class="scale-y-100"
  50. leave-to-class="scale-y-0"
  51. >
  52. <div v-show="spoiler"
  53. class="h-[250px] origin-top transform overflow-auto bg-muted rounded-lg px-3 mt-2 border shadow-xs">
  54. <div class="max-w-full prose dark:prose-invert text-xs" v-html="thinking ?? stream.think"></div>
  55. </div>
  56. </transition>
  57. <div class="max-w-full prose dark:prose-invert text-sm mt-2" v-html="content ?? stream.content"></div>
  58. </div>
  59. </template>
  60. <style scoped>
  61. </style>