[Assistants] Filter on names (#841)
Browse files* [Assistants] Filter on names
* Add `$text` index on `assistant.name` (#844)
* add maxlength
* better experience
* use `$meta: "textScore"`
* Update src/routes/assistants/+page.server.ts
Co-authored-by: Eliott C. <coyotte508@gmail.com>
* null, not undefined
* [Assistants] Filter on names (using searchTokens) (#873)
Filter with `searchTokens`
* input
* rm extra whitespace
* hide UI before migration
* rm ad-hoc migration
---------
Co-authored-by: Eliott C. <coyotte508@gmail.com>
Co-authored-by: Nathan Sarrazin <sarrazin.nathan@gmail.com>
Co-authored-by: Victor Mustar <victor.mustar@gmail.com>
- src/lib/server/database.ts +1 -0
- src/lib/types/Assistant.ts +1 -0
- src/lib/utils/debounce.ts +17 -0
- src/lib/utils/searchTokens.ts +33 -0
- src/routes/assistants/+page.server.ts +4 -0
- src/routes/assistants/+page.svelte +38 -4
- src/routes/settings/assistants/[assistantId]/edit/+page.server.ts +2 -0
- src/routes/settings/assistants/new/+page.server.ts +2 -0
    	
        src/lib/server/database.ts
    CHANGED
    
    | @@ -117,6 +117,7 @@ client.on("open", () => { | |
| 117 | 
             
            	assistants.createIndex({ userCount: 1 }).catch(console.error);
         | 
| 118 | 
             
            	assistants.createIndex({ featured: 1, userCount: -1 }).catch(console.error);
         | 
| 119 | 
             
            	assistants.createIndex({ modelId: 1, userCount: -1 }).catch(console.error);
         | 
|  | |
| 120 | 
             
            	reports.createIndex({ assistantId: 1 }).catch(console.error);
         | 
| 121 | 
             
            	reports.createIndex({ createdBy: 1, assistantId: 1 }).catch(console.error);
         | 
| 122 | 
             
            });
         | 
|  | |
| 117 | 
             
            	assistants.createIndex({ userCount: 1 }).catch(console.error);
         | 
| 118 | 
             
            	assistants.createIndex({ featured: 1, userCount: -1 }).catch(console.error);
         | 
| 119 | 
             
            	assistants.createIndex({ modelId: 1, userCount: -1 }).catch(console.error);
         | 
| 120 | 
            +
            	assistants.createIndex({ searchTokens: 1 }).catch(console.error);
         | 
| 121 | 
             
            	reports.createIndex({ assistantId: 1 }).catch(console.error);
         | 
| 122 | 
             
            	reports.createIndex({ createdBy: 1, assistantId: 1 }).catch(console.error);
         | 
| 123 | 
             
            });
         | 
    	
        src/lib/types/Assistant.ts
    CHANGED
    
    | @@ -14,4 +14,5 @@ export interface Assistant extends Timestamps { | |
| 14 | 
             
            	preprompt: string;
         | 
| 15 | 
             
            	userCount?: number;
         | 
| 16 | 
             
            	featured?: boolean;
         | 
|  | |
| 17 | 
             
            }
         | 
|  | |
| 14 | 
             
            	preprompt: string;
         | 
| 15 | 
             
            	userCount?: number;
         | 
| 16 | 
             
            	featured?: boolean;
         | 
| 17 | 
            +
            	searchTokens: string[];
         | 
| 18 | 
             
            }
         | 
    	
        src/lib/utils/debounce.ts
    ADDED
    
    | @@ -0,0 +1,17 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            /**
         | 
| 2 | 
            +
             * A debounce function that works in both browser and Nodejs.
         | 
| 3 | 
            +
             * For pure Nodejs work, prefer the `Debouncer` class.
         | 
| 4 | 
            +
             */
         | 
| 5 | 
            +
            export function debounce<T extends unknown[]>(
         | 
| 6 | 
            +
            	callback: (...rest: T) => unknown,
         | 
| 7 | 
            +
            	limit: number
         | 
| 8 | 
            +
            ): (...rest: T) => void {
         | 
| 9 | 
            +
            	let timer: ReturnType<typeof setTimeout>;
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            	return function (...rest) {
         | 
| 12 | 
            +
            		clearTimeout(timer);
         | 
| 13 | 
            +
            		timer = setTimeout(() => {
         | 
| 14 | 
            +
            			callback(...rest);
         | 
| 15 | 
            +
            		}, limit);
         | 
| 16 | 
            +
            	};
         | 
| 17 | 
            +
            }
         | 
    	
        src/lib/utils/searchTokens.ts
    ADDED
    
    | @@ -0,0 +1,33 @@ | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
