Models.svelte 3.8 KB

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