Spaces:
				
			
			
	
			
			
					
		Running
		
			on 
			
			CPU Upgrade
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
			on 
			
			CPU Upgrade
	Add "disable streaming tokens" settings & remove media query (#1299)
Browse files- src/lib/components/chat/ChatMessage.svelte +5 -10
- src/lib/stores/settings.ts +1 -0
- src/lib/types/Settings.ts +4 -1
- src/lib/utils/isReduceMotion.ts +0 -5
- src/routes/+layout.server.ts +1 -0
- src/routes/conversation/[id]/+page.svelte +1 -4
- src/routes/settings/(nav)/+page.svelte +8 -0
- src/routes/settings/(nav)/+server.ts +3 -2
    	
        src/lib/components/chat/ChatMessage.svelte
    CHANGED
    
    | @@ -2,7 +2,7 @@ | |
| 2 | 
             
            	import { marked, type MarkedOptions } from "marked";
         | 
| 3 | 
             
            	import markedKatex from "marked-katex-extension";
         | 
| 4 | 
             
            	import type { Message, MessageFile } from "$lib/types/Message";
         | 
| 5 | 
            -
            	import { afterUpdate, createEventDispatcher,  | 
| 6 | 
             
            	import { deepestChild } from "$lib/utils/deepestChild";
         | 
| 7 | 
             
            	import { page } from "$app/stores";
         | 
| 8 |  | 
| @@ -30,9 +30,9 @@ | |
| 30 | 
             
            	} from "$lib/types/MessageUpdate";
         | 
| 31 | 
             
            	import { base } from "$app/paths";
         | 
| 32 | 
             
            	import { useConvTreeStore } from "$lib/stores/convTree";
         | 
| 33 | 
            -
            	import { isReducedMotion } from "$lib/utils/isReduceMotion";
         | 
| 34 | 
             
            	import Modal from "../Modal.svelte";
         | 
| 35 | 
             
            	import ToolUpdate from "./ToolUpdate.svelte";
         | 
|  | |
| 36 |  | 
| 37 | 
             
            	function sanitizeMd(md: string) {
         | 
| 38 | 
             
            		let ret = md
         | 
| @@ -80,9 +80,6 @@ | |
| 80 | 
             
            	let isCopied = false;
         | 
| 81 |  | 
| 82 | 
             
            	let initialized = false;
         | 
| 83 | 
            -
             | 
| 84 | 
            -
            	let reducedMotionMode = false;
         | 
| 85 | 
            -
             | 
| 86 | 
             
            	const renderer = new marked.Renderer();
         | 
| 87 | 
             
            	// For code blocks with simple backticks
         | 
| 88 | 
             
            	renderer.codespan = (code) => {
         | 
| @@ -118,12 +115,10 @@ | |
| 118 | 
             
            	$: emptyLoad =
         | 
| 119 | 
             
            		!message.content && (webSearchIsDone || (searchUpdates && searchUpdates.length === 0));
         | 
| 120 |  | 
| 121 | 
            -
            	 | 
| 122 | 
            -
            		reducedMotionMode = isReducedMotion(window);
         | 
| 123 | 
            -
            	});
         | 
| 124 |  | 
| 125 | 
             
            	afterUpdate(() => {
         | 
| 126 | 
            -
            		if ( | 
| 127 | 
             
            			return;
         | 
| 128 | 
             
            		}
         | 
| 129 |  | 
| @@ -301,7 +296,7 @@ | |
| 301 | 
             
            				class="prose max-w-none max-sm:prose-sm dark:prose-invert prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
         | 
| 302 | 
             
            				bind:this={contentEl}
         | 
| 303 | 
             
            			>
         | 
| 304 | 
            -
            				{#if isLast && loading &&  | 
| 305 | 
             
            					<IconLoading classNames="loading inline ml-2 first:ml-0" />
         | 
| 306 | 
             
            				{/if}
         | 
| 307 | 
             
            				{#each tokens as token}
         | 
|  | |
| 2 | 
             
            	import { marked, type MarkedOptions } from "marked";
         | 
| 3 | 
             
            	import markedKatex from "marked-katex-extension";
         | 
| 4 | 
             
            	import type { Message, MessageFile } from "$lib/types/Message";
         | 
| 5 | 
            +
            	import { afterUpdate, createEventDispatcher, tick } from "svelte";
         | 
| 6 | 
             
            	import { deepestChild } from "$lib/utils/deepestChild";
         | 
| 7 | 
             
            	import { page } from "$app/stores";
         | 
| 8 |  | 
|  | |
| 30 | 
             
            	} from "$lib/types/MessageUpdate";
         | 
| 31 | 
             
            	import { base } from "$app/paths";
         | 
| 32 | 
             
            	import { useConvTreeStore } from "$lib/stores/convTree";
         | 
|  | |
| 33 | 
             
            	import Modal from "../Modal.svelte";
         | 
| 34 | 
             
            	import ToolUpdate from "./ToolUpdate.svelte";
         | 
| 35 | 
            +
            	import { useSettingsStore } from "$lib/stores/settings";
         | 
| 36 |  | 
| 37 | 
             
            	function sanitizeMd(md: string) {
         | 
| 38 | 
             
            		let ret = md
         | 
|  | |
| 80 | 
             
            	let isCopied = false;
         | 
| 81 |  | 
| 82 | 
             
            	let initialized = false;
         | 
|  | |
|  | |
|  | |
| 83 | 
             
            	const renderer = new marked.Renderer();
         | 
| 84 | 
             
            	// For code blocks with simple backticks
         | 
| 85 | 
             
            	renderer.codespan = (code) => {
         | 
|  | |
| 115 | 
             
            	$: emptyLoad =
         | 
| 116 | 
             
            		!message.content && (webSearchIsDone || (searchUpdates && searchUpdates.length === 0));
         | 
| 117 |  | 
| 118 | 
            +
            	const settings = useSettingsStore();
         | 
|  | |
|  | |
| 119 |  | 
| 120 | 
             
            	afterUpdate(() => {
         | 
| 121 | 
            +
            		if ($settings.disableStream) {
         | 
| 122 | 
             
            			return;
         | 
| 123 | 
             
            		}
         | 
| 124 |  | 
|  | |
| 296 | 
             
            				class="prose max-w-none max-sm:prose-sm dark:prose-invert prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
         | 
| 297 | 
             
            				bind:this={contentEl}
         | 
| 298 | 
             
            			>
         | 
| 299 | 
            +
            				{#if isLast && loading && $settings.disableStream}
         | 
| 300 | 
             
            					<IconLoading classNames="loading inline ml-2 first:ml-0" />
         | 
| 301 | 
             
            				{/if}
         | 
| 302 | 
             
            				{#each tokens as token}
         | 
    	
        src/lib/stores/settings.ts
    CHANGED
    
    | @@ -16,6 +16,7 @@ type SettingsStore = { | |
| 16 | 
             
            	recentlySaved: boolean;
         | 
| 17 | 
             
            	assistants: Array<ObjectId | string>;
         | 
| 18 | 
             
            	tools?: Record<string, boolean>;
         | 
|  | |
| 19 | 
             
            };
         | 
| 20 |  | 
| 21 | 
             
            type SettingsStoreWritable = Writable<SettingsStore> & {
         | 
|  | |
| 16 | 
             
            	recentlySaved: boolean;
         | 
| 17 | 
             
            	assistants: Array<ObjectId | string>;
         | 
| 18 | 
             
            	tools?: Record<string, boolean>;
         | 
| 19 | 
            +
            	disableStream: boolean;
         | 
| 20 | 
             
            };
         | 
| 21 |  | 
| 22 | 
             
            type SettingsStoreWritable = Writable<SettingsStore> & {
         | 
    	
        src/lib/types/Settings.ts
    CHANGED
    
    | @@ -22,8 +22,10 @@ export interface Settings extends Timestamps { | |
| 22 |  | 
| 23 | 
             
            	assistants?: Assistant["_id"][];
         | 
| 24 | 
             
            	tools?: Record<string, boolean>;
         | 
|  | |
| 25 | 
             
            }
         | 
| 26 |  | 
|  | |
| 27 | 
             
            // TODO: move this to a constant file along with other constants
         | 
| 28 | 
             
            export const DEFAULT_SETTINGS = {
         | 
| 29 | 
             
            	shareConversationsWithModelAuthors: true,
         | 
| @@ -32,4 +34,5 @@ export const DEFAULT_SETTINGS = { | |
| 32 | 
             
            	customPrompts: {},
         | 
| 33 | 
             
            	assistants: [],
         | 
| 34 | 
             
            	tools: {},
         | 
| 35 | 
            -
             | 
|  | 
|  | |
| 22 |  | 
| 23 | 
             
            	assistants?: Assistant["_id"][];
         | 
| 24 | 
             
            	tools?: Record<string, boolean>;
         | 
| 25 | 
            +
            	disableStream: boolean;
         | 
| 26 | 
             
            }
         | 
| 27 |  | 
| 28 | 
            +
            export type SettingsEditable = Omit<Settings, "ethicsModalAcceptedAt" | "createdAt" | "updatedAt">;
         | 
| 29 | 
             
            // TODO: move this to a constant file along with other constants
         | 
| 30 | 
             
            export const DEFAULT_SETTINGS = {
         | 
| 31 | 
             
            	shareConversationsWithModelAuthors: true,
         | 
|  | |
| 34 | 
             
            	customPrompts: {},
         | 
| 35 | 
             
            	assistants: [],
         | 
| 36 | 
             
            	tools: {},
         | 
| 37 | 
            +
            	disableStream: false,
         | 
| 38 | 
            +
            } satisfies SettingsEditable;
         | 
    	
        src/lib/utils/isReduceMotion.ts
    DELETED
    
    | @@ -1,5 +0,0 @@ | |
| 1 | 
            -
            export function isReducedMotion(window: Window) {
         | 
| 2 | 
            -
            	const { matchMedia } = window;
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            	return matchMedia("(prefers-reduced-motion: reduce)").matches;
         | 
| 5 | 
            -
            }
         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | 
    	
        src/routes/+layout.server.ts
    CHANGED
    
    | @@ -147,6 +147,7 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => { | |
| 147 | 
             
            			customPrompts: settings?.customPrompts ?? {},
         | 
| 148 | 
             
            			assistants: userAssistants,
         | 
| 149 | 
             
            			tools: settings?.tools ?? {},
         | 
|  | |
| 150 | 
             
            		},
         | 
| 151 | 
             
            		models: models.map((model) => ({
         | 
| 152 | 
             
            			id: model.id,
         | 
|  | |
| 147 | 
             
            			customPrompts: settings?.customPrompts ?? {},
         | 
| 148 | 
             
            			assistants: userAssistants,
         | 
| 149 | 
             
            			tools: settings?.tools ?? {},
         | 
| 150 | 
            +
            			disableStream: settings?.disableStream ?? DEFAULT_SETTINGS.disableStream,
         | 
| 151 | 
             
            		},
         | 
| 152 | 
             
            		models: models.map((model) => ({
         | 
| 153 | 
             
            			id: model.id,
         | 
    	
        src/routes/conversation/[id]/+page.svelte
    CHANGED
    
    | @@ -23,7 +23,6 @@ | |
| 23 | 
             
            	import { fetchMessageUpdates } from "$lib/utils/messageUpdates";
         | 
| 24 | 
             
            	import { createConvTreeStore } from "$lib/stores/convTree";
         | 
| 25 | 
             
            	import type { v4 } from "uuid";
         | 
| 26 | 
            -
            	import { isReducedMotion } from "$lib/utils/isReduceMotion.js";
         | 
| 27 | 
             
            	import { useSettingsStore } from "$lib/stores/settings.js";
         | 
| 28 |  | 
| 29 | 
             
            	export let data;
         | 
| @@ -80,8 +79,6 @@ | |
| 80 | 
             
            			$isAborted = false;
         | 
| 81 | 
             
            			loading = true;
         | 
| 82 | 
             
            			pending = true;
         | 
| 83 | 
            -
            			const reducedMotionMode = isReducedMotion(window);
         | 
| 84 | 
            -
             | 
| 85 | 
             
            			const base64Files = await Promise.all(
         | 
| 86 | 
             
            				(files ?? []).map((file) =>
         | 
| 87 | 
             
            					file2base64(file).then((value) => ({
         | 
| @@ -237,7 +234,7 @@ | |
| 237 |  | 
| 238 | 
             
            				messageUpdates.push(update);
         | 
| 239 |  | 
| 240 | 
            -
            				if (update.type === MessageUpdateType.Stream &&  | 
| 241 | 
             
            					messageToWriteTo.content += update.token;
         | 
| 242 | 
             
            					pending = false;
         | 
| 243 | 
             
            					messages = [...messages];
         | 
|  | |
| 23 | 
             
            	import { fetchMessageUpdates } from "$lib/utils/messageUpdates";
         | 
| 24 | 
             
            	import { createConvTreeStore } from "$lib/stores/convTree";
         | 
| 25 | 
             
            	import type { v4 } from "uuid";
         | 
|  | |
| 26 | 
             
            	import { useSettingsStore } from "$lib/stores/settings.js";
         | 
| 27 |  | 
| 28 | 
             
            	export let data;
         | 
|  | |
| 79 | 
             
            			$isAborted = false;
         | 
| 80 | 
             
            			loading = true;
         | 
| 81 | 
             
            			pending = true;
         | 
|  | |
|  | |
| 82 | 
             
            			const base64Files = await Promise.all(
         | 
| 83 | 
             
            				(files ?? []).map((file) =>
         | 
| 84 | 
             
            					file2base64(file).then((value) => ({
         | 
|  | |
| 234 |  | 
| 235 | 
             
            				messageUpdates.push(update);
         | 
| 236 |  | 
| 237 | 
            +
            				if (update.type === MessageUpdateType.Stream && !$settings.disableStream) {
         | 
| 238 | 
             
            					messageToWriteTo.content += update.token;
         | 
| 239 | 
             
            					pending = false;
         | 
| 240 | 
             
            					messages = [...messages];
         | 
    	
        src/routes/settings/(nav)/+page.svelte
    CHANGED
    
    | @@ -46,6 +46,14 @@ | |
| 46 | 
             
            			</div>
         | 
| 47 | 
             
            		</label>
         | 
| 48 |  | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 49 | 
             
            		<div class="mt-12 flex flex-col gap-3">
         | 
| 50 | 
             
            			<a
         | 
| 51 | 
             
            				href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
         | 
|  | |
| 46 | 
             
            			</div>
         | 
| 47 | 
             
            		</label>
         | 
| 48 |  | 
| 49 | 
            +
            		<!-- svelte-ignore a11y-label-has-associated-control -->
         | 
| 50 | 
            +
            		<label class="mt-6 flex items-center">
         | 
| 51 | 
            +
            			<Switch name="disableStream" bind:checked={$settings.disableStream} />
         | 
| 52 | 
            +
            			<div class="inline cursor-pointer select-none items-center gap-2 pl-2">
         | 
| 53 | 
            +
            				Disable streaming tokens
         | 
| 54 | 
            +
            			</div>
         | 
| 55 | 
            +
            		</label>
         | 
| 56 | 
            +
             | 
| 57 | 
             
            		<div class="mt-12 flex flex-col gap-3">
         | 
| 58 | 
             
            			<a
         | 
| 59 | 
             
            				href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions"
         | 
    	
        src/routes/settings/(nav)/+server.ts
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 | 
             
            import { collections } from "$lib/server/database";
         | 
| 2 | 
             
            import { z } from "zod";
         | 
| 3 | 
             
            import { authCondition } from "$lib/server/auth";
         | 
| 4 | 
            -
            import { DEFAULT_SETTINGS } from "$lib/types/Settings";
         | 
| 5 |  | 
| 6 | 
             
            export async function POST({ request, locals }) {
         | 
| 7 | 
             
            	const body = await request.json();
         | 
| @@ -16,8 +16,9 @@ export async function POST({ request, locals }) { | |
| 16 | 
             
            			activeModel: z.string().default(DEFAULT_SETTINGS.activeModel),
         | 
| 17 | 
             
            			customPrompts: z.record(z.string()).default({}),
         | 
| 18 | 
             
            			tools: z.record(z.boolean()).optional(),
         | 
|  | |
| 19 | 
             
            		})
         | 
| 20 | 
            -
            		.parse(body);
         | 
| 21 |  | 
| 22 | 
             
            	await collections.settings.updateOne(
         | 
| 23 | 
             
            		authCondition(locals),
         | 
|  | |
| 1 | 
             
            import { collections } from "$lib/server/database";
         | 
| 2 | 
             
            import { z } from "zod";
         | 
| 3 | 
             
            import { authCondition } from "$lib/server/auth";
         | 
| 4 | 
            +
            import { DEFAULT_SETTINGS, type SettingsEditable } from "$lib/types/Settings";
         | 
| 5 |  | 
| 6 | 
             
            export async function POST({ request, locals }) {
         | 
| 7 | 
             
            	const body = await request.json();
         | 
|  | |
| 16 | 
             
            			activeModel: z.string().default(DEFAULT_SETTINGS.activeModel),
         | 
| 17 | 
             
            			customPrompts: z.record(z.string()).default({}),
         | 
| 18 | 
             
            			tools: z.record(z.boolean()).optional(),
         | 
| 19 | 
            +
            			disableStream: z.boolean().default(false),
         | 
| 20 | 
             
            		})
         | 
| 21 | 
            +
            		.parse(body) satisfies SettingsEditable;
         | 
| 22 |  | 
| 23 | 
             
            	await collections.settings.updateOne(
         | 
| 24 | 
             
            		authCondition(locals),
         | 

