victor HF staff commited on
Commit
5da61b4
1 Parent(s): 17dba7d

Prettier update (#54)

Browse files

* Update .prettierrc

* prettify everthing

prettify everything

Files changed (39) hide show
  1. .eslintrc.cjs +11 -11
  2. .prettierrc +1 -2
  3. src/app.html +2 -2
  4. src/hooks.server.ts +6 -6
  5. src/lib/actions/snapScrollToBottom.ts +6 -6
  6. src/lib/buildPrompt.ts +6 -6
  7. src/lib/components/CodeBlock.svelte +6 -6
  8. src/lib/components/CopyToClipBoardBtn.svelte +6 -6
  9. src/lib/components/ScrollToBottomBtn.svelte +7 -7
  10. src/lib/components/Tooltip.svelte +3 -3
  11. src/lib/components/chat/ChatInput.svelte +5 -5
  12. src/lib/components/chat/ChatIntroduction.svelte +10 -10
  13. src/lib/components/chat/ChatMessage.svelte +12 -12
  14. src/lib/components/chat/ChatMessages.svelte +6 -6
  15. src/lib/components/chat/ChatWindow.svelte +9 -9
  16. src/lib/components/icons/IconChevron.svelte +1 -1
  17. src/lib/components/icons/IconCopy.svelte +1 -1
  18. src/lib/components/icons/IconLoading.svelte +1 -1
  19. src/lib/components/icons/Logo.svelte +1 -1
  20. src/lib/server/database.ts +7 -7
  21. src/lib/stores/pendingMessage.ts +2 -2
  22. src/lib/types/Conversation.ts +2 -2
  23. src/lib/types/Message.ts +1 -1
  24. src/lib/types/SharedConversation.ts +1 -1
  25. src/lib/utils/sha256.ts +2 -2
  26. src/routes/+layout.server.ts +7 -7
  27. src/routes/+layout.svelte +26 -26
  28. src/routes/+page.svelte +8 -8
  29. src/routes/conversation/+server.ts +10 -10
  30. src/routes/conversation/[id]/+page.server.ts +8 -8
  31. src/routes/conversation/[id]/+page.svelte +15 -15
  32. src/routes/conversation/[id]/+server.ts +32 -32
  33. src/routes/conversation/[id]/share/+server.ts +15 -15
  34. src/routes/r/[id]/+page.server.ts +6 -6
  35. src/routes/r/[id]/+page.svelte +2 -2
  36. src/styles/highlight-js.css +1 -1
  37. src/styles/main.css +1 -1
  38. svelte.config.js +8 -8
  39. vite.config.ts +6 -6
.eslintrc.cjs CHANGED
@@ -1,23 +1,23 @@
1
  module.exports = {
2
  root: true,
3
- parser: '@typescript-eslint/parser',
4
- extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
5
- plugins: ['svelte3', '@typescript-eslint'],
6
- ignorePatterns: ['*.cjs'],
7
- overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
8
  settings: {
9
- 'svelte3/typescript': () => require('typescript')
10
  },
11
  parserOptions: {
12
- sourceType: 'module',
13
- ecmaVersion: 2020
14
  },
15
  rules: {
16
- 'no-shadow': ['error']
17
  },
18
  env: {
19
  browser: true,
20
  es2017: true,
21
- node: true
22
- }
23
  };
 
1
  module.exports = {
2
  root: true,
3
+ parser: "@typescript-eslint/parser",
4
+ extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
5
+ plugins: ["svelte3", "@typescript-eslint"],
6
+ ignorePatterns: ["*.cjs"],
7
+ overrides: [{ files: ["*.svelte"], processor: "svelte3/svelte3" }],
8
  settings: {
9
+ "svelte3/typescript": () => require("typescript"),
10
  },
11
  parserOptions: {
12
+ sourceType: "module",
13
+ ecmaVersion: 2020,
14
  },
15
  rules: {
16
+ "no-shadow": ["error"],
17
  },
18
  env: {
19
  browser: true,
20
  es2017: true,
21
+ node: true,
22
+ },
23
  };
.prettierrc CHANGED
@@ -1,7 +1,6 @@
1
  {
2
  "useTabs": true,
3
- "singleQuote": true,
4
- "trailingComma": "none",
5
  "printWidth": 100,
6
  "plugins": ["prettier-plugin-svelte"],
7
  "pluginSearchDirs": ["."],
 
1
  {
2
  "useTabs": true,
3
+ "trailingComma": "es5",
 
4
  "printWidth": 100,
5
  "plugins": ["prettier-plugin-svelte"],
6
  "pluginSearchDirs": ["."],
src/app.html CHANGED
@@ -10,8 +10,8 @@
10
  <div style="display: contents">%sveltekit.body%</div>
11
  </body>
12
  <script>
13
- if (localStorage.theme === 'dark') {
14
- document.documentElement.classList.add('dark');
15
  }
16
  </script>
17
  </html>
 
10
  <div style="display: contents">%sveltekit.body%</div>
11
  </body>
12
  <script>
13
+ if (localStorage.theme === "dark") {
14
+ document.documentElement.classList.add("dark");
15
  }
16
  </script>
17
  </html>
src/hooks.server.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { COOKIE_NAME } from '$env/static/private';
2
- import type { Handle } from '@sveltejs/kit';
3
- import { addYears } from 'date-fns';
4
 
5
  export const handle: Handle = async ({ event, resolve }) => {
6
  const token = event.cookies.get(COOKIE_NAME);
@@ -9,11 +9,11 @@ export const handle: Handle = async ({ event, resolve }) => {
9
 
10
  // Refresh cookie expiration date
11
  event.cookies.set(COOKIE_NAME, event.locals.sessionId, {
12
- path: '/',
13
- sameSite: 'lax',
14
  secure: true,
15
  httpOnly: true,
16
- expires: addYears(new Date(), 1)
17
  });
18
 
19
  const response = await resolve(event);
 
1
+ import { COOKIE_NAME } from "$env/static/private";
2
+ import type { Handle } from "@sveltejs/kit";
3
+ import { addYears } from "date-fns";
4
 
5
  export const handle: Handle = async ({ event, resolve }) => {
6
  const token = event.cookies.get(COOKIE_NAME);
 
9
 
10
  // Refresh cookie expiration date
11
  event.cookies.set(COOKIE_NAME, event.locals.sessionId, {
12
+ path: "/",
13
+ sameSite: "lax",
14
  secure: true,
15
  httpOnly: true,
16
+ expires: addYears(new Date(), 1),
17
  });
18
 
19
  const response = await resolve(event);
src/lib/actions/snapScrollToBottom.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { navigating } from '$app/stores';
2
- import { get } from 'svelte/store';
3
 
4
  /**
5
  * @param node element to snap scroll to bottom
@@ -31,18 +31,18 @@ export const snapScrollToBottom = (node: HTMLElement, dependency: any) => {
31
  if (!force && isDetached && !get(navigating)) return;
32
 
33
  node.scroll({
34
- top: node.scrollHeight
35
  });
36
  };
37
 
38
- node.addEventListener('scroll', handleScroll);
39
 
40
  updateScroll({ force: true });
41
 
42
  return {
43
  update: updateScroll,
44
  destroy: () => {
45
- node.removeEventListener('scroll', handleScroll);
46
- }
47
  };
48
  };
 
1
+ import { navigating } from "$app/stores";
2
+ import { get } from "svelte/store";
3
 
4
  /**
5
  * @param node element to snap scroll to bottom
 
31
  if (!force && isDetached && !get(navigating)) return;
32
 
33
  node.scroll({
34
+ top: node.scrollHeight,
35
  });
36
  };
37
 
38
+ node.addEventListener("scroll", handleScroll);
39
 
40
  updateScroll({ force: true });
41
 
42
  return {
43
  update: updateScroll,
44
  destroy: () => {
45
+ node.removeEventListener("scroll", handleScroll);
46
+ },
47
  };
48
  };
src/lib/buildPrompt.ts CHANGED
@@ -1,9 +1,9 @@
1
  import {
2
  PUBLIC_ASSISTANT_MESSAGE_TOKEN,
3
  PUBLIC_SEP_TOKEN,
4
- PUBLIC_USER_MESSAGE_TOKEN
5
- } from '$env/static/public';
6
- import type { Message } from './types/Message';
7
 
8
  /**
9
  * Convert [{user: "assistant", content: "hi"}, {user: "user", content: "hello"}] to:
@@ -15,11 +15,11 @@ export function buildPrompt(messages: Message[]): string {
15
  messages
16
  .map(
17
  (m) =>
18
- (m.from === 'user'
19
  ? PUBLIC_USER_MESSAGE_TOKEN + m.content
20
  : PUBLIC_ASSISTANT_MESSAGE_TOKEN + m.content) +
21
- (m.content.endsWith(PUBLIC_SEP_TOKEN) ? '' : PUBLIC_SEP_TOKEN)
22
  )
23
- .join('') + PUBLIC_ASSISTANT_MESSAGE_TOKEN
24
  );
25
  }
 
1
  import {
2
  PUBLIC_ASSISTANT_MESSAGE_TOKEN,
3
  PUBLIC_SEP_TOKEN,
4
+ PUBLIC_USER_MESSAGE_TOKEN,
5
+ } from "$env/static/public";
6
+ import type { Message } from "./types/Message";
7
 
8
  /**
9
  * Convert [{user: "assistant", content: "hi"}, {user: "user", content: "hello"}] to:
 
15
  messages
16
  .map(
17
  (m) =>
18
+ (m.from === "user"
19
  ? PUBLIC_USER_MESSAGE_TOKEN + m.content
20
  : PUBLIC_ASSISTANT_MESSAGE_TOKEN + m.content) +
21
+ (m.content.endsWith(PUBLIC_SEP_TOKEN) ? "" : PUBLIC_SEP_TOKEN)
22
  )
23
+ .join("") + PUBLIC_ASSISTANT_MESSAGE_TOKEN
24
  );
25
  }
src/lib/components/CodeBlock.svelte CHANGED
@@ -1,14 +1,14 @@
1
  <script lang="ts">
2
- import { afterUpdate } from 'svelte';
3
- import CopyToClipBoardBtn from './CopyToClipBoardBtn.svelte';
4
 
5
- export let code = '';
6
- export let lang = '';
7
 
8
- $: highlightedCode = '';
9
 
10
  afterUpdate(async () => {
11
- const { default: hljs } = await import('highlight.js');
12
  const language = hljs.getLanguage(lang);
13
 
14
  highlightedCode = hljs.highlightAuto(code, language?.aliases).value;
 
1
  <script lang="ts">
2
+ import { afterUpdate } from "svelte";
3
+ import CopyToClipBoardBtn from "./CopyToClipBoardBtn.svelte";
4
 
5
+ export let code = "";
6
+ export let lang = "";
7
 
8
+ $: highlightedCode = "";
9
 
10
  afterUpdate(async () => {
11
+ const { default: hljs } = await import("highlight.js");
12
  const language = hljs.getLanguage(lang);
13
 
14
  highlightedCode = hljs.highlightAuto(code, language?.aliases).value;
src/lib/components/CopyToClipBoardBtn.svelte CHANGED
@@ -1,10 +1,10 @@
1
  <script lang="ts">
2
- import { onDestroy } from 'svelte';
3
 
4
- import IconCopy from './icons/IconCopy.svelte';
5
- import Tooltip from './Tooltip.svelte';
6
 
7
- export let classNames = '';
8
  export let value: string;
9
 
10
  let isSuccess = false;
@@ -39,12 +39,12 @@
39
  {!isSuccess && 'text-gray-200 dark:text-gray-200'}
40
  {isSuccess && 'text-green-500'}
41
  "
42
- title={'Copy to clipboard'}
43
  type="button"
44
  on:click={handleClick}
45
  >
46
  <span class="relative">
47
  <IconCopy />
48
- <Tooltip classNames={isSuccess ? 'opacity-100' : 'opacity-0'} />
49
  </span>
50
  </button>
 
1
  <script lang="ts">
2
+ import { onDestroy } from "svelte";
3
 
4
+ import IconCopy from "./icons/IconCopy.svelte";
5
+ import Tooltip from "./Tooltip.svelte";
6
 
7
+ export let classNames = "";
8
  export let value: string;
9
 
10
  let isSuccess = false;
 
39
  {!isSuccess && 'text-gray-200 dark:text-gray-200'}
40
  {isSuccess && 'text-green-500'}
41
  "
42
+ title={"Copy to clipboard"}
43
  type="button"
44
  on:click={handleClick}
45
  >
46
  <span class="relative">
47
  <IconCopy />
48
+ <Tooltip classNames={isSuccess ? "opacity-100" : "opacity-0"} />
49
  </span>
50
  </button>
src/lib/components/ScrollToBottomBtn.svelte CHANGED
@@ -1,16 +1,16 @@
1
  <script lang="ts">
2
- import { fade } from 'svelte/transition';
3
- import IconChevron from './icons/IconChevron.svelte';
4
- import { onDestroy } from 'svelte';
5
 
6
  export let scrollNode: HTMLElement;
7
  export { className as class };
8
 
9
  let visible: boolean = false;
10
- let className = '';
11
 
12
  $: if (scrollNode) {
13
- scrollNode.addEventListener('scroll', onScroll);
14
  }
15
 
16
  function onScroll() {
@@ -20,14 +20,14 @@
20
 
21
  onDestroy(() => {
22
  if (!scrollNode) return;
23
- scrollNode.removeEventListener('scroll', onScroll);
24
  });
25
  </script>
26
 
27
  {#if visible}
28
  <button
29
  transition:fade={{ duration: 150 }}
30
- on:click={() => scrollNode.scrollTo({ top: scrollNode.scrollHeight, behavior: 'smooth' })}
31
  class="absolute flex rounded-full border w-10 h-10 items-center justify-center shadow bg-white dark:bg-gray-700 dark:border-gray-600 {className}"
32
  ><IconChevron /></button
33
  >
 
1
  <script lang="ts">
2
+ import { fade } from "svelte/transition";
3
+ import IconChevron from "./icons/IconChevron.svelte";
4
+ import { onDestroy } from "svelte";
5
 
6
  export let scrollNode: HTMLElement;
7
  export { className as class };
8
 
9
  let visible: boolean = false;
10
+ let className = "";
11
 
12
  $: if (scrollNode) {
13
+ scrollNode.addEventListener("scroll", onScroll);
14
  }
15
 
16
  function onScroll() {
 
20
 
21
  onDestroy(() => {
22
  if (!scrollNode) return;
23
+ scrollNode.removeEventListener("scroll", onScroll);
24
  });
25
  </script>
26
 
27
  {#if visible}
28
  <button
29
  transition:fade={{ duration: 150 }}
30
+ on:click={() => scrollNode.scrollTo({ top: scrollNode.scrollHeight, behavior: "smooth" })}
31
  class="absolute flex rounded-full border w-10 h-10 items-center justify-center shadow bg-white dark:bg-gray-700 dark:border-gray-600 {className}"
32
  ><IconChevron /></button
33
  >
src/lib/components/Tooltip.svelte CHANGED
@@ -1,7 +1,7 @@
1
  <script lang="ts">
2
- export let classNames = '';
3
- export let label = 'Copied';
4
- export let position = 'left-1/2 top-full transform -translate-x-1/2 translate-y-2';
5
  </script>
6
 
7
  <div
 
1
  <script lang="ts">
2
+ export let classNames = "";
3
+ export let label = "Copied";
4
+ export let position = "left-1/2 top-full transform -translate-x-1/2 translate-y-2";
5
  </script>
6
 
7
  <div
src/lib/components/chat/ChatInput.svelte CHANGED
@@ -1,8 +1,8 @@
1
  <script lang="ts">
2
- export let value = '';
3
  export let minRows = 1;
4
  export let maxRows: null | number = null;
5
- export let placeholder = '';
6
  export let disabled = false;
7
  export let autofocus = false;
8
 
@@ -11,10 +11,10 @@
11
 
12
  function handleKeydown(event: KeyboardEvent) {
13
  // submit on enter
14
- if (event.key === 'Enter' && !event.shiftKey) {
15
  event.preventDefault();
16
 
17
- textareaElement.closest('form')?.requestSubmit();
18
  }
19
  }
20
 
@@ -25,7 +25,7 @@
25
  <pre
26
  class="invisible py-3"
27
  aria-hidden="true"
28
- style="min-height: {minHeight}; max-height: {maxHeight}">{value + '&nbsp;\n'}</pre>
29
 
30
  <textarea
31
  tabindex="0"
 
1
  <script lang="ts">
2
+ export let value = "";
3
  export let minRows = 1;
4
  export let maxRows: null | number = null;
5
+ export let placeholder = "";
6
  export let disabled = false;
7
  export let autofocus = false;
8
 
 
11
 
12
  function handleKeydown(event: KeyboardEvent) {
13
  // submit on enter
14
+ if (event.key === "Enter" && !event.shiftKey) {
15
  event.preventDefault();
16
 
17
+ textareaElement.closest("form")?.requestSubmit();
18
  }
19
  }
20
 
 
25
  <pre
26
  class="invisible py-3"
27
  aria-hidden="true"
28
+ style="min-height: {minHeight}; max-height: {maxHeight}">{value + "&nbsp;\n"}</pre>
29
 
30
  <textarea
31
  tabindex="0"
src/lib/components/chat/ChatIntroduction.svelte CHANGED
@@ -1,10 +1,10 @@
1
  <script lang="ts">
2
- import { PUBLIC_DISABLE_INTRO_TILES, PUBLIC_MODEL_NAME } from '$env/static/public';
3
 
4
- import Logo from '$lib/components/icons/Logo.svelte';
5
- import CarbonArrowUpRight from '~icons/carbon/arrow-up-right';
6
- import CarbonEarth from '~icons/carbon/earth';
7
- import { createEventDispatcher } from 'svelte';
8
 
9
  const dispatch = createEventDispatcher<{ message: string }>();
10
  </script>
@@ -62,7 +62,7 @@
62
  </div>
63
  </div>
64
  </div>
65
- {#if PUBLIC_DISABLE_INTRO_TILES !== 'true'}
66
  <div class="lg:col-span-3 lg:mt-12">
67
  <p class="mb-3 text-gray-600 dark:text-gray-300">Examples</p>
68
  <div class="grid lg:grid-cols-3 gap-3 lg:gap-5">
@@ -71,8 +71,8 @@
71
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
72
  on:click={() =>
73
  dispatch(
74
- 'message',
75
- 'Write an email from bullet list: \n\n- Buy milk\n- Buy eggs\n- Buy bread'
76
  )}
77
  >
78
  "Write an email from bullet list"
@@ -81,14 +81,14 @@
81
  type="button"
82
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
83
  on:click={() =>
84
- dispatch('message', 'Code a snake game in python, the snake should be red')}
85
  >
86
  "Code a snake game"
87
  </button>
88
  <button
89
  type="button"
90
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
91
- on:click={() => dispatch('message', 'How do I make a lemon cheesecake?')}
92
  >
93
  "Assist in a task"
94
  </button>
 
1
  <script lang="ts">
2
+ import { PUBLIC_DISABLE_INTRO_TILES, PUBLIC_MODEL_NAME } from "$env/static/public";
3
 
4
+ import Logo from "$lib/components/icons/Logo.svelte";
5
+ import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
6
+ import CarbonEarth from "~icons/carbon/earth";
7
+ import { createEventDispatcher } from "svelte";
8
 
9
  const dispatch = createEventDispatcher<{ message: string }>();
10
  </script>
 
62
  </div>
63
  </div>
64
  </div>
65
+ {#if PUBLIC_DISABLE_INTRO_TILES !== "true"}
66
  <div class="lg:col-span-3 lg:mt-12">
67
  <p class="mb-3 text-gray-600 dark:text-gray-300">Examples</p>
68
  <div class="grid lg:grid-cols-3 gap-3 lg:gap-5">
 
71
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
72
  on:click={() =>
73
  dispatch(
74
+ "message",
75
+ "Write an email from bullet list: \n\n- Buy milk\n- Buy eggs\n- Buy bread"
76
  )}
77
  >
78
  "Write an email from bullet list"
 
81
  type="button"
82
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
83
  on:click={() =>
84
+ dispatch("message", "Code a snake game in python, the snake should be red")}
85
  >
86
  "Code a snake game"
87
  </button>
88
  <button
89
  type="button"
90
  class="text-gray-600 dark:text-gray-300 p-4 bg-gray-50 dark:bg-gray-800 dark:hover:bg-gray-700 hover:bg-gray-100 border dark:border-gray-800 rounded-xl"
91
+ on:click={() => dispatch("message", "How do I make a lemon cheesecake?")}
92
  >
93
  "Assist in a task"
94
  </button>
src/lib/components/chat/ChatMessage.svelte CHANGED
@@ -1,14 +1,14 @@
1
  <script lang="ts">
2
- import { marked } from 'marked';
3
- import type { Message } from '$lib/types/Message';
4
- import { afterUpdate } from 'svelte';
5
- import { deepestChild } from '$lib/utils/dom';
6
 
7
- import CodeBlock from '../CodeBlock.svelte';
8
- import IconLoading from '../icons/IconLoading.svelte';
9
 
10
  function sanitizeMd(md: string) {
11
- return md.replaceAll('<', '&lt;');
12
  }
13
 
14
  export let message: Message;
@@ -20,7 +20,7 @@
20
 
21
  const options: marked.MarkedOptions = {
22
  ...marked.getDefaults(),
23
- gfm: true
24
  };
25
 
26
  $: tokens = marked.lexer(sanitizeMd(message.content));
@@ -35,7 +35,7 @@
35
  if (contentEl) {
36
  loadingEl = new IconLoading({
37
  target: deepestChild(contentEl),
38
- props: { classNames: 'loading inline ml-2' }
39
  });
40
  }
41
  }, 600);
@@ -43,7 +43,7 @@
43
  });
44
  </script>
45
 
46
- {#if message.from === 'assistant'}
47
  <div class="flex items-start justify-start gap-4 leading-relaxed">
48
  <img
49
  alt=""
@@ -61,7 +61,7 @@
61
  bind:this={contentEl}
62
  >
63
  {#each tokens as token}
64
- {#if token.type === 'code'}
65
  <CodeBlock lang={token.lang} code={token.text} />
66
  {:else}
67
  {@html marked.parser([token], options)}
@@ -71,7 +71,7 @@
71
  </div>
72
  </div>
73
  {/if}
74
- {#if message.from === 'user'}
75
  <div class="flex items-start justify-start gap-4">
76
  <div class="mt-5 w-3 h-3 flex-none rounded-full" />
77
  <div class="rounded-2xl px-5 py-3.5 text-gray-500 dark:text-gray-400 whitespace-break-spaces">
 
1
  <script lang="ts">
2
+ import { marked } from "marked";
3
+ import type { Message } from "$lib/types/Message";
4
+ import { afterUpdate } from "svelte";
5
+ import { deepestChild } from "$lib/utils/dom";
6
 
7
+ import CodeBlock from "../CodeBlock.svelte";
8
+ import IconLoading from "../icons/IconLoading.svelte";
9
 
10
  function sanitizeMd(md: string) {
11
+ return md.replaceAll("<", "&lt;");
12
  }
13
 
14
  export let message: Message;
 
20
 
21
  const options: marked.MarkedOptions = {
22
  ...marked.getDefaults(),
23
+ gfm: true,
24
  };
25
 
26
  $: tokens = marked.lexer(sanitizeMd(message.content));
 
35
  if (contentEl) {
36
  loadingEl = new IconLoading({
37
  target: deepestChild(contentEl),
38
+ props: { classNames: "loading inline ml-2" },
39
  });
40
  }
41
  }, 600);
 
43
  });
44
  </script>
45
 
46
+ {#if message.from === "assistant"}
47
  <div class="flex items-start justify-start gap-4 leading-relaxed">
48
  <img
49
  alt=""
 
61
  bind:this={contentEl}
62
  >
63
  {#each tokens as token}
64
+ {#if token.type === "code"}
65
  <CodeBlock lang={token.lang} code={token.text} />
66
  {:else}
67
  {@html marked.parser([token], options)}
 
71
  </div>
72
  </div>
73
  {/if}
74
+ {#if message.from === "user"}
75
  <div class="flex items-start justify-start gap-4">
76
  <div class="mt-5 w-3 h-3 flex-none rounded-full" />
77
  <div class="rounded-2xl px-5 py-3.5 text-gray-500 dark:text-gray-400 whitespace-break-spaces">
src/lib/components/chat/ChatMessages.svelte CHANGED
@@ -1,9 +1,9 @@
1
  <script lang="ts">
2
- import type { Message } from '$lib/types/Message';
3
- import { snapScrollToBottom } from '$lib/actions/snapScrollToBottom';
4
- import ScrollToBottomBtn from '$lib/components/ScrollToBottomBtn.svelte';
5
- import ChatIntroduction from './ChatIntroduction.svelte';
6
- import ChatMessage from './ChatMessage.svelte';
7
 
8
  export let messages: Message[];
9
  export let loading: boolean;
@@ -20,7 +20,7 @@
20
  <ChatIntroduction on:message />
21
  {/each}
22
  {#if pending}
23
- <ChatMessage message={{ from: 'assistant', content: '' }} />
24
  {/if}
25
  <div class="h-32 flex-none" />
26
  </div>
 
1
  <script lang="ts">
2
+ import type { Message } from "$lib/types/Message";
3
+ import { snapScrollToBottom } from "$lib/actions/snapScrollToBottom";
4
+ import ScrollToBottomBtn from "$lib/components/ScrollToBottomBtn.svelte";
5
+ import ChatIntroduction from "./ChatIntroduction.svelte";
6
+ import ChatMessage from "./ChatMessage.svelte";
7
 
8
  export let messages: Message[];
9
  export let loading: boolean;
 
20
  <ChatIntroduction on:message />
21
  {/each}
22
  {#if pending}
23
+ <ChatMessage message={{ from: "assistant", content: "" }} />
24
  {/if}
25
  <div class="h-32 flex-none" />
26
  </div>
src/lib/components/chat/ChatWindow.svelte CHANGED
@@ -1,13 +1,13 @@
1
  <script lang="ts">
2
- import type { Message } from '$lib/types/Message';
3
- import { createEventDispatcher } from 'svelte';
4
 
5
- import CarbonAdd from '~icons/carbon/add';
6
- import CarbonSendAltFilled from '~icons/carbon/send-alt-filled';
7
- import CarbonTextAlignJustify from '~icons/carbon/text-align-justify';
8
 
9
- import ChatMessages from './ChatMessages.svelte';
10
- import ChatInput from './ChatInput.svelte';
11
 
12
  export let messages: Message[] = [];
13
  export let disabled: boolean = false;
@@ -32,8 +32,8 @@
32
  <form
33
  on:submit|preventDefault={() => {
34
  if (loading) return;
35
- dispatch('message', message);
36
- message = '';
37
  }}
38
  class="w-full relative flex items-center rounded-xl flex-1 max-w-4xl border bg-gray-100 focus-within:border-gray-300 dark:bg-gray-700 dark:border-gray-600 dark:focus-within:border-gray-500 "
39
  >
 
1
  <script lang="ts">
2
+ import type { Message } from "$lib/types/Message";
3
+ import { createEventDispatcher } from "svelte";
4
 
5
+ import CarbonAdd from "~icons/carbon/add";
6
+ import CarbonSendAltFilled from "~icons/carbon/send-alt-filled";
7
+ import CarbonTextAlignJustify from "~icons/carbon/text-align-justify";
8
 
9
+ import ChatMessages from "./ChatMessages.svelte";
10
+ import ChatInput from "./ChatInput.svelte";
11
 
12
  export let messages: Message[] = [];
13
  export let disabled: boolean = false;
 
32
  <form
33
  on:submit|preventDefault={() => {
34
  if (loading) return;
35
+ dispatch("message", message);
36
+ message = "";
37
  }}
38
  class="w-full relative flex items-center rounded-xl flex-1 max-w-4xl border bg-gray-100 focus-within:border-gray-300 dark:bg-gray-700 dark:border-gray-600 dark:focus-within:border-gray-500 "
39
  >
src/lib/components/icons/IconChevron.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- export let classNames: string = '';
3
  </script>
4
 
5
  <svg
 
1
  <script lang="ts">
2
+ export let classNames: string = "";
3
  </script>
4
 
5
  <svg
src/lib/components/icons/IconCopy.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- export let classNames = '';
3
  </script>
4
 
5
  <svg
 
1
  <script lang="ts">
2
+ export let classNames = "";
3
  </script>
4
 
5
  <svg
src/lib/components/icons/IconLoading.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- export let classNames: string = '';
3
  </script>
4
 
5
  <svg
 
1
  <script lang="ts">
2
+ export let classNames: string = "";
3
  </script>
4
 
5
  <svg
src/lib/components/icons/Logo.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- export let classNames: string = '';
3
  </script>
4
 
5
  <svg
 
1
  <script lang="ts">
2
+ export let classNames: string = "";
3
  </script>
4
 
5
  <svg
src/lib/server/database.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { MONGODB_URL, MONGODB_DB_NAME } from '$env/static/private';
2
- import { MongoClient } from 'mongodb';
3
- import type { Conversation } from '$lib/types/Conversation';
4
- import type { SharedConversation } from '$lib/types/SharedConversation';
5
 
6
  const client = new MongoClient(MONGODB_URL, {
7
  // directConnection: true
@@ -11,13 +11,13 @@ export const connectPromise = client.connect().catch(console.error);
11
 
12
  const db = client.db(MONGODB_DB_NAME);
13
 
14
- const conversations = db.collection<Conversation>('conversations');
15
- const sharedConversations = db.collection<SharedConversation>('sharedConversations');
16
 
17
  export { client, db };
18
  export const collections = { conversations, sharedConversations };
19
 
20
- client.on('open', () => {
21
  conversations.createIndex({ sessionId: 1, updatedAt: -1 });
22
  sharedConversations.createIndex({ hash: 1 }, { unique: true });
23
  });
 
1
+ import { MONGODB_URL, MONGODB_DB_NAME } from "$env/static/private";
2
+ import { MongoClient } from "mongodb";
3
+ import type { Conversation } from "$lib/types/Conversation";
4
+ import type { SharedConversation } from "$lib/types/SharedConversation";
5
 
6
  const client = new MongoClient(MONGODB_URL, {
7
  // directConnection: true
 
11
 
12
  const db = client.db(MONGODB_DB_NAME);
13
 
14
+ const conversations = db.collection<Conversation>("conversations");
15
+ const sharedConversations = db.collection<SharedConversation>("sharedConversations");
16
 
17
  export { client, db };
18
  export const collections = { conversations, sharedConversations };
19
 
20
+ client.on("open", () => {
21
  conversations.createIndex({ sessionId: 1, updatedAt: -1 });
22
  sharedConversations.createIndex({ hash: 1 }, { unique: true });
23
  });
src/lib/stores/pendingMessage.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { writable } from 'svelte/store';
2
 
3
- export const pendingMessage = writable<string>('');
 
1
+ import { writable } from "svelte/store";
2
 
3
+ export const pendingMessage = writable<string>("");
src/lib/types/Conversation.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { ObjectId } from 'mongodb';
2
- import type { Message } from './Message';
3
 
4
  export interface Conversation {
5
  _id: ObjectId;
 
1
+ import type { ObjectId } from "mongodb";
2
+ import type { Message } from "./Message";
3
 
4
  export interface Conversation {
5
  _id: ObjectId;
src/lib/types/Message.ts CHANGED
@@ -1,4 +1,4 @@
1
  export interface Message {
2
- from: 'user' | 'assistant';
3
  content: string;
4
  }
 
1
  export interface Message {
2
+ from: "user" | "assistant";
3
  content: string;
4
  }
src/lib/types/SharedConversation.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Message } from './Message';
2
 
3
  export interface SharedConversation {
4
  _id: string;
 
1
+ import type { Message } from "./Message";
2
 
3
  export interface SharedConversation {
4
  _id: string;
src/lib/utils/sha256.ts CHANGED
@@ -1,7 +1,7 @@
1
  export async function sha256(input: string): Promise<string> {
2
  const utf8 = new TextEncoder().encode(input);
3
- const hashBuffer = await crypto.subtle.digest('SHA-256', utf8);
4
  const hashArray = Array.from(new Uint8Array(hashBuffer));
5
- const hashHex = hashArray.map((bytes) => bytes.toString(16).padStart(2, '0')).join('');
6
  return hashHex;
7
  }
 
1
  export async function sha256(input: string): Promise<string> {
2
  const utf8 = new TextEncoder().encode(input);
3
+ const hashBuffer = await crypto.subtle.digest("SHA-256", utf8);
4
  const hashArray = Array.from(new Uint8Array(hashBuffer));
5
+ const hashHex = hashArray.map((bytes) => bytes.toString(16).padStart(2, "0")).join("");
6
  return hashHex;
7
  }
src/routes/+layout.server.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { LayoutServerLoad } from './$types';
2
- import { collections } from '$lib/server/database';
3
- import type { Conversation } from '$lib/types/Conversation';
4
 
5
  export const load: LayoutServerLoad = async (event) => {
6
  const { conversations } = collections;
@@ -8,16 +8,16 @@ export const load: LayoutServerLoad = async (event) => {
8
  return {
9
  conversations: await conversations
10
  .find({
11
- sessionId: event.locals.sessionId
12
  })
13
  .sort({ updatedAt: -1 })
14
- .project<Pick<Conversation, 'title' | '_id' | 'updatedAt' | 'createdAt'>>({
15
  title: 1,
16
  _id: 1,
17
  updatedAt: 1,
18
- createdAt: 1
19
  })
20
  .map((conv) => ({ id: conv._id.toString(), title: conv.title }))
21
- .toArray()
22
  };
23
  };
 
1
+ import type { LayoutServerLoad } from "./$types";
2
+ import { collections } from "$lib/server/database";
3
+ import type { Conversation } from "$lib/types/Conversation";
4
 
5
  export const load: LayoutServerLoad = async (event) => {
6
  const { conversations } = collections;
 
8
  return {
9
  conversations: await conversations
10
  .find({
11
+ sessionId: event.locals.sessionId,
12
  })
13
  .sort({ updatedAt: -1 })
14
+ .project<Pick<Conversation, "title" | "_id" | "updatedAt" | "createdAt">>({
15
  title: 1,
16
  _id: 1,
17
  updatedAt: 1,
18
+ createdAt: 1,
19
  })
20
  .map((conv) => ({ id: conv._id.toString(), title: conv.title }))
21
+ .toArray(),
22
  };
23
  };
src/routes/+layout.svelte CHANGED
@@ -1,37 +1,37 @@
1
  <script lang="ts">
2
- import { goto, invalidateAll } from '$app/navigation';
3
- import { page } from '$app/stores';
4
- import '../styles/main.css';
5
- import type { LayoutData } from './$types';
6
 
7
- import CarbonTrashCan from '~icons/carbon/trash-can';
8
- import CarbonExport from '~icons/carbon/export';
9
- import { base } from '$app/paths';
10
 
11
  export let data: LayoutData;
12
 
13
  function switchTheme() {
14
- const { classList } = document.querySelector('html') as HTMLElement;
15
- if (classList.contains('dark')) {
16
- classList.remove('dark');
17
- localStorage.theme = 'light';
18
  } else {
19
- classList.add('dark');
20
- localStorage.theme = 'dark';
21
  }
22
  }
23
 
24
  async function shareConversation(id: string, title: string) {
25
  try {
26
  const res = await fetch(`${base}/conversation/${id}/share`, {
27
- method: 'POST',
28
  headers: {
29
- 'Content-Type': 'application/json'
30
- }
31
  });
32
 
33
  if (!res.ok) {
34
- alert('Error while sharing conversation: ' + (await res.text()));
35
  return;
36
  }
37
 
@@ -40,29 +40,29 @@
40
  if (navigator.share) {
41
  navigator.share({
42
  title,
43
- text: 'Share this chat with others',
44
- url
45
  });
46
  } else {
47
- prompt('Share this link with your friends:', url);
48
  }
49
  } catch (err) {
50
  console.error(err);
51
- alert('Error while sharing conversation: ' + err);
52
  }
53
  }
54
 
55
  async function deleteConversation(id: string) {
56
  try {
57
  const res = await fetch(`${base}/conversation/${id}`, {
58
- method: 'DELETE',
59
  headers: {
60
- 'Content-Type': 'application/json'
61
- }
62
  });
63
 
64
  if (!res.ok) {
65
- alert('Error while deleting conversation: ' + (await res.text()));
66
  return;
67
  }
68
 
@@ -73,7 +73,7 @@
73
  }
74
  } catch (err) {
75
  console.error(err);
76
- alert('Error while deleting conversation: ' + err);
77
  }
78
  }
79
  </script>
 
1
  <script lang="ts">
2
+ import { goto, invalidateAll } from "$app/navigation";
3
+ import { page } from "$app/stores";
4
+ import "../styles/main.css";
5
+ import type { LayoutData } from "./$types";
6
 
7
+ import CarbonTrashCan from "~icons/carbon/trash-can";
8
+ import CarbonExport from "~icons/carbon/export";
9
+ import { base } from "$app/paths";
10
 
11
  export let data: LayoutData;
12
 
13
  function switchTheme() {
14
+ const { classList } = document.querySelector("html") as HTMLElement;
15
+ if (classList.contains("dark")) {
16
+ classList.remove("dark");
17
+ localStorage.theme = "light";
18
  } else {
19
+ classList.add("dark");
20
+ localStorage.theme = "dark";
21
  }
22
  }
23
 
24
  async function shareConversation(id: string, title: string) {
25
  try {
26
  const res = await fetch(`${base}/conversation/${id}/share`, {
27
+ method: "POST",
28
  headers: {
29
+ "Content-Type": "application/json",
30
+ },
31
  });
32
 
33
  if (!res.ok) {
34
+ alert("Error while sharing conversation: " + (await res.text()));
35
  return;
36
  }
37
 
 
40
  if (navigator.share) {
41
  navigator.share({
42
  title,
43
+ text: "Share this chat with others",
44
+ url,
45
  });
46
  } else {
47
+ prompt("Share this link with your friends:", url);
48
  }
49
  } catch (err) {
50
  console.error(err);
51
+ alert("Error while sharing conversation: " + err);
52
  }
53
  }
54
 
55
  async function deleteConversation(id: string) {
56
  try {
57
  const res = await fetch(`${base}/conversation/${id}`, {
58
+ method: "DELETE",
59
  headers: {
60
+ "Content-Type": "application/json",
61
+ },
62
  });
63
 
64
  if (!res.ok) {
65
+ alert("Error while deleting conversation: " + (await res.text()));
66
  return;
67
  }
68
 
 
73
  }
74
  } catch (err) {
75
  console.error(err);
76
+ alert("Error while deleting conversation: " + err);
77
  }
78
  }
79
  </script>
src/routes/+page.svelte CHANGED
@@ -1,8 +1,8 @@
1
  <script lang="ts">
2
- import { goto } from '$app/navigation';
3
- import { base } from '$app/paths';
4
- import ChatWindow from '$lib/components/chat/ChatWindow.svelte';
5
- import { pendingMessage } from '$lib/stores/pendingMessage';
6
 
7
  let loading = false;
8
 
@@ -10,14 +10,14 @@
10
  try {
11
  loading = true;
12
  const res = await fetch(`${base}/conversation`, {
13
- method: 'POST',
14
  headers: {
15
- 'Content-Type': 'application/json'
16
- }
17
  });
18
 
19
  if (!res.ok) {
20
- alert('Error while creating conversation: ' + (await res.text()));
21
  return;
22
  }
23
 
 
1
  <script lang="ts">
2
+ import { goto } from "$app/navigation";
3
+ import { base } from "$app/paths";
4
+ import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
5
+ import { pendingMessage } from "$lib/stores/pendingMessage";
6
 
7
  let loading = false;
8
 
 
10
  try {
11
  loading = true;
12
  const res = await fetch(`${base}/conversation`, {
13
+ method: "POST",
14
  headers: {
15
+ "Content-Type": "application/json",
16
+ },
17
  });
18
 
19
  if (!res.ok) {
20
+ alert("Error while creating conversation: " + (await res.text()));
21
  return;
22
  }
23
 
src/routes/conversation/+server.ts CHANGED
@@ -1,29 +1,29 @@
1
- import type { RequestHandler } from './$types';
2
- import { collections } from '$lib/server/database';
3
- import { ObjectId } from 'mongodb';
4
- import { redirect } from '@sveltejs/kit';
5
- import { base } from '$app/paths';
6
 
7
  export const POST: RequestHandler = async (input) => {
8
  const res = await collections.conversations.insertOne({
9
  _id: new ObjectId(),
10
  title:
11
- 'Untitled ' +
12
  ((await collections.conversations.countDocuments({ sessionId: input.locals.sessionId })) + 1),
13
  messages: [],
14
  createdAt: new Date(),
15
  updatedAt: new Date(),
16
- sessionId: input.locals.sessionId
17
  });
18
 
19
  return new Response(
20
  JSON.stringify({
21
- conversationId: res.insertedId.toString()
22
  }),
23
- { headers: { 'Content-Type': 'application/json' } }
24
  );
25
  };
26
 
27
  export const GET: RequestHandler = async () => {
28
- throw redirect(301, base || '/');
29
  };
 
1
+ import type { RequestHandler } from "./$types";
2
+ import { collections } from "$lib/server/database";
3
+ import { ObjectId } from "mongodb";
4
+ import { redirect } from "@sveltejs/kit";
5
+ import { base } from "$app/paths";
6
 
7
  export const POST: RequestHandler = async (input) => {
8
  const res = await collections.conversations.insertOne({
9
  _id: new ObjectId(),
10
  title:
11
+ "Untitled " +
12
  ((await collections.conversations.countDocuments({ sessionId: input.locals.sessionId })) + 1),
13
  messages: [],
14
  createdAt: new Date(),
15
  updatedAt: new Date(),
16
+ sessionId: input.locals.sessionId,
17
  });
18
 
19
  return new Response(
20
  JSON.stringify({
21
+ conversationId: res.insertedId.toString(),
22
  }),
23
+ { headers: { "Content-Type": "application/json" } }
24
  );
25
  };
26
 
27
  export const GET: RequestHandler = async () => {
28
+ throw redirect(301, base || "/");
29
  };
src/routes/conversation/[id]/+page.server.ts CHANGED
@@ -1,21 +1,21 @@
1
- import type { PageServerLoad } from './$types';
2
- import { collections } from '$lib/server/database';
3
- import type { Conversation } from '$lib/types/Conversation';
4
- import { ObjectId } from 'mongodb';
5
- import { error } from '@sveltejs/kit';
6
 
7
  export const load: PageServerLoad = async (event) => {
8
  // todo: add validation on params.id
9
  const conversation = await collections.conversations.findOne({
10
  _id: new ObjectId(event.params.id),
11
- sessionId: event.locals.sessionId
12
  });
13
 
14
  if (!conversation) {
15
- throw error(404, 'Conversation not found');
16
  }
17
 
18
  return {
19
- messages: conversation.messages
20
  };
21
  };
 
1
+ import type { PageServerLoad } from "./$types";
2
+ import { collections } from "$lib/server/database";
3
+ import type { Conversation } from "$lib/types/Conversation";
4
+ import { ObjectId } from "mongodb";
5
+ import { error } from "@sveltejs/kit";
6
 
7
  export const load: PageServerLoad = async (event) => {
8
  // todo: add validation on params.id
9
  const conversation = await collections.conversations.findOne({
10
  _id: new ObjectId(event.params.id),
11
+ sessionId: event.locals.sessionId,
12
  });
13
 
14
  if (!conversation) {
15
+ throw error(404, "Conversation not found");
16
  }
17
 
18
  return {
19
+ messages: conversation.messages,
20
  };
21
  };
src/routes/conversation/[id]/+page.svelte CHANGED
@@ -1,11 +1,11 @@
1
  <script lang="ts">
2
- import ChatWindow from '$lib/components/chat/ChatWindow.svelte';
3
- import { pendingMessage } from '$lib/stores/pendingMessage';
4
- import { onMount } from 'svelte';
5
- import type { PageData } from './$types';
6
- import { page } from '$app/stores';
7
- import { HfInference } from '@huggingface/inference';
8
- import { invalidate } from '$app/navigation';
9
 
10
  export let data: PageData;
11
 
@@ -30,12 +30,12 @@
30
  truncate: 1024,
31
  watermark: false,
32
  max_new_tokens: 1024,
33
- stop: ['<|endoftext|>'],
34
- return_full_text: false
35
- }
36
  },
37
  {
38
- use_cache: false
39
  }
40
  );
41
 
@@ -47,9 +47,9 @@
47
  if (!data.token.special) {
48
  const lastMessage = messages.at(-1);
49
 
50
- if (lastMessage?.from !== 'assistant') {
51
  // First token has a space at the beginning, trim it
52
- messages = [...messages, { from: 'assistant', content: data.token.text.trimStart() }];
53
  } else {
54
  lastMessage.content += data.token.text;
55
  messages = [...messages];
@@ -65,7 +65,7 @@
65
  loading = true;
66
  pending = true;
67
 
68
- messages = [...messages, { from: 'user', content: message }];
69
 
70
  await getTextGenerationStream(message);
71
 
@@ -79,7 +79,7 @@
79
  onMount(async () => {
80
  if ($pendingMessage) {
81
  const val = $pendingMessage;
82
- $pendingMessage = '';
83
 
84
  if (messages.length === 0) {
85
  writeMessage(val);
 
1
  <script lang="ts">
2
+ import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
3
+ import { pendingMessage } from "$lib/stores/pendingMessage";
4
+ import { onMount } from "svelte";
5
+ import type { PageData } from "./$types";
6
+ import { page } from "$app/stores";
7
+ import { HfInference } from "@huggingface/inference";
8
+ import { invalidate } from "$app/navigation";
9
 
10
  export let data: PageData;
11
 
 
30
  truncate: 1024,
31
  watermark: false,
32
  max_new_tokens: 1024,
33
+ stop: ["<|endoftext|>"],
34
+ return_full_text: false,
35
+ },
36
  },
37
  {
38
+ use_cache: false,
39
  }
40
  );
41
 
 
47
  if (!data.token.special) {
48
  const lastMessage = messages.at(-1);
49
 
50
+ if (lastMessage?.from !== "assistant") {
51
  // First token has a space at the beginning, trim it
52
+ messages = [...messages, { from: "assistant", content: data.token.text.trimStart() }];
53
  } else {
54
  lastMessage.content += data.token.text;
55
  messages = [...messages];
 
65
  loading = true;
66
  pending = true;
67
 
68
+ messages = [...messages, { from: "user", content: message }];
69
 
70
  await getTextGenerationStream(message);
71
 
 
79
  onMount(async () => {
80
  if ($pendingMessage) {
81
  const val = $pendingMessage;
82
+ $pendingMessage = "";
83
 
84
  if (messages.length === 0) {
85
  writeMessage(val);
src/routes/conversation/[id]/+server.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { HF_TOKEN } from '$env/static/private';
2
- import { PUBLIC_MODEL_ENDPOINT } from '$env/static/public';
3
- import { buildPrompt } from '$lib/buildPrompt.js';
4
- import { collections } from '$lib/server/database.js';
5
- import type { Message } from '$lib/types/Message.js';
6
- import { streamToAsyncIterable } from '$lib/utils/streamToAsyncIterable';
7
- import { sum } from '$lib/utils/sum';
8
- import { error } from '@sveltejs/kit';
9
- import { ObjectId } from 'mongodb';
10
 
11
  export async function POST({ request, fetch, locals, params }) {
12
  // todo: add validation on params.id
@@ -14,29 +14,29 @@ export async function POST({ request, fetch, locals, params }) {
14
 
15
  const conv = await collections.conversations.findOne({
16
  _id: convId,
17
- sessionId: locals.sessionId
18
  });
19
 
20
  if (!conv) {
21
- throw error(404, 'Conversation not found');
22
  }
23
 
24
  // Todo: validate prompt with zod? or aktype
25
  const json = await request.json();
26
 
27
- const messages = [...conv.messages, { from: 'user', content: json.inputs }] satisfies Message[];
28
  const prompt = buildPrompt(messages);
29
 
30
  const resp = await fetch(PUBLIC_MODEL_ENDPOINT, {
31
  headers: {
32
- 'Content-Type': request.headers.get('Content-Type') ?? 'application/json',
33
- Authorization: `Basic ${HF_TOKEN}`
34
  },
35
- method: 'POST',
36
  body: JSON.stringify({
37
  ...json,
38
- inputs: prompt
39
- })
40
  });
41
 
42
  const [stream1, stream2] = resp.body!.tee();
@@ -49,17 +49,17 @@ export async function POST({ request, fetch, locals, params }) {
49
  generated_text = generated_text.slice(prompt.length);
50
  }
51
 
52
- messages.push({ from: 'assistant', content: generated_text });
53
 
54
  await collections.conversations.updateOne(
55
  {
56
- _id: convId
57
  },
58
  {
59
  $set: {
60
  messages,
61
- updatedAt: new Date()
62
- }
63
  }
64
  );
65
  }
@@ -70,7 +70,7 @@ export async function POST({ request, fetch, locals, params }) {
70
  return new Response(stream1, {
71
  headers: Object.fromEntries(resp.headers.entries()),
72
  status: resp.status,
73
- statusText: resp.statusText
74
  });
75
  }
76
 
@@ -79,11 +79,11 @@ export async function DELETE({ locals, params }) {
79
 
80
  const conv = await collections.conversations.findOne({
81
  _id: convId,
82
- sessionId: locals.sessionId
83
  });
84
 
85
  if (!conv) {
86
- throw error(404, 'Conversation not found');
87
  }
88
 
89
  if (conv.shares?.length) {
@@ -113,24 +113,24 @@ async function parseGeneratedText(stream: ReadableStream): Promise<string> {
113
  // Get last line starting with "data:" and parse it as JSON to get the generated text
114
  const message = new TextDecoder().decode(completeInput);
115
 
116
- let lastIndex = message.lastIndexOf('\ndata:');
117
  if (lastIndex === -1) {
118
- lastIndex = message.indexOf('data');
119
  }
120
 
121
  if (lastIndex === -1) {
122
- console.error('Could not parse in last message');
123
  }
124
 
125
- let lastMessage = message.slice(lastIndex).trim().slice('data:'.length);
126
- if (lastMessage.includes('\n')) {
127
- lastMessage = lastMessage.slice(0, lastMessage.indexOf('\n'));
128
  }
129
 
130
  const res = JSON.parse(lastMessage).generated_text;
131
 
132
- if (typeof res !== 'string') {
133
- throw new Error('Could not parse generated text');
134
  }
135
 
136
  return res;
 
1
+ import { HF_TOKEN } from "$env/static/private";
2
+ import { PUBLIC_MODEL_ENDPOINT } from "$env/static/public";
3
+ import { buildPrompt } from "$lib/buildPrompt.js";
4
+ import { collections } from "$lib/server/database.js";
5
+ import type { Message } from "$lib/types/Message.js";
6
+ import { streamToAsyncIterable } from "$lib/utils/streamToAsyncIterable";
7
+ import { sum } from "$lib/utils/sum";
8
+ import { error } from "@sveltejs/kit";
9
+ import { ObjectId } from "mongodb";
10
 
11
  export async function POST({ request, fetch, locals, params }) {
12
  // todo: add validation on params.id
 
14
 
15
  const conv = await collections.conversations.findOne({
16
  _id: convId,
17
+ sessionId: locals.sessionId,
18
  });
19
 
20
  if (!conv) {
21
+ throw error(404, "Conversation not found");
22
  }
23
 
24
  // Todo: validate prompt with zod? or aktype
25
  const json = await request.json();
26
 
27
+ const messages = [...conv.messages, { from: "user", content: json.inputs }] satisfies Message[];
28
  const prompt = buildPrompt(messages);
29
 
30
  const resp = await fetch(PUBLIC_MODEL_ENDPOINT, {
31
  headers: {
32
+ "Content-Type": request.headers.get("Content-Type") ?? "application/json",
33
+ Authorization: `Basic ${HF_TOKEN}`,
34
  },
35
+ method: "POST",
36
  body: JSON.stringify({
37
  ...json,
38
+ inputs: prompt,
39
+ }),
40
  });
41
 
42
  const [stream1, stream2] = resp.body!.tee();
 
49
  generated_text = generated_text.slice(prompt.length);
50
  }
51
 
52
+ messages.push({ from: "assistant", content: generated_text });
53
 
54
  await collections.conversations.updateOne(
55
  {
56
+ _id: convId,
57
  },
58
  {
59
  $set: {
60
  messages,
61
+ updatedAt: new Date(),
62
+ },
63
  }
64
  );
65
  }
 
70
  return new Response(stream1, {
71
  headers: Object.fromEntries(resp.headers.entries()),
72
  status: resp.status,
73
+ statusText: resp.statusText,
74
  });
75
  }
76
 
 
79
 
80
  const conv = await collections.conversations.findOne({
81
  _id: convId,
82
+ sessionId: locals.sessionId,
83
  });
84
 
85
  if (!conv) {
86
+ throw error(404, "Conversation not found");
87
  }
88
 
89
  if (conv.shares?.length) {
 
113
  // Get last line starting with "data:" and parse it as JSON to get the generated text
114
  const message = new TextDecoder().decode(completeInput);
115
 
116
+ let lastIndex = message.lastIndexOf("\ndata:");
117
  if (lastIndex === -1) {
118
+ lastIndex = message.indexOf("data");
119
  }
120
 
121
  if (lastIndex === -1) {
122
+ console.error("Could not parse in last message");
123
  }
124
 
125
+ let lastMessage = message.slice(lastIndex).trim().slice("data:".length);
126
+ if (lastMessage.includes("\n")) {
127
+ lastMessage = lastMessage.slice(0, lastMessage.indexOf("\n"));
128
  }
129
 
130
  const res = JSON.parse(lastMessage).generated_text;
131
 
132
+ if (typeof res !== "string") {
133
+ throw new Error("Could not parse generated text");
134
  }
135
 
136
  return res;
src/routes/conversation/[id]/share/+server.ts CHANGED
@@ -1,20 +1,20 @@
1
- import { base } from '$app/paths';
2
- import { SHARE_BASE_URL } from '$env/static/private';
3
- import { collections } from '$lib/server/database.js';
4
- import type { SharedConversation } from '$lib/types/SharedConversation.js';
5
- import { sha256 } from '$lib/utils/sha256.js';
6
- import { error } from '@sveltejs/kit';
7
- import { ObjectId } from 'mongodb';
8
- import { nanoid } from 'nanoid';
9
 
10
  export async function POST({ params, url, locals }) {
11
  const conversation = await collections.conversations.findOne({
12
  _id: new ObjectId(params.id),
13
- sessionId: locals.sessionId
14
  });
15
 
16
  if (!conversation) {
17
- throw error(404, 'Conversation not found');
18
  }
19
 
20
  const hash = await sha256(JSON.stringify(conversation.messages));
@@ -24,9 +24,9 @@ export async function POST({ params, url, locals }) {
24
  if (existingShare) {
25
  return new Response(
26
  JSON.stringify({
27
- url: url.origin + `${base}/r/${existingShare._id}`
28
  }),
29
- { headers: { 'Content-Type': 'application/json' } }
30
  );
31
  }
32
 
@@ -36,15 +36,15 @@ export async function POST({ params, url, locals }) {
36
  messages: conversation.messages,
37
  hash,
38
  updatedAt: new Date(),
39
- title: conversation.title
40
  };
41
 
42
  await collections.sharedConversations.insertOne(shared);
43
 
44
  return new Response(
45
  JSON.stringify({
46
- url: (SHARE_BASE_URL || `${url.origin}${base}`) + `/r/${shared._id}`
47
  }),
48
- { headers: { 'Content-Type': 'application/json' } }
49
  );
50
  }
 
1
+ import { base } from "$app/paths";
2
+ import { SHARE_BASE_URL } from "$env/static/private";
3
+ import { collections } from "$lib/server/database.js";
4
+ import type { SharedConversation } from "$lib/types/SharedConversation.js";
5
+ import { sha256 } from "$lib/utils/sha256.js";
6
+ import { error } from "@sveltejs/kit";
7
+ import { ObjectId } from "mongodb";
8
+ import { nanoid } from "nanoid";
9
 
10
  export async function POST({ params, url, locals }) {
11
  const conversation = await collections.conversations.findOne({
12
  _id: new ObjectId(params.id),
13
+ sessionId: locals.sessionId,
14
  });
15
 
16
  if (!conversation) {
17
+ throw error(404, "Conversation not found");
18
  }
19
 
20
  const hash = await sha256(JSON.stringify(conversation.messages));
 
24
  if (existingShare) {
25
  return new Response(
26
  JSON.stringify({
27
+ url: url.origin + `${base}/r/${existingShare._id}`,
28
  }),
29
+ { headers: { "Content-Type": "application/json" } }
30
  );
31
  }
32
 
 
36
  messages: conversation.messages,
37
  hash,
38
  updatedAt: new Date(),
39
+ title: conversation.title,
40
  };
41
 
42
  await collections.sharedConversations.insertOne(shared);
43
 
44
  return new Response(
45
  JSON.stringify({
46
+ url: (SHARE_BASE_URL || `${url.origin}${base}`) + `/r/${shared._id}`,
47
  }),
48
+ { headers: { "Content-Type": "application/json" } }
49
  );
50
  }
src/routes/r/[id]/+page.server.ts CHANGED
@@ -1,17 +1,17 @@
1
- import type { PageServerLoad } from './$types';
2
- import { collections } from '$lib/server/database';
3
- import { error } from '@sveltejs/kit';
4
 
5
  export const load: PageServerLoad = async ({ params }) => {
6
  const conversation = await collections.sharedConversations.findOne({
7
- _id: params.id
8
  });
9
 
10
  if (!conversation) {
11
- throw error(404, 'Conversation not found');
12
  }
13
 
14
  return {
15
- messages: conversation.messages
16
  };
17
  };
 
1
+ import type { PageServerLoad } from "./$types";
2
+ import { collections } from "$lib/server/database";
3
+ import { error } from "@sveltejs/kit";
4
 
5
  export const load: PageServerLoad = async ({ params }) => {
6
  const conversation = await collections.sharedConversations.findOne({
7
+ _id: params.id,
8
  });
9
 
10
  if (!conversation) {
11
+ throw error(404, "Conversation not found");
12
  }
13
 
14
  return {
15
+ messages: conversation.messages,
16
  };
17
  };
src/routes/r/[id]/+page.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
- import ChatWindow from '$lib/components/chat/ChatWindow.svelte';
3
- import type { PageData } from './$types';
4
 
5
  export let data: PageData;
6
  </script>
 
1
  <script lang="ts">
2
+ import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
3
+ import type { PageData } from "./$types";
4
 
5
  export let data: PageData;
6
  </script>
src/styles/highlight-js.css CHANGED
@@ -1 +1 @@
1
- @import 'highlight.js/styles/atom-one-dark';
 
1
+ @import "highlight.js/styles/atom-one-dark";
src/styles/main.css CHANGED
@@ -1,4 +1,4 @@
1
- @import './highlight-js.css';
2
 
3
  @tailwind base;
4
  @tailwind components;
 
1
+ @import "./highlight-js.css";
2
 
3
  @tailwind base;
4
  @tailwind components;
svelte.config.js CHANGED
@@ -1,9 +1,9 @@
1
- import adapter from '@sveltejs/adapter-node';
2
- import { vitePreprocess } from '@sveltejs/kit/vite';
3
- import dotenv from 'dotenv';
4
 
5
- dotenv.config({ path: './.env.local' });
6
- dotenv.config({ path: './.env' });
7
 
8
  /** @type {import('@sveltejs/kit').Config} */
9
  const config = {
@@ -15,9 +15,9 @@ const config = {
15
  adapter: adapter(),
16
 
17
  paths: {
18
- base: process.env.APP_BASE || ''
19
- }
20
- }
21
  };
22
 
23
  export default config;
 
1
+ import adapter from "@sveltejs/adapter-node";
2
+ import { vitePreprocess } from "@sveltejs/kit/vite";
3
+ import dotenv from "dotenv";
4
 
5
+ dotenv.config({ path: "./.env.local" });
6
+ dotenv.config({ path: "./.env" });
7
 
8
  /** @type {import('@sveltejs/kit').Config} */
9
  const config = {
 
15
  adapter: adapter(),
16
 
17
  paths: {
18
+ base: process.env.APP_BASE || "",
19
+ },
20
+ },
21
  };
22
 
23
  export default config;
vite.config.ts CHANGED
@@ -1,12 +1,12 @@
1
- import { sveltekit } from '@sveltejs/kit/vite';
2
- import { defineConfig } from 'vite';
3
- import Icons from 'unplugin-icons/vite';
4
 
5
  export default defineConfig({
6
  plugins: [
7
  sveltekit(),
8
  Icons({
9
- compiler: 'svelte'
10
- })
11
- ]
12
  });
 
1
+ import { sveltekit } from "@sveltejs/kit/vite";
2
+ import { defineConfig } from "vite";
3
+ import Icons from "unplugin-icons/vite";
4
 
5
  export default defineConfig({
6
  plugins: [
7
  sveltekit(),
8
  Icons({
9
+ compiler: "svelte",
10
+ }),
11
+ ],
12
  });