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.1 KiB88 linesraw
1import { NEWLINE } from '$lib/constants';
2import { MessageRole, MessageType } from '$lib/enums';
3import { ConversationTransferService } from '$lib/services/conversation-transfer.service';
4import type { ExportedConversation } from '$lib/types/database';
5import { strToU8, zipSync } from 'fflate';
6import { describe, expect, it } from 'vitest';
7
8function makeSession(id: string): ExportedConversation {
9	return {
10		conv: { currNode: `${id}-msg`, id, lastModified: 0, name: `Chat ${id}` },
11		messages: [
12			{
13				children: [],
14				content: `hello from ${id}`,
15				convId: id,
16				id: `${id}-msg`,
17				parent: null,
18				role: MessageRole.USER,
19				timestamp: 0,
20				type: MessageType.TEXT
21			}
22		]
23	} as unknown as ExportedConversation;
24}
25
26/**
27 * `parseImportFile` detects the format from the file contents. iOS has no UTI
28 * for `.jsonl`, so the picker cannot filter on it and the filename carries no
29 * guarantee: a JSONL export must import under any name.
30 */
31describe('ConversationTransferService.parseImportFile', () => {
32	it('imports a JSONL export whose name has no meaningful extension', async () => {
33		const jsonl = ConversationTransferService.serializeSessionToJsonl(makeSession('a'));
34		const sessions = await ConversationTransferService.parseImportFile(new File([jsonl], 'export'));
35
36		expect(sessions).toHaveLength(1);
37		expect(sessions[0].conv.id).toBe('a');
38		expect(sessions[0].messages[0].content).toBe('hello from a');
39	});
40
41	it('imports several sessions from one JSONL file', async () => {
42		const jsonl = [makeSession('a'), makeSession('b')]
43			.map((session) => ConversationTransferService.serializeSessionToJsonl(session))
44			.join(NEWLINE);
45		const sessions = await ConversationTransferService.parseImportFile(
46			new File([jsonl], 'export.txt')
47		);
48
49		expect(sessions.map((session) => session.conv.id)).toEqual(['a', 'b']);
50	});
51
52	it('imports a ZIP archive whose name has no meaningful extension', async () => {
53		const zipped = zipSync({
54			'a.jsonl': strToU8(ConversationTransferService.serializeSessionToJsonl(makeSession('a'))),
55			'b.jsonl': strToU8(ConversationTransferService.serializeSessionToJsonl(makeSession('b'))),
56			'notes.txt': strToU8('ignored')
57		});
58		const sessions = await ConversationTransferService.parseImportFile(
59			new File([zipped], 'archive')
60		);
61
62		expect(sessions.map((session) => session.conv.id).sort()).toEqual(['a', 'b']);
63	});
64
65	it('imports the legacy JSON array format', async () => {
66		const json = JSON.stringify([makeSession('a')], null, 2);
67		const sessions = await ConversationTransferService.parseImportFile(
68			new File([json], 'export.jsonl')
69		);
70
71		expect(sessions).toHaveLength(1);
72		expect(sessions[0].conv.id).toBe('a');
73	});
74
75	it('imports the legacy JSON single object format', async () => {
76		const json = JSON.stringify(makeSession('a'));
77		const sessions = await ConversationTransferService.parseImportFile(new File([json], 'export'));
78
79		expect(sessions).toHaveLength(1);
80		expect(sessions[0].conv.id).toBe('a');
81	});
82
83	it('rejects a file that holds neither format', async () => {
84		await expect(
85			ConversationTransferService.parseImportFile(new File(['not an export'], 'export.jsonl'))
86		).rejects.toThrow();
87	});
88});