Team Ai
Modelpublic

Felipe97/llama-cpp-compiled

sourceHugging Faceupdated 21d agoView on Hugging Face
0likes1.2kdownloads
use-processing-state.svelte.ts325 linesDownload Raw Back to hooks
1import { STATS_UNITS } from '$lib/constants';2import { chatStore } from '$lib/stores';3import type { ApiProcessingState, LiveGenerationStats, LiveProcessingStats } from '$lib/types';4 5export interface UseProcessingStateReturn {6	readonly processingState: ApiProcessingState | null;7	getProcessingDetails(): string[];8	getTechnicalDetails(): string[];9	getProcessingMessage(): string;10	getPromptProgressText(): string | null;11	getLiveProcessingStats(): LiveProcessingStats | null;12	getLiveGenerationStats(): LiveGenerationStats | null;13	shouldShowDetails(): boolean;14	startMonitoring(): void;15	stopMonitoring(): void;16}17 18/**19 * useProcessingState - Reactive processing state hook20 *21 * This hook provides reactive access to the processing state of the server.22 * It directly reads from chatStore's reactive state and provides23 * formatted processing details for UI display.24 *25 * **Features:**26 * - Real-time processing state via direct reactive state binding27 * - Context and output token tracking28 * - Tokens per second calculation29 * - Automatic updates when streaming data arrives30 * - Supports multiple concurrent conversations31 *32 * @returns Hook interface with processing state and control methods33 */34export function useProcessingState(): UseProcessingStateReturn {35	let isMonitoring = $state(false);36	let lastKnownState = $state<ApiProcessingState | null>(null);37	let lastKnownProcessingStats = $state<LiveProcessingStats | null>(null);38 39	// Derive processing state reactively from chatStore's direct state40	const processingState = $derived.by(() => {41		if (!isMonitoring) {42			return lastKnownState;43		}44 45		// Read directly from the reactive state46		return chatStore.processing.activeState;47	});48 49	$effect(() => {50		if (processingState && isMonitoring) {51			lastKnownState = processingState;52		}53	});54 55	// Track last known processing stats for when promptProgress disappears56	$effect(() => {57		if (processingState?.promptProgress) {58			const { cache, processed, time_ms, total } = processingState.promptProgress;59			const actualProcessed = processed - cache;60			const actualTotal = total - cache;61 62			if (actualProcessed > 0 && time_ms > 0) {63				const tokensPerSecond = actualProcessed / (time_ms / 1000);64 65				lastKnownProcessingStats = {66					timeMs: time_ms,67					tokensPerSecond,68					tokensProcessed: actualProcessed,69					totalTokens: actualTotal70				};71			}72		}73	});74 75	function getETASecs(done: number, total: number, elapsedMs: number): number | undefined {76		const elapsedSecs = elapsedMs / 1000;77		const progressETASecs =78			done === 0 || elapsedSecs < 0.579				? undefined // can be the case for the 0% progress report80				: elapsedSecs * (total / done - 1);81 82		return progressETASecs;83	}84 85	function startMonitoring(): void {86		if (isMonitoring) return;87 88		isMonitoring = true;89	}90 91	function stopMonitoring(): void {92		if (!isMonitoring) return;93 94		isMonitoring = false;95	}96 97	function getProcessingMessage(): string {98		if (!processingState) {99			return 'Processing...';100		}101 102		switch (processingState.status) {103			case 'initializing':104				return 'Initializing...';105			case 'preparing':106				if (processingState.progressPercent !== undefined) {107					return `Processing (${processingState.progressPercent}%)`;108				}109 110				return 'Preparing response...';111			case 'generating':112				return '';113			default:114				return 'Processing...';115		}116	}117 118	function getProcessingDetails(): string[] {119		// Use current processing state or fall back to last known state120		const stateToUse = processingState || lastKnownState;121 122		if (!stateToUse) {123			return [];124		}125 126		const details: string[] = [];127 128		// Show prompt processing progress with ETA during preparation phase129		if (stateToUse.promptProgress) {130			const { cache, processed, time_ms, total } = stateToUse.promptProgress;131			const actualProcessed = processed - cache;132			const actualTotal = total - cache;133 134			if (actualProcessed < actualTotal && actualProcessed > 0) {135				const percent = Math.round((actualProcessed / actualTotal) * 100);136				const eta = getETASecs(actualProcessed, actualTotal, time_ms);137 138				if (eta !== undefined) {139					const etaSecs = Math.ceil(eta);140 141					details.push(`Processing ${percent}% (ETA: ${etaSecs}s)`);142				} else {143					details.push(`Processing ${percent}%`);144				}145			}146		}147 148		// Always show context info when we have valid data149		if (150			typeof stateToUse.contextTotal === 'number' &&151			stateToUse.contextUsed >= 0 &&152			stateToUse.contextTotal > 0153		) {154			const contextPercent = Math.round((stateToUse.contextUsed / stateToUse.contextTotal) * 100);155 156			details.push(157				`Context: ${stateToUse.contextUsed}/${stateToUse.contextTotal} (${contextPercent}%)`158			);159		}160 161		if (stateToUse.outputTokensUsed > 0) {162			// Handle infinite max_tokens (-1) case163			if (stateToUse.outputTokensMax <= 0) {164				details.push(`Output: ${stateToUse.outputTokensUsed}/∞`);165			} else {166				const outputPercent = Math.round(167					(stateToUse.outputTokensUsed / stateToUse.outputTokensMax) * 100168				);169 170				details.push(171					`Output: ${stateToUse.outputTokensUsed}/${stateToUse.outputTokensMax} (${outputPercent}%)`172				);173			}174		}175 176		if (stateToUse.tokensPerSecond && stateToUse.tokensPerSecond > 0) {177			details.push(`${stateToUse.tokensPerSecond.toFixed(1)} ${STATS_UNITS.TOKENS_PER_SECOND}`);178		}179 180		if (stateToUse.speculative) {181			details.push('Speculative decoding enabled');182		}183 184		return details;185	}186 187	/**188	 * Returns technical details without the progress message (for bottom bar)189	 */190	function getTechnicalDetails(): string[] {191		const stateToUse = processingState || lastKnownState;192 193		if (!stateToUse) {194			return [];195		}196 197		const details: string[] = [];198 199		// Always show context info when we have valid data200		if (201			typeof stateToUse.contextTotal === 'number' &&202			stateToUse.contextUsed >= 0 &&203			stateToUse.contextTotal > 0204		) {205			const contextPercent = Math.round((stateToUse.contextUsed / stateToUse.contextTotal) * 100);206 207			details.push(208				`Context: ${stateToUse.contextUsed}/${stateToUse.contextTotal} (${contextPercent}%)`209			);210		}211 212		if (stateToUse.outputTokensUsed > 0) {213			// Handle infinite max_tokens (-1) case214			if (stateToUse.outputTokensMax <= 0) {215				details.push(`Output: ${stateToUse.outputTokensUsed}/∞`);216			} else {217				const outputPercent = Math.round(218					(stateToUse.outputTokensUsed / stateToUse.outputTokensMax) * 100219				);220 221				details.push(222					`Output: ${stateToUse.outputTokensUsed}/${stateToUse.outputTokensMax} (${outputPercent}%)`223				);224			}225		}226 227		if (stateToUse.tokensPerSecond && stateToUse.tokensPerSecond > 0) {228			details.push(`${stateToUse.tokensPerSecond.toFixed(1)} ${STATS_UNITS.TOKENS_PER_SECOND}`);229		}230 231		if (stateToUse.speculative) {232			details.push('Speculative decoding enabled');233		}234 235		return details;236	}237 238	function shouldShowDetails(): boolean {239		return processingState !== null && processingState.status !== 'idle';240	}241 242	/**243	 * Returns a short progress message with percent244	 */245	function getPromptProgressText(): string | null {246		if (!processingState?.promptProgress) return null;247 248		const { cache, processed, total } = processingState.promptProgress;249		const actualProcessed = processed - cache;250		const actualTotal = total - cache;251		const percent = Math.round((actualProcessed / actualTotal) * 100);252		const eta = getETASecs(actualProcessed, actualTotal, processingState.promptProgress.time_ms);253 254		if (eta !== undefined) {255			const etaSecs = Math.ceil(eta);256 257			return `Processing ${percent}% (ETA: ${etaSecs}s)`;258		}259 260		return `Processing ${percent}%`;261	}262 263	/**264	 * Returns live processing statistics for display (prompt processing phase)265	 * Returns last known stats when promptProgress becomes unavailable266	 */267	function getLiveProcessingStats(): LiveProcessingStats | null {268		if (processingState?.promptProgress) {269			const { cache, processed, time_ms, total } = processingState.promptProgress;270			const actualProcessed = processed - cache;271			const actualTotal = total - cache;272 273			if (actualProcessed > 0 && time_ms > 0) {274				const tokensPerSecond = actualProcessed / (time_ms / 1000);275 276				return {277					timeMs: time_ms,278					tokensPerSecond,279					tokensProcessed: actualProcessed,280					totalTokens: actualTotal281				};282			}283		}284 285		// Return last known stats if promptProgress is no longer available286		return lastKnownProcessingStats;287	}288 289	/**290	 * Returns live generation statistics for display (token generation phase)291	 */292	function getLiveGenerationStats(): LiveGenerationStats | null {293		if (!processingState) return null;294 295		const { tokensDecoded, tokensPerSecond } = processingState;296 297		if (tokensDecoded <= 0) return null;298 299		// Calculate time from tokens and speed300		const timeMs =301			tokensPerSecond && tokensPerSecond > 0 ? (tokensDecoded / tokensPerSecond) * 1000 : 0;302 303		return {304			timeMs,305			tokensGenerated: tokensDecoded,306			tokensPerSecond: tokensPerSecond || 0307		};308	}309 310	return {311		getLiveGenerationStats,312		getLiveProcessingStats,313		getProcessingDetails,314		getProcessingMessage,315		getPromptProgressText,316		getTechnicalDetails,317		get processingState() {318			return processingState;319		},320		shouldShowDetails,321		startMonitoring,322		stopMonitoring323	};324}325