Models.svelte 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. <script lang="ts">
  2. import { createEventDispatcher } from 'svelte';
  3. import { generatePrompt } from '$lib/apis/ollama';
  4. import { models } from '$lib/stores';
  5. import { splitStream } from '$lib/utils';
  6. import { tick, getContext } from 'svelte';
  7. import { toast } from 'svelte-sonner';
  8. const i18n = getContext('i18n');
  9. const dispatch = createEventDispatcher();
  10. export let prompt = '';
  11. export let user = null;
  12. export let chatInputPlaceholder = '';
  13. export let messages = [];
  14. let selectedIdx = 0;
  15. let filteredModels = [];
  16. $: filteredModels = $models
  17. .filter((p) => p.name.includes(prompt.split(' ')?.at(0)?.substring(1) ?? ''))
  18. .sort((a, b) => a.name.localeCompare(b.name));
  19. $: if (prompt) {
  20. selectedIdx = 0;
  21. }
  22. export const selectUp = () => {
  23. selectedIdx = Math.max(0, selectedIdx - 1);
  24. };
  25. export const selectDown = () => {
  26. selectedIdx = Math.min(selectedIdx + 1, filteredModels.length - 1);
  27. };
  28. const confirmSelect = async (model) => {
  29. prompt = '';
  30. dispatch('select', model);
  31. };
  32. const confirmSelectCollaborativeChat = async (model) => {
  33. // dispatch('select', model);
  34. prompt = '';
  35. user = JSON.parse(JSON.stringify(model.name));
  36. await tick();
  37. chatInputPlaceholder = $i18n.t('{{modelName}} is thinking...', { modelName: model.name });
  38. const chatInputElement = document.getElementById('chat-textarea');
  39. await tick();
  40. chatInputElement?.focus();
  41. await tick();
  42. const convoText = messages.reduce((a, message, i, arr) => {
  43. return `${a}### ${message.role.toUpperCase()}\n${message.content}\n\n`;
  44. }, '');
  45. const res = await generatePrompt(localStorage.token, model.name, convoText);
  46. if (res && res.ok) {
  47. const reader = res.body
  48. .pipeThrough(new TextDecoderStream())
  49. .pipeThrough(splitStream('\n'))
  50. .getReader();
  51. while (true) {
  52. const { value, done } = await reader.read();
  53. if (done) {
  54. break;
  55. }
  56. try {
  57. let lines = value.split('\n');
  58. for (const line of lines) {
  59. if (line !== '') {
  60. console.log(line);
  61. let data = JSON.parse(line);
  62. if ('detail' in data) {
  63. throw data;
  64. }
  65. if ('id' in data) {
  66. console.log(data);
  67. } else {
  68. if (data.done == false) {
  69. if (prompt == '' && data.response == '\n') {
  70. continue;
  71. } else {
  72. prompt += data.response;
  73. console.log(data.response);
  74. chatInputElement.scrollTop = chatInputElement.scrollHeight;
  75. await tick();
  76. }
  77. }
  78. }
  79. }
  80. }
  81. } catch (error) {
  82. console.log(error);
  83. if ('detail' in error) {
  84. toast.error(error.detail);
  85. }
  86. break;
  87. }
  88. }
  89. } else {
  90. if (res !== null) {
  91. const error = await res.json();
  92. console.log(error);
  93. if ('detail' in error) {
  94. toast.error(error.detail);
  95. } else {
  96. toast.error(error.error);
  97. }
  98. } else {
  99. toast.error(
  100. $i18n.t('Uh-oh! There was an issue connecting to {{provider}}.', { provider: 'llama' })
  101. );
  102. }
  103. }
  104. chatInputPlaceholder = '';
  105. console.log(user);
  106. };
  107. </script>
  108. {#if prompt.charAt(0) === '@'}
  109. {#if filteredModels.length > 0}
  110. <div class="pl-1 pr-12 mb-3 text-left w-full absolute bottom-0 left-0 right-0">
  111. <div class="flex w-full px-2">
  112. <div class=" bg-gray-100 dark:bg-gray-700 w-10 rounded-l-xl text-center">
  113. <div class=" text-lg font-semibold mt-2">@</div>
  114. </div>
  115. <div class="max-h-60 flex flex-col w-full rounded-r-xl bg-white">
  116. <div class="m-1 overflow-y-auto p-1 rounded-r-xl space-y-0.5">
  117. {#each filteredModels as model, modelIdx}
  118. <button
  119. class=" px-3 py-1.5 rounded-xl w-full text-left {modelIdx === selectedIdx
  120. ? ' bg-gray-100 selected-command-option-button'
  121. : ''}"
  122. type="button"
  123. on:click={() => {
  124. confirmSelect(model);
  125. }}
  126. on:mousemove={() => {
  127. selectedIdx = modelIdx;
  128. }}
  129. on:focus={() => {}}
  130. >
  131. <div class=" font-medium text-black line-clamp-1">
  132. {model.name}
  133. </div>
  134. <!-- <div class=" text-xs text-gray-600 line-clamp-1">
  135. {doc.title}
  136. </div> -->
  137. </button>
  138. {/each}
  139. </div>
  140. </div>
  141. </div>
  142. </div>
  143. {/if}
  144. {/if}