|  | |
| 1 | 
            +
            const PUNCTUATION_REGEX = /\p{P}/gu;
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            function removeDiacritics(s: string, form: "NFD" | "NFKD" = "NFD"): string {
         | 
| 4 | 
            +
            	return s.normalize(form).replace(/[\u0300-\u036f]/g, "");
         | 
| 5 | 
            +
            }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            export function generateSearchTokens(value: string): string[] {
         | 
| 8 | 
            +
            	const fullTitleToken = removeDiacritics(value)
         | 
| 9 | 
            +
            		.replace(PUNCTUATION_REGEX, "")
         | 
| 10 | 
            +
            		.replaceAll(/\s+/g, "")
         | 
| 11 | 
            +
            		.toLowerCase();
         | 
| 12 | 
            +
            	return [
         | 
| 13 | 
            +
            		...new Set([
         | 
| 14 | 
            +
            			...removeDiacritics(value)
         | 
| 15 | 
            +
            				.split(/\s+/)
         | 
| 16 | 
            +
            				.map((word) => word.replace(PUNCTUATION_REGEX, "").toLowerCase())
         | 
| 17 | 
            +
            				.filter((word) => word.length),
         | 
| 18 | 
            +
            			...(fullTitleToken.length ? [fullTitleToken] : []),
         | 
| 19 | 
            +
            		]),
         | 
| 20 | 
            +
            	];
         | 
| 21 | 
            +
            }
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            function escapeForRegExp(s: string): string {
         | 
| 24 | 
            +
            	return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
         | 
| 25 | 
            +
            }
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            export function generateQueryTokens(query: string): RegExp[] {
         | 
| 28 | 
            +
            	return removeDiacritics(query)
         | 
| 29 | 
            +
            		.split(/\s+/)
         | 
| 30 | 
            +
            		.map((word) => word.replace(PUNCTUATION_REGEX, "").toLowerCase())
         | 
| 31 | 
            +
            		.filter((word) => word.length)
         | 
| 32 | 
            +
            		.map((token) => new RegExp(`^${escapeForRegExp(token)}`));
         | 
| 33 | 
            +
            }
         | 
    	
        src/routes/assistants/+page.server.ts
    CHANGED
    
    | @@ -3,6 +3,7 @@ import { ENABLE_ASSISTANTS } from "$env/static/private"; | |
| 3 | 
             
            import { collections } from "$lib/server/database.js";
         | 
| 4 | 
             
            import type { Assistant } from "$lib/types/Assistant";
         | 
| 5 | 
             
            import type { User } from "$lib/types/User";
         | 
|  | |
| 6 | 
             
            import { error, redirect } from "@sveltejs/kit";
         | 
| 7 | 
             
            import type { Filter } from "mongodb";
         | 
| 8 |  | 
| @@ -16,6 +17,7 @@ export const load = async ({ url, locals }) => { | |
| 16 | 
             
            	const modelId = url.searchParams.get("modelId");
         | 
| 17 | 
             
            	const pageIndex = parseInt(url.searchParams.get("p") ?? "0");
         | 
| 18 | 
             
            	const username = url.searchParams.get("user");
         | 
|  | |
| 19 | 
             
            	const createdByCurrentUser = locals.user?.username && locals.user.username === username;
         | 
| 20 |  | 
| 21 | 
             
            	let user: Pick<User, "_id"> | null = null;
         | 
| @@ -34,6 +36,7 @@ export const load = async ({ url, locals }) => { | |
| 34 | 
             
            		...(modelId && { modelId }),
         | 
| 35 | 
             
            		...(!createdByCurrentUser && { userCount: { $gt: 1 } }),
         | 
| 36 | 
             
            		...(user ? { createdById: user._id } : { featured: true }),
         | 
|  | |
| 37 | 
             
            	};
         | 
| 38 | 
             
            	const assistants = await collections.assistants
         | 
| 39 | 
             
            		.find(filter)
         | 
| @@ -49,5 +52,6 @@ export const load = async ({ url, locals }) => { | |
| 49 | 
             
            		selectedModel: modelId ?? "",
         | 
| 50 | 
             
            		numTotalItems,
         | 
| 51 | 
             
            		numItemsPerPage: NUM_PER_PAGE,
         | 
|  | |
| 52 | 
             
            	};
         | 
| 53 | 
             
            };
         | 
