char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Services consolidation refactor (#27239)3dc7285b4

char/aliceai-t5-moe
3.3 KiB116 linesraw
1/**
2 * Utility functions for conversation data manipulation
3 */
4import { MessageRole } from '$lib/enums';
5import type { DatabaseMessage } from '$lib/types';
6
7/**
8 * Model that generated the conversation's latest assistant message, or null
9 * when no assistant message carries one.
10 */
11export function getConversationModel(messages: readonly DatabaseMessage[]): string | null {
12	for (let i = messages.length - 1; i >= 0; i--) {
13		const message = messages[i];
14
15		if (message.role === MessageRole.ASSISTANT && message.model) return message.model;
16	}
17
18	return null;
19}
20
21/**
22 * Creates a map of conversation IDs to their message counts from exported conversation data
23 * @param exportedData - Array of exported conversations with their messages
24 * @returns Map of conversation ID to message count
25 */
26export function createMessageCountMap(
27	exportedData: Array<{ conv: DatabaseConversation; messages: DatabaseMessage[] }>
28): Map<string, number> {
29	const countMap = new Map<string, number>();
30
31	for (const item of exportedData) {
32		countMap.set(item.conv.id, item.messages.length);
33	}
34
35	return countMap;
36}
37
38/**
39 * Gets the message count for a specific conversation from the count map
40 * @param conversationId - The ID of the conversation
41 * @param countMap - Map of conversation IDs to message counts
42 * @returns The message count, or 0 if not found
43 */
44export function getMessageCount(conversationId: string, countMap: Map<string, number>): number {
45	return countMap.get(conversationId) ?? 0;
46}
47
48export interface ConversationTreeItem {
49	conversation: DatabaseConversation;
50	depth: number;
51}
52
53// Pinned conversations first, then by lastModified descending
54const comparePinnedThenRecent = (a: DatabaseConversation, b: DatabaseConversation) => {
55	if (a.pinned && !b.pinned) return -1;
56
57	if (!a.pinned && b.pinned) return 1;
58
59	return b.lastModified - a.lastModified;
60};
61
62/**
63 * Builds a flat tree of conversations with depth levels for nested forks.
64 * Accepts a pre-filtered list so search filtering stays in the component.
65 *
66 * Output order matches the sidebar render exactly: pinned first, then
67 * unpinned by lastModified desc, with forks interleaved under their parents.
68 * Range-select / marquee in the sidebar rely on this alignment.
69 */
70export function buildConversationTree(convs: DatabaseConversation[]): ConversationTreeItem[] {
71	const childrenByParent = new Map<string, DatabaseConversation[]>();
72	const forkIds = new Set<string>();
73
74	for (const conv of convs) {
75		if (conv.forkedFromConversationId) {
76			forkIds.add(conv.id);
77
78			const siblings = childrenByParent.get(conv.forkedFromConversationId) || [];
79
80			siblings.push(conv);
81			childrenByParent.set(conv.forkedFromConversationId, siblings);
82		}
83	}
84
85	const result: ConversationTreeItem[] = [];
86	const visited = new Set<string>();
87
88	function walk(conv: DatabaseConversation, depth: number) {
89		visited.add(conv.id);
90		result.push({ conversation: conv, depth });
91
92		const children = childrenByParent.get(conv.id);
93
94		if (children) {
95			children.sort(comparePinnedThenRecent);
96
97			for (const child of children) {
98				walk(child, depth + 1);
99			}
100		}
101	}
102
103	const roots = convs.filter((c) => !forkIds.has(c.id)).sort(comparePinnedThenRecent);
104
105	for (const root of roots) {
106		walk(root, 0);
107	}
108
109	for (const conv of convs) {
110		if (!visited.has(conv.id)) {
111			walk(conv, 1);
112		}
113	}
114
115	return result;
116}