nsarrazin HF staff victor HF staff commited on
Commit
64b910e
1 Parent(s): 403d4bd

Direct model page (#858)

Browse files

* Direct model page

* Remove auto redirect

* fix thumbnail

* css warning

* change url for models

* instantSet from settings

* Get rid of redirect

* thumbnail

* also link from /models

* use public app color

---------

Co-authored-by: Victor Mustar <victor.mustar@gmail.com>

src/routes/models/+page.svelte CHANGED
@@ -40,7 +40,7 @@
40
  <dl class="mt-8 grid grid-cols-1 gap-3 sm:gap-5 xl:grid-cols-2">
41
  {#each data.models.filter((el) => !el.unlisted) as model, index (model.id)}
42
  <a
43
- href="{base}/?model={model.id}"
44
  class="relative flex flex-col gap-2 overflow-hidden rounded-xl border bg-gray-50/50 px-6 py-5 shadow hover:bg-gray-50 hover:shadow-inner dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40"
45
  >
46
  <div class="flex items-center justify-between">
 
40
  <dl class="mt-8 grid grid-cols-1 gap-3 sm:gap-5 xl:grid-cols-2">
41
  {#each data.models.filter((el) => !el.unlisted) as model, index (model.id)}
42
  <a
43
+ href="{base}/models/{model.id}"
44
  class="relative flex flex-col gap-2 overflow-hidden rounded-xl border bg-gray-50/50 px-6 py-5 shadow hover:bg-gray-50 hover:shadow-inner dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40"
45
  >
46
  <div class="flex items-center justify-between">
src/routes/models/[...model]/+page.server.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { base } from "$app/paths";
2
+ import { authCondition } from "$lib/server/auth.js";
3
+ import { collections } from "$lib/server/database.js";
4
+ import { models } from "$lib/server/models";
5
+ import { redirect } from "@sveltejs/kit";
6
+
7
+ export async function load({ params, locals, parent }) {
8
+ const model = models.find(({ id }) => id === params.model);
9
+ const data = await parent();
10
+
11
+ if (!model || model.unlisted) {
12
+ throw redirect(302, `${base}/`);
13
+ }
14
+
15
+ if (locals.user?._id ?? locals.sessionId) {
16
+ await collections.settings.updateOne(
17
+ authCondition(locals),
18
+ {
19
+ $set: {
20
+ activeModel: model.id,
21
+ updatedAt: new Date(),
22
+ },
23
+ $setOnInsert: {
24
+ createdAt: new Date(),
25
+ },
26
+ },
27
+ {
28
+ upsert: true,
29
+ }
30
+ );
31
+ }
32
+
33
+ return {
34
+ settings: {
35
+ ...data.settings,
36
+ activeModel: model.id,
37
+ },
38
+ };
39
+ }
src/routes/models/[...model]/+page.svelte ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { page } from "$app/stores";
3
+ import { base } from "$app/paths";
4
+ import { goto } from "$app/navigation";
5
+ import { onMount } from "svelte";
6
+ import { PUBLIC_APP_NAME, PUBLIC_ORIGIN } from "$env/static/public";
7
+ import ChatWindow from "$lib/components/chat/ChatWindow.svelte";
8
+ import { findCurrentModel } from "$lib/utils/models";
9
+ import { useSettingsStore } from "$lib/stores/settings";
10
+ import { ERROR_MESSAGES, error } from "$lib/stores/errors";
11
+ import { pendingMessage } from "$lib/stores/pendingMessage";
12
+
13
+ export let data;
14
+
15
+ let loading = false;
16
+ let files: File[] = [];
17
+
18
+ const settings = useSettingsStore();
19
+ const modelId = $page.params.model;
20
+
21
+ async function createConversation(message: string) {
22
+ try {
23
+ loading = true;
24
+ // check if $settings.activeModel is a valid model
25
+ // else check if it's an assistant, and use that model
26
+ // else use the first model
27
+
28
+ const validModels = data.models.map((model) => model.id);
29
+
30
+ let model;
31
+ if (validModels.includes($settings.activeModel)) {
32
+ model = $settings.activeModel;
33
+ } else {
34
+ if (validModels.includes(data.assistant?.modelId)) {
35
+ model = data.assistant?.modelId;
36
+ } else {
37
+ model = data.models[0].id;
38
+ }
39
+ }
40
+ const res = await fetch(`${base}/conversation`, {
41
+ method: "POST",
42
+ headers: {
43
+ "Content-Type": "application/json",
44
+ },
45
+ body: JSON.stringify({
46
+ model,
47
+ preprompt: $settings.customPrompts[$settings.activeModel],
48
+ }),
49
+ });
50
+
51
+ if (!res.ok) {
52
+ error.set("Error while creating conversation, try again.");
53
+ console.error("Error while creating conversation: " + (await res.text()));
54
+ return;
55
+ }
56
+
57
+ const { conversationId } = await res.json();
58
+
59
+ // Ugly hack to use a store as temp storage, feel free to improve ^^
60
+ pendingMessage.set({
61
+ content: message,
62
+ files,
63
+ });
64
+
65
+ // invalidateAll to update list of conversations
66
+ await goto(`${base}/conversation/${conversationId}`, { invalidateAll: true });
67
+ } catch (err) {
68
+ error.set(ERROR_MESSAGES.default);
69
+ console.error(err);
70
+ } finally {
71
+ loading = false;
72
+ }
73
+ }
74
+
75
+ onMount(async () => {
76
+ settings.instantSet({
77
+ activeModel: modelId,
78
+ });
79
+ });
80
+ </script>
81
+
82
+ <svelte:head>
83
+ <meta property="og:title" content={modelId + " - " + PUBLIC_APP_NAME} />
84
+ <meta property="og:type" content="link" />
85
+ <meta property="og:description" content={`Use ${modelId} inside of ${PUBLIC_APP_NAME}`} />
86
+ <meta
87
+ property="og:image"
88
+ content="{PUBLIC_ORIGIN || $page.url.origin}{base}/models/{modelId}/thumbnail.png"
89
+ />
90
+ <meta property="og:url" content={$page.url.href} />
91
+ <meta name="twitter:card" content="summary_large_image" />
92
+ </svelte:head>
93
+
94
+ <ChatWindow
95
+ on:message={(ev) => createConversation(ev.detail)}
96
+ {loading}
97
+ currentModel={findCurrentModel([...data.models, ...data.oldModels], modelId)}
98
+ models={data.models}
99
+ bind:files
100
+ />
src/routes/models/[...model]/thumbnail.png/+server.ts ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ModelThumbnail from "./ModelThumbnail.svelte";
2
+ import { redirect, type RequestHandler } from "@sveltejs/kit";
3
+ import type { SvelteComponent } from "svelte";
4
+
5
+ import { Resvg } from "@resvg/resvg-js";
6
+ import satori from "satori";
7
+ import { html } from "satori-html";
8
+
9
+ import InterRegular from "../../../../../static/fonts/Inter-Regular.ttf";
10
+ import InterBold from "../../../../../static/fonts/Inter-Bold.ttf";
11
+ import { base } from "$app/paths";
12
+ import { models } from "$lib/server/models";
13
+
14
+ export const GET: RequestHandler = (async ({ params }) => {
15
+ const model = models.find(({ id }) => id === params.model);
16
+
17
+ if (!model || model.unlisted) {
18
+ throw redirect(302, `${base}/`);
19
+ }
20
+ const renderedComponent = (ModelThumbnail as unknown as SvelteComponent).render({
21
+ name: model.name,
22
+ logoUrl: model.logoUrl,
23
+ });
24
+
25
+ const reactLike = html(
26
+ "<style>" + renderedComponent.css.code + "</style>" + renderedComponent.html
27
+ );
28
+
29
+ const svg = await satori(reactLike, {
30
+ width: 1200,
31
+ height: 648,
32
+ fonts: [
33
+ {
34
+ name: "Inter",
35
+ data: InterRegular as unknown as ArrayBuffer,
36
+ weight: 500,
37
+ },
38
+ {
39
+ name: "Inter",
40
+ data: InterBold as unknown as ArrayBuffer,
41
+ weight: 700,
42
+ },
43
+ ],
44
+ });
45
+
46
+ const png = new Resvg(svg, {
47
+ fitTo: { mode: "original" },
48
+ })
49
+ .render()
50
+ .asPng();
51
+
52
+ return new Response(png, {
53
+ headers: {
54
+ "Content-Type": "image/png",
55
+ },
56
+ });
57
+ }) satisfies RequestHandler;
src/routes/models/[...model]/thumbnail.png/ModelThumbnail.svelte ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { PUBLIC_APP_COLOR } from "$env/static/public";
3
+ import { isHuggingChat } from "$lib/utils/isHuggingChat";
4
+
5
+ export let name: string;
6
+ export let logoUrl: string | undefined;
7
+
8
+ import logo from "../../../../../static/huggingchat/logo.svg?raw";
9
+ </script>
10
+
11
+ <div class=" flex h-[648px] w-full flex-col items-center bg-white">
12
+ <div class="flex flex-1 flex-col items-center justify-center gap-2">
13
+ {#if logoUrl}
14
+ <img class="h-48 w-48" src={logoUrl} alt="avatar" />
15
+ {/if}
16
+ <h1 class="m-0 text-5xl font-bold text-black">
17
+ {name}
18
+ </h1>
19
+ </div>
20
+
21
+ <div
22
+ class="flex h-[200px] w-full flex-col items-center justify-center rounded-b-none bg-{PUBLIC_APP_COLOR}-500/10 pb-10 pt-10 text-4xl text-gray-500"
23
+ style="border-radius: 100% 100% 0 0;"
24
+ >
25
+ Try it now
26
+ {#if isHuggingChat}
27
+ on
28
+ {/if}
29
+
30
+ {#if isHuggingChat}
31
+ <div class="flex flex-row pt-3 text-5xl font-bold text-black">
32
+ <div class="mr-5 flex items-center justify-center" id="logo">
33
+ <!-- eslint-disable-next-line -->
34
+ {@html logo}
35
+ </div>
36
+ <span>HuggingChat</span>
37
+ </div>
38
+ {/if}
39
+ </div>
40
+ </div>
src/routes/settings/(nav)/[...model]/+page.svelte CHANGED
@@ -77,7 +77,7 @@
77
  </a>
78
  {/if}
79
  <CopyToClipBoardBtn
80
- value="{PUBLIC_ORIGIN || $page.url.origin}{base}?model={model.id}"
81
  classNames="!border-none !shadow-none !py-0 !px-1 !rounded-md"
82
  >
83
  <div class="flex items-center gap-1.5 hover:underline">
 
77
  </a>
78
  {/if}
79
  <CopyToClipBoardBtn
80
+ value="{PUBLIC_ORIGIN || $page.url.origin}{base}/models/{model.id}"
81
  classNames="!border-none !shadow-none !py-0 !px-1 !rounded-md"
82
  >
83
  <div class="flex items-center gap-1.5 hover:underline">