|  | |
| 3 | 
             
            import { collections } from "$lib/server/database.js";
         | 
| 4 | 
             
            import type { Assistant } from "$lib/types/Assistant";
         | 
| 5 | 
             
            import type { User } from "$lib/types/User";
         | 
| 6 | 
            +
            import { generateQueryTokens } from "$lib/utils/searchTokens.js";
         | 
| 7 | 
             
            import { error, redirect } from "@sveltejs/kit";
         | 
| 8 | 
             
            import type { Filter } from "mongodb";
         | 
| 9 |  | 
|  | |
| 17 | 
             
            	const modelId = url.searchParams.get("modelId");
         | 
| 18 | 
             
            	const pageIndex = parseInt(url.searchParams.get("p") ?? "0");
         | 
| 19 | 
             
            	const username = url.searchParams.get("user");
         | 
| 20 | 
            +
            	const query = url.searchParams.get("q")?.trim() ?? null;
         | 
| 21 | 
             
            	const createdByCurrentUser = locals.user?.username && locals.user.username === username;
         | 
| 22 |  | 
| 23 | 
             
            	let user: Pick<User, "_id"> | null = null;
         | 
|  | |
| 36 | 
             
            		...(modelId && { modelId }),
         | 
| 37 | 
             
            		...(!createdByCurrentUser && { userCount: { $gt: 1 } }),
         | 
| 38 | 
             
            		...(user ? { createdById: user._id } : { featured: true }),
         | 
| 39 | 
            +
            		...(query && { searchTokens: { $all: generateQueryTokens(query) } }),
         | 
| 40 | 
             
            	};
         | 
| 41 | 
             
            	const assistants = await collections.assistants
         | 
| 42 | 
             
            		.find(filter)
         | 
|  | |
| 52 | 
             
            		selectedModel: modelId ?? "",
         | 
| 53 | 
             
            		numTotalItems,
         | 
| 54 | 
             
            		numItemsPerPage: NUM_PER_PAGE,
         | 
| 55 | 
            +
            		query,
         | 
| 56 | 
             
            	};
         | 
| 57 | 
             
            };
         | 
    	
        src/routes/assistants/+page.svelte
    CHANGED
    
    | @@ -4,6 +4,7 @@ | |
| 4 | 
             
            	import { PUBLIC_APP_ASSETS, PUBLIC_ORIGIN } from "$env/static/public";
         | 
| 5 | 
             
            	import { isHuggingChat } from "$lib/utils/isHuggingChat";
         | 
| 6 |  | 
|  | |
| 7 | 
             
            	import { goto } from "$app/navigation";
         | 
| 8 | 
             
            	import { base } from "$app/paths";
         | 
| 9 | 
             
            	import { page } from "$app/stores";
         | 
| @@ -14,9 +15,11 @@ | |
| 14 | 
             
            	import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
         | 
| 15 | 
             
            	import CarbonEarthAmerica from "~icons/carbon/earth-americas-filled";
         | 
| 16 | 
             
            	import CarbonUserMultiple from "~icons/carbon/user-multiple";
         | 
|  | |
| 17 | 
             
            	import Pagination from "$lib/components/Pagination.svelte";
         | 
| 18 | 
             
            	import { formatUserCount } from "$lib/utils/formatUserCount";
         | 
| 19 | 
             
            	import { getHref } from "$lib/utils/getHref";
         | 
|  | |
| 20 | 
             
            	import { useSettingsStore } from "$lib/stores/settings";
         | 
| 21 |  | 
| 22 | 
             
            	export let data: PageData;
         | 
| @@ -24,6 +27,10 @@ | |
| 24 | 
             
            	$: assistantsCreator = $page.url.searchParams.get("user");
         | 
| 25 | 
             
            	$: createdByMe = data.user?.username && data.user.username === assistantsCreator;
         | 
