Models.svelte 3.8 KB

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