Message.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  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, e.status))
  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 class="space-y-2">
  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-else-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 v-if="stream.status === 3">
  44. <Badge class="cursor-pointer p-1" variant="outline">
  45. <Spinner/>
  46. </Badge>
  47. </div>
  48. </div>
  49. <transition
  50. enter-active-class="transition-transform duration-300 ease-out"
  51. enter-from-class="scale-y-0"
  52. enter-to-class="scale-y-100"
  53. leave-active-class="transition-transform duration-300 ease-in"
  54. leave-from-class="scale-y-100"
  55. leave-to-class="scale-y-0"
  56. >
  57. <div v-if="thinking ?? stream.think" v-show="spoiler"
  58. class="max-h-[250px] origin-top transform overflow-auto bg-muted rounded-lg px-3 py-2 mt-2 border shadow-xs">
  59. <div class="max-w-full prose dark:prose-invert text-xs" v-html="thinking ?? stream.think"></div>
  60. </div>
  61. </transition>
  62. <div v-if="content ?? stream.content" class="max-w-full prose dark:prose-invert text-sm mt-2"
  63. v-html="content ?? stream.content"></div>
  64. </div>
  65. </template>
  66. <style scoped>
  67. </style>