| 26 |  | 
|  | |
|  | |
|  | |
|  | |
| 27 | 
             
            	const onModelChange = (e: Event) => {
         | 
| 28 | 
             
            		const newUrl = getHref($page.url, {
         | 
| 29 | 
             
            			newKeys: { modelId: (e.target as HTMLSelectElement).value },
         | 
| @@ -32,6 +39,18 @@ | |
| 32 | 
             
            		goto(newUrl);
         | 
| 33 | 
             
            	};
         | 
| 34 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 35 | 
             
            	const settings = useSettingsStore();
         | 
| 36 | 
             
            </script>
         | 
| 37 |  | 
| @@ -99,7 +118,7 @@ | |
| 99 | 
             
            					{assistantsCreator}'s Assistants
         | 
| 100 | 
             
            					<a
         | 
| 101 | 
             
            						href={getHref($page.url, {
         | 
| 102 | 
            -
            							existingKeys: { behaviour: "delete", keys: ["user", "modelId", "p"] },
         | 
| 103 | 
             
            						})}
         | 
| 104 | 
             
            						class="group"
         | 
| 105 | 
             
            						><CarbonClose
         | 
| @@ -119,7 +138,7 @@ | |
| 119 | 
             
            			{:else}
         | 
| 120 | 
             
            				<a
         | 
| 121 | 
             
            					href={getHref($page.url, {
         | 
| 122 | 
            -
            						existingKeys: { behaviour: "delete", keys: ["user", "modelId", "p"] },
         | 
| 123 | 
             
            					})}
         | 
| 124 | 
             
            					class="flex items-center gap-1.5 rounded-full border px-3 py-1 {!assistantsCreator
         | 
| 125 | 
             
            						? 'border-gray-300 bg-gray-50  dark:border-gray-600 dark:bg-gray-700 dark:text-white'
         | 
| @@ -132,9 +151,9 @@ | |
| 132 | 
             
            					<a
         | 
| 133 | 
             
            						href={getHref($page.url, {
         | 
| 134 | 
             
            							newKeys: { user: data.user.username },
         | 
| 135 | 
            -
            							existingKeys: { behaviour: "delete", keys: ["modelId", "p"] },
         | 
| 136 | 
             
            						})}
         | 
| 137 | 
            -
            						class="flex items-center gap-1.5 rounded-full border px-3 py-1 {assistantsCreator &&
         | 
| 138 | 
             
            						createdByMe
         | 
| 139 | 
             
            							? 'border-gray-300 bg-gray-50  dark:border-gray-600 dark:bg-gray-700 dark:text-white'
         | 
| 140 | 
             
            							: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-300'}"
         | 
| @@ -142,6 +161,21 @@ | |
| 142 | 
             
            					</a>
         | 
| 143 | 
             
            				{/if}
         | 
| 144 | 
             
            			{/if}
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 145 | 
             
            		</div>
         | 
| 146 |  | 
| 147 | 
             
            		<div class="mt-8 grid grid-cols-2 gap-3 sm:gap-5 md:grid-cols-3 lg:grid-cols-4">
         | 
|  | |
| 4 | 
             
            	import { PUBLIC_APP_ASSETS, PUBLIC_ORIGIN } from "$env/static/public";
         | 
| 5 | 
             
            	import { isHuggingChat } from "$lib/utils/isHuggingChat";
         | 
| 6 |  | 
| 7 | 
            +
            	import { tick } from "svelte";
         | 
| 8 | 
             
            	import { goto } from "$app/navigation";
         | 
| 9 | 
             
            	import { base } from "$app/paths";
         | 
| 10 | 
             
            	import { page } from "$app/stores";
         | 
|  | |
| 15 | 
             
            	import CarbonArrowUpRight from "~icons/carbon/arrow-up-right";
         | 
| 16 | 
             
            	import CarbonEarthAmerica from "~icons/carbon/earth-americas-filled";
         | 
| 17 | 
             
            	import CarbonUserMultiple from "~icons/carbon/user-multiple";
         | 
| 18 | 
            +
            	import CarbonSearch from "~icons/carbon/search";
         | 
| 19 | 
             
            	import Pagination from "$lib/components/Pagination.svelte";
         | 
| 20 | 
             
            	import { formatUserCount } from "$lib/utils/formatUserCount";
         | 
| 21 | 
             
            	import { getHref } from "$lib/utils/getHref";
         | 
| 22 | 
            +
            	import { debounce } from "$lib/utils/debounce";
         | 
| 23 | 
             
            	import { useSettingsStore } from "$lib/stores/settings";
         | 
| 24 |  | 
| 25 | 
             
            	export let data: PageData;
         | 
|  | |
| 27 | 
             
            	$: assistantsCreator = $page.url.searchParams.get("user");
         | 
| 28 | 
             
            	$: createdByMe = data.user?.username && data.user.username === assistantsCreator;
         | 
| 29 |  | 
| 30 | 
            +
            	const SEARCH_DEBOUNCE_DELAY = 400;
         | 
| 31 | 
            +
            	let filterInputEl: HTMLInputElement;
         | 
| 32 | 
            +
            	let searchDisabled = false;
         | 
| 33 | 
            +
             | 
| 34 | 
             
            	const onModelChange = (e: Event) => {
         | 
| 35 | 
             
            		const newUrl = getHref($page.url, {
         | 
| 36 | 
             
            			newKeys: { modelId: (e.target as HTMLSelectElement).value },
         | 
|  | |
| 39 | 
             
            		goto(newUrl);
         | 
| 40 | 
             
            	};
         | 
| 41 |  | 
| 42 | 
            +
            	const filterOnName = debounce(async (e: Event) => {
         | 
| 43 | 
            +
            		searchDisabled = true;
         | 
| 44 | 
            +
            		const value = (e.target as HTMLInputElement).value;
         | 
| 45 | 
            +
            		const newUrl = getHref($page.url, { newKeys: { q: value } });
         | 
| 46 | 
            +
            		await goto(newUrl);
         | 
| 47 | 
            +
            		setTimeout(async () => {
         | 
| 48 | 
            +
            			searchDisabled = false;
         | 
| 49 | 
            +
            			await tick();
         | 
| 50 | 
            +
            			filterInputEl.focus();
         | 
| 51 | 
            +
            		}, 0);
         | 
| 52 | 
            +
            	}, SEARCH_DEBOUNCE_DELAY);
         | 
| 53 | 
            +
             | 
| 54 | 
             
            	const settings = useSettingsStore();
         | 
| 55 | 
             
            </script>
         | 
| 56 |  | 
|  | |
| 118 | 
             
            					{assistantsCreator}'s Assistants
         | 
| 119 | 
             
            					<a
         | 
| 120 | 
             
            						href={getHref($page.url, {
         | 
| 121 | 
            +
            							existingKeys: { behaviour: "delete", keys: ["user", "modelId", "p", "q"] },
         | 
| 122 | 
             
            						})}
         | 
| 123 | 
             
            						class="group"
         | 
| 124 | 
             
            						><CarbonClose
         | 
|  | |
| 138 | 
             
            			{:else}
         | 
| 139 | 
             
            				<a
         | 
| 140 | 
             
            					href={getHref($page.url, {
         | 
| 141 | 
            +
            						existingKeys: { behaviour: "delete", keys: ["user", "modelId", "p", "q"] },
         | 
| 142 | 
             
            					})}
         | 
| 143 | 
             
            					class="flex items-center gap-1.5 rounded-full border px-3 py-1 {!assistantsCreator
         | 
| 144 | 
             
            						? 'border-gray-300 bg-gray-50  dark:border-gray-600 dark:bg-gray-700 dark:text-white'
         | 
|  | |
| 151 | 
             
            					<a
         | 
| 152 | 
             
            						href={getHref($page.url, {
         | 
| 153 | 
             
            							newKeys: { user: data.user.username },
         | 
| 154 | 
            +
            							existingKeys: { behaviour: "delete", keys: ["modelId", "p", "q"] },
         | 
| 155 | 
             
            						})}
         | 
| 156 | 
            +
            						class="flex items-center gap-1.5 truncate rounded-full border px-3 py-1 {assistantsCreator &&
         | 
| 157 | 
             
            						createdByMe
         | 
| 158 | 
             
            							? 'border-gray-300 bg-gray-50  dark:border-gray-600 dark:bg-gray-700 dark:text-white'
         | 
| 159 | 
             
            							: 'border-transparent text-gray-400 hover:text-gray-800 dark:hover:text-gray-300'}"
         | 
|  | |
| 161 | 
             
            					</a>
         | 
| 162 | 
             
            				{/if}
         | 
| 163 | 
             
            			{/if}
         | 
| 164 | 
            +
            			<div
         | 
| 165 | 
            +
            				class="relative ml-auto flex hidden h-[30px] w-40 items-center rounded-full border px-2 has-[:focus]:border-gray-400 sm:w-64 dark:border-gray-600"
         | 
| 166 | 
            +
            			>
         | 
| 167 | 
            +
            				<CarbonSearch class="pointer-events-none absolute left-2 text-xs text-gray-400" />
         | 
| 168 | 
            +
            				<input
         | 
| 169 | 
            +
            					class="h-[30px] w-full bg-transparent pl-5 focus:outline-none"
         | 
| 170 | 
            +
            					placeholder="Filter by name"
         | 
| 171 | 
            +
            					value={data.query}
         | 
| 172 | 
            +
            					on:input={filterOnName}
         | 
| 173 | 
            +
            					bind:this={filterInputEl}
         | 
| 174 | 
            +
            					maxlength="150"
         | 
| 175 | 
            +
            					type="search"
         | 
| 176 | 
            +
            					disabled={searchDisabled}
         | 
| 177 | 
            +
            				/>
         | 
| 178 | 
            +
            			</div>
         | 
| 179 | 
             
            		</div>
         | 
| 180 |  | 
| 181 | 
             
            		<div class="mt-8 grid grid-cols-2 gap-3 sm:gap-5 md:grid-cols-3 lg:grid-cols-4">
         | 
    	
        src/routes/settings/assistants/[assistantId]/edit/+page.server.ts
    CHANGED
    
    | @@ -8,6 +8,7 @@ import { z } from "zod"; | |
| 8 | 
             
            import { sha256 } from "$lib/utils/sha256";
         | 
| 9 |  | 
| 10 | 
             
            import sharp from "sharp";
         | 
|  | |
| 11 |  | 
| 12 | 
             
            const newAsssistantSchema = z.object({
         | 
| 13 | 
             
            	name: z.string().min(1),
         | 
| @@ -130,6 +131,7 @@ export const actions: Actions = { | |
| 130 | 
             
            					exampleInputs,
         | 
| 131 | 
             
            					avatar: deleteAvatar ? undefined : hash ?? assistant.avatar,
         | 
| 132 | 
             
            					updatedAt: new Date(),
         | 
|  | |
| 133 | 
             
            				},
         | 
| 134 | 
             
            			}
         | 
| 135 | 
             
            		);
         | 
|  | |
| 8 | 
             
            import { sha256 } from "$lib/utils/sha256";
         | 
| 9 |  | 
| 10 | 
             
            import sharp from "sharp";
         | 
| 11 | 
            +
            import { generateSearchTokens } from "$lib/utils/searchTokens";
         | 
| 12 |  | 
| 13 | 
             
            const newAsssistantSchema = z.object({
         | 
| 14 | 
             
            	name: z.string().min(1),
         | 
|  | |
| 131 | 
             
            					exampleInputs,
         | 
| 132 | 
             
            					avatar: deleteAvatar ? undefined : hash ?? assistant.avatar,
         | 
| 133 | 
             
            					updatedAt: new Date(),
         | 
| 134 | 
            +
            					searchTokens: generateSearchTokens(parse.data.name),
         | 
| 135 | 
             
            				},
         | 
| 136 | 
             
            			}
         | 
| 137 | 
             
            		);
         | 
    	
        src/routes/settings/assistants/new/+page.server.ts
    CHANGED
    
    | @@ -7,6 +7,7 @@ import { ObjectId } from "mongodb"; | |
| 7 | 
             
            import { z } from "zod";
         | 
| 8 | 
             
            import { sha256 } from "$lib/utils/sha256";
         | 
| 9 | 
             
            import sharp from "sharp";
         | 
|  | |
| 10 |  | 
| 11 | 
             
            const newAsssistantSchema = z.object({
         | 
| 12 | 
             
            	name: z.string().min(1),
         | 
| @@ -99,6 +100,7 @@ export const actions: Actions = { | |
| 99 | 
             
            			updatedAt: new Date(),
         | 
| 100 | 
             
            			userCount: 1,
         | 
| 101 | 
             
            			featured: false,
         | 
|  | |
| 102 | 
             
            		});
         | 
| 103 |  | 
| 104 | 
             
            		// add insertedId to user settings
         | 
|  | |
| 7 | 
             
            import { z } from "zod";
         | 
| 8 | 
             
            import { sha256 } from "$lib/utils/sha256";
         | 
| 9 | 
             
            import sharp from "sharp";
         | 
| 10 | 
            +
            import { generateSearchTokens } from "$lib/utils/searchTokens";
         | 
| 11 |  | 
| 12 | 
             
            const newAsssistantSchema = z.object({
         | 
| 13 | 
             
            	name: z.string().min(1),
         | 
|  | |
| 100 | 
             
            			updatedAt: new Date(),
         | 
| 101 | 
             
            			userCount: 1,
         | 
| 102 | 
             
            			featured: false,
         | 
| 103 | 
            +
            			searchTokens: generateSearchTokens(parse.data.name),
         | 
| 104 | 
             
            		});
         | 
| 105 |  | 
| 106 | 
             
            		// add insertedId to user settings
         | 
 
			

 
		 
		