kazutab commited on
Commit
d4da86c
·
verified ·
1 Parent(s): ef6e870

Upload folder using huggingface_hub (part 9)

Browse files
backend/llama.cpp/tools/ui/tests/unit/pwa.spec.ts ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { describe, expect, it } from 'vitest';
4
+
5
+ const DIST_DIR = resolve(__dirname, '../../dist');
6
+ const distExists = existsSync(DIST_DIR);
7
+
8
+ // PWA Build Output tests are integration tests that require a built dist/.
9
+ // CI builds first then runs these tests; local devs should run `npm run build` or use `npm run test:pwa`.
10
+ describe('PWA Build Output', () => {
11
+ if (!distExists) {
12
+ console.warn(`⚠ Skipping PWA Build Output tests - dist/ not found (run 'npm run build' first)`);
13
+ it('skipped - dist/ not found', () => {});
14
+ return;
15
+ }
16
+
17
+ const swContent = readFileSync(resolve(DIST_DIR, 'sw.js'), 'utf-8');
18
+ const indexContent = readFileSync(resolve(DIST_DIR, 'index.html'), 'utf-8');
19
+
20
+ describe('Core files exist', () => {
21
+ it('service worker (sw.js) exists', () => {
22
+ expect(existsSync(resolve(DIST_DIR, 'sw.js')), 'sw.js not found').toBeTruthy();
23
+ });
24
+
25
+ it('workbox library exists (hashed filename)', () => {
26
+ // SvelteKit generates workbox-{hash}.js files
27
+ const files = readdirSync(DIST_DIR).filter((f) => f.match(/^workbox-[^.]+\.js$/));
28
+ expect(files.length).toBeGreaterThan(0);
29
+ });
30
+
31
+ it('manifest.webmanifest exists', () => {
32
+ expect(
33
+ existsSync(resolve(DIST_DIR, 'manifest.webmanifest')),
34
+ 'manifest.webmanifest not found'
35
+ ).toBeTruthy();
36
+ });
37
+
38
+ it('SvelteKit bundle.js exists in _app/immutable/', () => {
39
+ // SvelteKit generates hashed bundle names in _app/immutable/
40
+ const appDir = resolve(DIST_DIR, '_app', 'immutable');
41
+ expect(existsSync(appDir), '_app/immutable/ not found').toBeTruthy();
42
+ const files = readdirSync(appDir).filter((f) => f.startsWith('bundle.') && f.endsWith('.js'));
43
+ expect(files.length).toBeGreaterThan(0);
44
+ });
45
+
46
+ it('SvelteKit bundle.css exists in _app/immutable/assets/', () => {
47
+ // SvelteKit generates hashed CSS bundles in _app/immutable/assets/
48
+ const cssDir = resolve(DIST_DIR, '_app', 'immutable', 'assets');
49
+ expect(existsSync(cssDir), '_app/immutable/assets/ not found').toBeTruthy();
50
+ const files = readdirSync(cssDir).filter(
51
+ (f) => f.startsWith('bundle.') && f.endsWith('.css')
52
+ );
53
+ expect(files.length).toBeGreaterThan(0);
54
+ });
55
+
56
+ it('version.json exists in _app/', () => {
57
+ // SvelteKit stores version.json in _app directory
58
+ expect(
59
+ existsSync(resolve(DIST_DIR, '_app', 'version.json')),
60
+ '_app/version.json not found'
61
+ ).toBeTruthy();
62
+ });
63
+ });
64
+
65
+ describe('version.json content', () => {
66
+ it('has valid JSON with version field', () => {
67
+ const content = readFileSync(resolve(DIST_DIR, '_app', 'version.json'), 'utf-8');
68
+ const parsed = JSON.parse(content);
69
+ expect(parsed).toHaveProperty('version');
70
+ expect(typeof parsed.version).toBe('string');
71
+ expect(parsed.version.length).toBeGreaterThan(0);
72
+ });
73
+ });
74
+
75
+ describe('Service worker content', () => {
76
+ it('service worker has minified self.define format', () => {
77
+ expect(swContent).toBeTruthy();
78
+ // SvelteKit's workbox-plugin-sveltekit produces a minified SW with self.define
79
+ expect(swContent).toMatch(/if\(!self.define\)/);
80
+ });
81
+
82
+ it('references hashed workbox file (SvelteKit build output)', () => {
83
+ expect(swContent).toBeTruthy();
84
+ // SvelteKit's workbox-plugin-sveltekit references hashed workbox files
85
+ expect(swContent).toMatch(/define\(\["\.\/workbox-[a-zA-Z0-9]+"\]/);
86
+ });
87
+
88
+ it('precache contains SvelteKit bundle.js with content hash', () => {
89
+ expect(swContent).toBeTruthy();
90
+ // SvelteKit uses content-hashed bundle names in _app/immutable/
91
+ expect(swContent).toMatch(/"_app\/immutable\/bundle\.[a-zA-Z0-9_-]+\.js"/);
92
+ });
93
+
94
+ it('precache contains SvelteKit bundle.css with content hash', () => {
95
+ expect(swContent).toBeTruthy();
96
+ // SvelteKit uses content-hashed CSS bundle names in _app/immutable/assets/
97
+ expect(swContent).toMatch(/"_app\/immutable\/assets\/bundle\.[a-zA-Z0-9_-]+\.css"/);
98
+ });
99
+
100
+ it('precache contains _app/version.json', () => {
101
+ expect(swContent).toBeTruthy();
102
+ // SvelteKit stores version.json in _app directory
103
+ expect(swContent).toMatch(/"_app\/version\.json"/);
104
+ });
105
+
106
+ it('precache contains manifest.webmanifest', () => {
107
+ expect(swContent).toBeTruthy();
108
+ expect(swContent).toMatch(/"manifest\.webmanifest"/);
109
+ });
110
+
111
+ it('no navigation route — API endpoints bypass PWA', () => {
112
+ expect(swContent).toBeTruthy();
113
+ // NavigationRoute is intentionally absent so direct browser
114
+ // navigation to server API endpoints returns JSON, not HTML.
115
+ expect(swContent).not.toMatch(/NavigationRoute/);
116
+ });
117
+
118
+ it('has runtime caching for API routes', () => {
119
+ expect(swContent).toBeTruthy();
120
+ expect(swContent).toMatch(/api-cache/);
121
+ expect(swContent).toMatch(/NetworkFirst/);
122
+ });
123
+ });
124
+
125
+ describe('index.html content', () => {
126
+ it('has modulepreload link for SvelteKit bundle with content hash', () => {
127
+ expect(indexContent).toBeTruthy();
128
+ // SvelteKit generates hashed bundle names in _app/immutable/
129
+ expect(indexContent).toMatch(/href="(\.\/|\/)_app\/immutable\/bundle\.[a-zA-Z0-9_-]+\.js"/);
130
+ });
131
+
132
+ it('has stylesheet link for SvelteKit bundle.css with content hash', () => {
133
+ expect(indexContent).toBeTruthy();
134
+ expect(indexContent).toMatch(
135
+ /href="(\.\/|\/)_app\/immutable\/assets\/bundle\.[a-zA-Z0-9_-]+\.css"/
136
+ );
137
+ });
138
+
139
+ it('has dynamic import for SvelteKit bundle with content hash', () => {
140
+ expect(indexContent).toBeTruthy();
141
+ expect(indexContent).toMatch(
142
+ /import\("(\.\/|\/)_app\/immutable\/bundle\.[a-zA-Z0-9_-]+\.js"\)/
143
+ );
144
+ });
145
+
146
+ it('has __sveltekit__ variable (SvelteKit adds hash suffix)', () => {
147
+ expect(indexContent).toBeTruthy();
148
+ // SvelteKit 2.x uses __sveltekit__ as base with random suffix
149
+ expect(indexContent).toMatch(/__sveltekit_[a-zA-Z0-9-]+/);
150
+ });
151
+
152
+ it('has PWA manifest link', () => {
153
+ expect(indexContent).toBeTruthy();
154
+ expect(indexContent).toMatch(/rel="manifest" href="(\.?\/)?manifest\.webmanifest"/);
155
+ });
156
+
157
+ it('has apple-touch-icon link', () => {
158
+ expect(indexContent).toBeTruthy();
159
+ expect(indexContent).toMatch(/rel="apple-touch-icon"/);
160
+ });
161
+
162
+ it('has _app paths for SvelteKit bundles', () => {
163
+ expect(indexContent).toBeTruthy();
164
+ // SvelteKit uses _app paths for hashed assets
165
+ expect(indexContent).toMatch(/_app\//);
166
+ });
167
+ });
168
+
169
+ describe('SvelteKit _app directory', () => {
170
+ it('_app directory exists (SvelteKit uses it for hashed assets)', () => {
171
+ expect(existsSync(resolve(DIST_DIR, '_app'))).toBeTruthy();
172
+ });
173
+ });
174
+
175
+ describe('Hashed workbox files', () => {
176
+ it('workbox-*.js files exist in dist root (SvelteKit build output)', () => {
177
+ const files = readdirSync(DIST_DIR).filter((f) => f.match(/^workbox-[^.]+\.js$/));
178
+ expect(files.length).toBeGreaterThan(0);
179
+ });
180
+ });
181
+
182
+ describe('Static assets', () => {
183
+ it('has favicon.ico', () => {
184
+ expect(existsSync(resolve(DIST_DIR, 'favicon.ico'))).toBeTruthy();
185
+ });
186
+
187
+ it('has PWA icons', () => {
188
+ expect(existsSync(resolve(DIST_DIR, 'pwa-64x64.png'))).toBeTruthy();
189
+ expect(existsSync(resolve(DIST_DIR, 'pwa-192x192.png'))).toBeTruthy();
190
+ expect(existsSync(resolve(DIST_DIR, 'pwa-512x512.png'))).toBeTruthy();
191
+ });
192
+ });
193
+ });
backend/llama.cpp/tools/ui/tests/unit/reasoning-context.test.ts ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, it, expect } from 'vitest';
2
+ import { MessageRole } from '$lib/enums';
3
+
4
+ /**
5
+ * Tests for the new reasoning content handling.
6
+ * In the new architecture, reasoning content is stored in a dedicated
7
+ * `reasoningContent` field on DatabaseMessage, not embedded in content with tags.
8
+ * The API sends it as `reasoning_content` on ApiChatMessageData.
9
+ */
10
+
11
+ describe('reasoning content in new structured format', () => {
12
+ it('reasoning is stored as separate field, not in content', () => {
13
+ // Simulate what the new chat store does
14
+ const message = {
15
+ content: 'The answer is 4.',
16
+ reasoningContent: 'Let me think: 2+2=4, basic arithmetic.'
17
+ };
18
+
19
+ // Content should be clean
20
+ expect(message.content).not.toContain('<<<');
21
+ expect(message.content).toBe('The answer is 4.');
22
+
23
+ // Reasoning in dedicated field
24
+ expect(message.reasoningContent).toBe('Let me think: 2+2=4, basic arithmetic.');
25
+ });
26
+
27
+ it('convertDbMessageToApiChatMessageData includes reasoning_content', () => {
28
+ // Simulate the conversion logic
29
+ const dbMessage = {
30
+ role: MessageRole.ASSISTANT,
31
+ content: 'The answer is 4.',
32
+ reasoningContent: 'Let me think: 2+2=4, basic arithmetic.'
33
+ };
34
+
35
+ const apiMessage: Record<string, unknown> = {
36
+ role: dbMessage.role,
37
+ content: dbMessage.content
38
+ };
39
+ if (dbMessage.reasoningContent) {
40
+ apiMessage.reasoning_content = dbMessage.reasoningContent;
41
+ }
42
+
43
+ expect(apiMessage.content).toBe('The answer is 4.');
44
+ expect(apiMessage.reasoning_content).toBe('Let me think: 2+2=4, basic arithmetic.');
45
+ // No internal tags leak into either field
46
+ expect(apiMessage.content).not.toContain('<<<');
47
+ expect(apiMessage.reasoning_content).not.toContain('<<<');
48
+ });
49
+
50
+ it('API message excludes reasoning when excludeReasoningFromContext is true', () => {
51
+ const dbMessage = {
52
+ role: MessageRole.ASSISTANT,
53
+ content: 'The answer is 4.',
54
+ reasoningContent: 'internal thinking'
55
+ };
56
+
57
+ const excludeReasoningFromContext = true;
58
+
59
+ const apiMessage: Record<string, unknown> = {
60
+ role: dbMessage.role,
61
+ content: dbMessage.content
62
+ };
63
+ if (!excludeReasoningFromContext && dbMessage.reasoningContent) {
64
+ apiMessage.reasoning_content = dbMessage.reasoningContent;
65
+ }
66
+
67
+ expect(apiMessage.content).toBe('The answer is 4.');
68
+ expect(apiMessage.reasoning_content).toBeUndefined();
69
+ });
70
+
71
+ it('handles messages with no reasoning', () => {
72
+ const dbMessage = {
73
+ role: MessageRole.ASSISTANT,
74
+ content: 'No reasoning here.',
75
+ reasoningContent: undefined
76
+ };
77
+
78
+ const apiMessage: Record<string, unknown> = {
79
+ role: dbMessage.role,
80
+ content: dbMessage.content
81
+ };
82
+ if (dbMessage.reasoningContent) {
83
+ apiMessage.reasoning_content = dbMessage.reasoningContent;
84
+ }
85
+
86
+ expect(apiMessage.content).toBe('No reasoning here.');
87
+ expect(apiMessage.reasoning_content).toBeUndefined();
88
+ });
89
+ });
backend/llama.cpp/tools/ui/tests/unit/redact.test.ts ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from 'vitest';
2
+ import { redactValue } from '$lib/utils/redact';
3
+
4
+ describe('redactValue', () => {
5
+ it('returns [redacted] by default', () => {
6
+ expect(redactValue('secret-token')).toBe('[redacted]');
7
+ });
8
+
9
+ it('shows last N characters when showLastChars is provided', () => {
10
+ expect(redactValue('session-abc12', 5)).toBe('....abc12');
11
+ });
12
+
13
+ it('handles value shorter than showLastChars', () => {
14
+ expect(redactValue('ab', 5)).toBe('....ab');
15
+ });
16
+
17
+ it('returns [redacted] when showLastChars is 0', () => {
18
+ expect(redactValue('secret', 0)).toBe('[redacted]');
19
+ });
20
+ });
backend/llama.cpp/tools/ui/tests/unit/request-helpers.test.ts ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ getRequestUrl,
4
+ getRequestMethod,
5
+ getRequestBody,
6
+ summarizeRequestBody,
7
+ formatDiagnosticErrorMessage,
8
+ extractJsonRpcMethods
9
+ } from '$lib/utils/request-helpers';
10
+
11
+ describe('getRequestUrl', () => {
12
+ it('returns a plain string input as-is', () => {
13
+ expect(getRequestUrl('https://example.com/mcp')).toBe('https://example.com/mcp');
14
+ });
15
+
16
+ it('returns href from a URL object', () => {
17
+ expect(getRequestUrl(new URL('https://example.com/mcp'))).toBe('https://example.com/mcp');
18
+ });
19
+
20
+ it('returns url from a Request object', () => {
21
+ const req = new Request('https://example.com/mcp');
22
+ expect(getRequestUrl(req)).toBe('https://example.com/mcp');
23
+ });
24
+ });
25
+
26
+ describe('getRequestMethod', () => {
27
+ it('prefers method from init', () => {
28
+ expect(getRequestMethod('https://example.com', { method: 'POST' })).toBe('POST');
29
+ });
30
+
31
+ it('falls back to Request.method', () => {
32
+ const req = new Request('https://example.com', { method: 'PUT' });
33
+ expect(getRequestMethod(req)).toBe('PUT');
34
+ });
35
+
36
+ it('falls back to baseInit.method', () => {
37
+ expect(getRequestMethod('https://example.com', undefined, { method: 'DELETE' })).toBe('DELETE');
38
+ });
39
+
40
+ it('defaults to GET', () => {
41
+ expect(getRequestMethod('https://example.com')).toBe('GET');
42
+ });
43
+ });
44
+
45
+ describe('getRequestBody', () => {
46
+ it('returns body from init', () => {
47
+ expect(getRequestBody('https://example.com', { body: 'payload' })).toBe('payload');
48
+ });
49
+
50
+ it('returns undefined when no body is present', () => {
51
+ expect(getRequestBody('https://example.com')).toBeUndefined();
52
+ });
53
+ });
54
+
55
+ describe('summarizeRequestBody', () => {
56
+ it('returns empty for null', () => {
57
+ expect(summarizeRequestBody(null)).toEqual({ kind: 'empty' });
58
+ });
59
+
60
+ it('returns empty for undefined', () => {
61
+ expect(summarizeRequestBody(undefined)).toEqual({ kind: 'empty' });
62
+ });
63
+
64
+ it('returns string kind with size', () => {
65
+ expect(summarizeRequestBody('hello')).toEqual({ kind: 'string', size: 5 });
66
+ });
67
+
68
+ it('returns blob kind with size', () => {
69
+ const blob = new Blob(['abc']);
70
+ expect(summarizeRequestBody(blob)).toEqual({ kind: 'blob', size: 3 });
71
+ });
72
+
73
+ it('returns formdata kind', () => {
74
+ expect(summarizeRequestBody(new FormData())).toEqual({ kind: 'formdata' });
75
+ });
76
+
77
+ it('returns arraybuffer kind with size', () => {
78
+ expect(summarizeRequestBody(new ArrayBuffer(8))).toEqual({ kind: 'arraybuffer', size: 8 });
79
+ });
80
+ });
81
+
82
+ describe('formatDiagnosticErrorMessage', () => {
83
+ it('appends CORS hint for Failed to fetch', () => {
84
+ expect(formatDiagnosticErrorMessage(new TypeError('Failed to fetch'))).toBe(
85
+ 'Failed to fetch (check CORS?)'
86
+ );
87
+ });
88
+
89
+ it('passes through other error messages unchanged', () => {
90
+ expect(formatDiagnosticErrorMessage(new Error('timeout'))).toBe('timeout');
91
+ });
92
+
93
+ it('handles non-Error values', () => {
94
+ expect(formatDiagnosticErrorMessage('some string')).toBe('some string');
95
+ });
96
+ });
97
+
98
+ describe('extractJsonRpcMethods', () => {
99
+ it('extracts methods from a JSON-RPC array', () => {
100
+ const body = JSON.stringify([
101
+ { jsonrpc: '2.0', id: 1, method: 'initialize' },
102
+ { jsonrpc: '2.0', method: 'notifications/initialized' }
103
+ ]);
104
+ expect(extractJsonRpcMethods(body)).toEqual(['initialize', 'notifications/initialized']);
105
+ });
106
+
107
+ it('extracts method from a single JSON-RPC message', () => {
108
+ const body = JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' });
109
+ expect(extractJsonRpcMethods(body)).toEqual(['tools/list']);
110
+ });
111
+
112
+ it('returns undefined for non-string body', () => {
113
+ expect(extractJsonRpcMethods(null)).toBeUndefined();
114
+ expect(extractJsonRpcMethods(undefined)).toBeUndefined();
115
+ });
116
+
117
+ it('returns undefined for invalid JSON', () => {
118
+ expect(extractJsonRpcMethods('not json')).toBeUndefined();
119
+ });
120
+
121
+ it('returns undefined when no methods found', () => {
122
+ expect(extractJsonRpcMethods(JSON.stringify({ foo: 'bar' }))).toBeUndefined();
123
+ });
124
+ });
backend/llama.cpp/tools/ui/tests/unit/sanitize-headers.test.ts ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from 'vitest';
2
+ import { sanitizeHeaders } from '$lib/utils/api-headers';
3
+ import { CORS_PROXY_HEADER_PREFIX } from '$lib/constants';
4
+
5
+ describe('sanitizeHeaders', () => {
6
+ it('returns empty object for undefined input', () => {
7
+ expect(sanitizeHeaders()).toEqual({});
8
+ });
9
+
10
+ it('passes through non-sensitive headers', () => {
11
+ const headers = new Headers({ 'content-type': 'application/json', accept: 'text/html' });
12
+ expect(sanitizeHeaders(headers)).toEqual({
13
+ 'content-type': 'application/json',
14
+ accept: 'text/html'
15
+ });
16
+ });
17
+
18
+ it('redacts known sensitive headers', () => {
19
+ const headers = new Headers({
20
+ authorization: 'Bearer secret',
21
+ 'x-api-key': 'key-123',
22
+ 'content-type': 'application/json'
23
+ });
24
+ const result = sanitizeHeaders(headers);
25
+ expect(result.authorization).toBe('[redacted]');
26
+ expect(result['x-api-key']).toBe('[redacted]');
27
+ expect(result['content-type']).toBe('application/json');
28
+ });
29
+
30
+ it('partially redacts headers specified in partialRedactHeaders', () => {
31
+ const headers = new Headers({ 'mcp-session-id': 'session-12345' });
32
+ const partial = new Map([['mcp-session-id', 5]]);
33
+ expect(sanitizeHeaders(headers, undefined, partial)['mcp-session-id']).toBe('....12345');
34
+ });
35
+
36
+ it('fully redacts mcp-session-id when no partialRedactHeaders is given', () => {
37
+ const headers = new Headers({ 'mcp-session-id': 'session-12345' });
38
+ expect(sanitizeHeaders(headers)['mcp-session-id']).toBe('[redacted]');
39
+ });
40
+
41
+ it('redacts extra headers provided by the caller', () => {
42
+ const headers = new Headers({
43
+ 'x-vendor-key': 'vendor-secret',
44
+ 'content-type': 'application/json'
45
+ });
46
+ const result = sanitizeHeaders(headers, ['x-vendor-key']);
47
+ expect(result['x-vendor-key']).toBe('[redacted]');
48
+ expect(result['content-type']).toBe('application/json');
49
+ });
50
+
51
+ it('handles case-insensitive extra header names', () => {
52
+ const headers = new Headers({ 'X-Custom-Token': 'token-value' });
53
+ const result = sanitizeHeaders(headers, ['X-CUSTOM-TOKEN']);
54
+ expect(result['x-custom-token']).toBe('[redacted]');
55
+ });
56
+
57
+ it('redacts proxied sensitive and custom target headers', () => {
58
+ const proxiedAuthorization = `${CORS_PROXY_HEADER_PREFIX}authorization`;
59
+ const proxiedSessionId = `${CORS_PROXY_HEADER_PREFIX}mcp-session-id`;
60
+ const proxiedVendorKey = `${CORS_PROXY_HEADER_PREFIX}x-vendor-key`;
61
+ const headers = new Headers({
62
+ [proxiedAuthorization]: 'Bearer secret',
63
+ [proxiedSessionId]: 'session-12345',
64
+ [proxiedVendorKey]: 'vendor-secret'
65
+ });
66
+ const partial = new Map([['mcp-session-id', 5]]);
67
+ const result = sanitizeHeaders(headers, ['x-vendor-key'], partial);
68
+
69
+ expect(result[proxiedAuthorization]).toBe('[redacted]');
70
+ expect(result[proxiedSessionId]).toBe('....12345');
71
+ expect(result[proxiedVendorKey]).toBe('[redacted]');
72
+ });
73
+ });
backend/llama.cpp/tools/ui/tests/unit/stream-discovery.test.ts ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ChatService } from '$lib/services/chat.service';
3
+ import type { ApiStreamSession } from '$lib/types';
4
+
5
+ function makeSession(overrides: Partial<ApiStreamSession>): ApiStreamSession {
6
+ return {
7
+ conversation_id: 'conv',
8
+ is_done: true,
9
+ total_bytes: 0,
10
+ started_at: 0,
11
+ completed_at: 0,
12
+ ...overrides
13
+ };
14
+ }
15
+
16
+ describe('selectActiveStream', () => {
17
+ it('returns null on empty input', () => {
18
+ expect(ChatService.selectActiveStream([])).toBeNull();
19
+ });
20
+
21
+ it('returns null on null or undefined input', () => {
22
+ expect(ChatService.selectActiveStream(null)).toBeNull();
23
+ expect(ChatService.selectActiveStream(undefined)).toBeNull();
24
+ });
25
+
26
+ it('returns the single session when it is running', () => {
27
+ const s = makeSession({ conversation_id: 'only', is_done: false, started_at: 42 });
28
+ expect(ChatService.selectActiveStream([s])).toBe(s);
29
+ });
30
+
31
+ it('returns null when the single session is finalized', () => {
32
+ const s = makeSession({ conversation_id: 'only', is_done: true, started_at: 42 });
33
+ expect(ChatService.selectActiveStream([s])).toBeNull();
34
+ });
35
+
36
+ it('prefers a still running session over a finalized one regardless of started_at', () => {
37
+ const finalized = makeSession({ conversation_id: 'old', is_done: true, started_at: 1000 });
38
+ const running = makeSession({ conversation_id: 'new', is_done: false, started_at: 10 });
39
+ expect(ChatService.selectActiveStream([finalized, running])?.conversation_id).toBe('new');
40
+ expect(ChatService.selectActiveStream([running, finalized])?.conversation_id).toBe('new');
41
+ });
42
+
43
+ it('among running sessions, picks the most recently started one', () => {
44
+ const a = makeSession({ conversation_id: 'a', is_done: false, started_at: 100 });
45
+ const b = makeSession({ conversation_id: 'b', is_done: false, started_at: 200 });
46
+ const c = makeSession({ conversation_id: 'c', is_done: false, started_at: 150 });
47
+ expect(ChatService.selectActiveStream([a, b, c])?.conversation_id).toBe('b');
48
+ expect(ChatService.selectActiveStream([c, a, b])?.conversation_id).toBe('b');
49
+ });
50
+
51
+ it('returns null when all sessions are finalized, the DB already holds the content', () => {
52
+ const a = makeSession({ conversation_id: 'a', is_done: true, started_at: 10 });
53
+ const b = makeSession({ conversation_id: 'b', is_done: true, started_at: 30 });
54
+ const c = makeSession({ conversation_id: 'c', is_done: true, started_at: 20 });
55
+ expect(ChatService.selectActiveStream([a, b, c])).toBeNull();
56
+ });
57
+
58
+ it('keeps the first match on ties when both are running with identical started_at', () => {
59
+ // reduce visits left to right, the initial accumulator stays unless a strictly greater value appears
60
+ const a = makeSession({ conversation_id: 'first', is_done: false, started_at: 50 });
61
+ const b = makeSession({ conversation_id: 'second', is_done: false, started_at: 50 });
62
+ expect(ChatService.selectActiveStream([a, b])?.conversation_id).toBe('first');
63
+ });
64
+
65
+ it('handles a typical realistic mix: two finalized old, one freshly running, one freshly finalized', () => {
66
+ const old1 = makeSession({ conversation_id: 'old1', is_done: true, started_at: 100 });
67
+ const old2 = makeSession({ conversation_id: 'old2', is_done: true, started_at: 200 });
68
+ const freshFin = makeSession({ conversation_id: 'freshFin', is_done: true, started_at: 500 });
69
+ const running = makeSession({ conversation_id: 'running', is_done: false, started_at: 400 });
70
+ expect(ChatService.selectActiveStream([old1, old2, freshFin, running])?.conversation_id).toBe(
71
+ 'running'
72
+ );
73
+ });
74
+ });
backend/llama.cpp/tools/ui/tests/unit/stream-resume.test.ts ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
2
+
3
+ // node env unit project has no DOM, install a minimal localStorage backed by a Map
4
+ beforeAll(() => {
5
+ const store = new Map<string, string>();
6
+ const polyfill: Storage = {
7
+ get length() {
8
+ return store.size;
9
+ },
10
+ clear: () => store.clear(),
11
+ getItem: (k) => (store.has(k) ? store.get(k)! : null),
12
+ key: (i) => Array.from(store.keys())[i] ?? null,
13
+ removeItem: (k) => {
14
+ store.delete(k);
15
+ },
16
+ setItem: (k, v) => {
17
+ store.set(k, String(v));
18
+ }
19
+ };
20
+ (globalThis as unknown as { localStorage: Storage }).localStorage = polyfill;
21
+ });
22
+
23
+ import { ChatService } from '$lib/services/chat.service';
24
+ import { STREAM_RESUME_LOCALSTORAGE_KEY_PREFIX } from '$lib/constants';
25
+
26
+ describe('ChatService stream resume', () => {
27
+ beforeEach(() => {
28
+ localStorage.clear();
29
+ });
30
+ afterEach(() => {
31
+ localStorage.clear();
32
+ });
33
+
34
+ it('returns null when no state exists for the conversation', () => {
35
+ expect(ChatService.getStreamState('conv-a')).toBeNull();
36
+ });
37
+
38
+ it('saves and reads back the byte count', () => {
39
+ ChatService.saveStreamState('conv-a', 4242);
40
+ const got = ChatService.getStreamState('conv-a');
41
+ expect(got).not.toBeNull();
42
+ expect(got!.bytesReceived).toBe(4242);
43
+ expect(typeof got!.updatedAt).toBe('number');
44
+ });
45
+
46
+ it('overwrites the previous byte count on a new save for the same conversation', () => {
47
+ ChatService.saveStreamState('conv-a', 100);
48
+ ChatService.saveStreamState('conv-a', 200);
49
+ const got = ChatService.getStreamState('conv-a');
50
+ expect(got!.bytesReceived).toBe(200);
51
+ });
52
+
53
+ it('keeps states for distinct conversations isolated', () => {
54
+ ChatService.saveStreamState('conv-a', 10);
55
+ ChatService.saveStreamState('conv-b', 20);
56
+ expect(ChatService.getStreamState('conv-a')!.bytesReceived).toBe(10);
57
+ expect(ChatService.getStreamState('conv-b')!.bytesReceived).toBe(20);
58
+ });
59
+
60
+ it('clears the state for a given conversation', () => {
61
+ ChatService.saveStreamState('conv-a', 10);
62
+ ChatService.clearStreamState('conv-a');
63
+ expect(ChatService.getStreamState('conv-a')).toBeNull();
64
+ });
65
+
66
+ it('ignores empty conversation id on save', () => {
67
+ ChatService.saveStreamState('', 1);
68
+ expect(ChatService.getStreamState('')).toBeNull();
69
+ });
70
+
71
+ it('returns null on corrupted storage payload', () => {
72
+ localStorage.setItem(`${STREAM_RESUME_LOCALSTORAGE_KEY_PREFIX}conv-a`, '{not-json');
73
+ expect(ChatService.getStreamState('conv-a')).toBeNull();
74
+ });
75
+
76
+ it('persists the model alongside the byte count', () => {
77
+ ChatService.saveStreamState('conv-a', 10, 'model-x');
78
+ expect(ChatService.getStreamState('conv-a')!.model).toBe('model-x');
79
+ });
80
+
81
+ it('stores a null model when none is provided', () => {
82
+ ChatService.saveStreamState('conv-a', 10);
83
+ expect(ChatService.getStreamState('conv-a')!.model).toBeNull();
84
+ });
85
+
86
+ it('overwrites the model on a new save for the same conversation', () => {
87
+ ChatService.saveStreamState('conv-a', 10, 'model-x');
88
+ ChatService.saveStreamState('conv-a', 20, 'model-y');
89
+ expect(ChatService.getStreamState('conv-a')!.model).toBe('model-y');
90
+ });
91
+
92
+ describe('resumeStreamIdentity', () => {
93
+ it('appends the persisted model so the resume key matches the frozen POST identity', () => {
94
+ ChatService.saveStreamState('conv-a', 10, 'model-x');
95
+ expect(
96
+ ChatService.resumeStreamIdentity('conv-a', ChatService.getStreamState('conv-a'), 'dropdown')
97
+ ).toBe('conv-a::model-x');
98
+ });
99
+
100
+ it('keeps the bare conv id when the persisted model is null', () => {
101
+ ChatService.saveStreamState('conv-a', 10);
102
+ expect(
103
+ ChatService.resumeStreamIdentity('conv-a', ChatService.getStreamState('conv-a'), 'dropdown')
104
+ ).toBe('conv-a');
105
+ });
106
+
107
+ it('falls back to the current model only when no state is persisted', () => {
108
+ expect(ChatService.resumeStreamIdentity('conv-a', null, 'dropdown')).toBe('conv-a::dropdown');
109
+ });
110
+
111
+ it('ignores the fallback when a state exists, the persisted value is authoritative', () => {
112
+ ChatService.saveStreamState('conv-a', 10, 'model-x');
113
+ expect(
114
+ ChatService.resumeStreamIdentity('conv-a', ChatService.getStreamState('conv-a'), 'dropdown')
115
+ ).toBe('conv-a::model-x');
116
+ });
117
+
118
+ it('falls back when a legacy state has no model field', () => {
119
+ localStorage.setItem(
120
+ `${STREAM_RESUME_LOCALSTORAGE_KEY_PREFIX}conv-a`,
121
+ JSON.stringify({ bytesReceived: 10, updatedAt: 1 })
122
+ );
123
+ expect(
124
+ ChatService.resumeStreamIdentity('conv-a', ChatService.getStreamState('conv-a'), 'dropdown')
125
+ ).toBe('conv-a::dropdown');
126
+ });
127
+ });
128
+ });
backend/llama.cpp/tools/ui/tests/unit/uri-template.test.ts ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { describe, it, expect } from 'vitest';
2
+ import {
3
+ extractTemplateVariables,
4
+ expandTemplate,
5
+ isTemplateComplete,
6
+ normalizeResourceUri
7
+ } from '../../src/lib/utils/uri-template';
8
+ import { URI_TEMPLATE_OPERATORS } from '../../src/lib/constants/uri-template';
9
+
10
+ describe('extractTemplateVariables', () => {
11
+ it('extracts simple variables', () => {
12
+ const vars = extractTemplateVariables('file:///{path}');
13
+ expect(vars).toEqual([{ name: 'path', operator: '' }]);
14
+ });
15
+
16
+ it('extracts multiple variables', () => {
17
+ const vars = extractTemplateVariables('db://{schema}/{table}');
18
+ expect(vars).toEqual([
19
+ { name: 'schema', operator: '' },
20
+ { name: 'table', operator: '' }
21
+ ]);
22
+ });
23
+
24
+ it('extracts variables with operators', () => {
25
+ const vars = extractTemplateVariables('http://example.com{+path}');
26
+ expect(vars).toEqual([{ name: 'path', operator: URI_TEMPLATE_OPERATORS.RESERVED }]);
27
+ });
28
+
29
+ it('extracts comma-separated variable lists', () => {
30
+ const vars = extractTemplateVariables('{x,y,z}');
31
+ expect(vars).toEqual([
32
+ { name: 'x', operator: '' },
33
+ { name: 'y', operator: '' },
34
+ { name: 'z', operator: '' }
35
+ ]);
36
+ });
37
+
38
+ it('deduplicates variable names', () => {
39
+ const vars = extractTemplateVariables('{name}/{name}');
40
+ expect(vars).toEqual([{ name: 'name', operator: '' }]);
41
+ });
42
+
43
+ it('handles fragment expansion', () => {
44
+ const vars = extractTemplateVariables('http://example.com/page{#section}');
45
+ expect(vars).toEqual([{ name: 'section', operator: URI_TEMPLATE_OPERATORS.FRAGMENT }]);
46
+ });
47
+
48
+ it('handles path segment expansion', () => {
49
+ const vars = extractTemplateVariables('http://example.com{/path}');
50
+ expect(vars).toEqual([{ name: 'path', operator: URI_TEMPLATE_OPERATORS.PATH_SEGMENT }]);
51
+ });
52
+
53
+ it('returns empty array for template without variables', () => {
54
+ const vars = extractTemplateVariables('http://example.com/static');
55
+ expect(vars).toEqual([]);
56
+ });
57
+
58
+ it('strips explode modifier', () => {
59
+ const vars = extractTemplateVariables('{list*}');
60
+ expect(vars).toEqual([{ name: 'list', operator: '' }]);
61
+ });
62
+
63
+ it('strips prefix modifier', () => {
64
+ const vars = extractTemplateVariables('{value:5}');
65
+ expect(vars).toEqual([{ name: 'value', operator: '' }]);
66
+ });
67
+ });
68
+
69
+ describe('expandTemplate', () => {
70
+ it('expands simple variable', () => {
71
+ const result = expandTemplate('file:///{path}', { path: 'src/main.rs' });
72
+ expect(result).toBe('file:///src%2Fmain.rs');
73
+ });
74
+
75
+ it('expands reserved variable (no encoding)', () => {
76
+ const result = expandTemplate('file:///{+path}', { path: 'src/main.rs' });
77
+ expect(result).toBe('file:///src/main.rs');
78
+ });
79
+
80
+ it('expands multiple variables', () => {
81
+ const result = expandTemplate('db://{schema}/{table}', {
82
+ schema: 'public',
83
+ table: 'users'
84
+ });
85
+ expect(result).toBe('db://public/users');
86
+ });
87
+
88
+ it('leaves empty for missing variables', () => {
89
+ const result = expandTemplate('{missing}', {});
90
+ expect(result).toBe('');
91
+ });
92
+
93
+ it('expands fragment', () => {
94
+ const result = expandTemplate('http://example.com/page{#section}', {
95
+ section: 'intro'
96
+ });
97
+ expect(result).toBe('http://example.com/page#intro');
98
+ });
99
+
100
+ it('expands path segments', () => {
101
+ const result = expandTemplate('http://example.com{/path}', { path: 'docs' });
102
+ expect(result).toBe('http://example.com/docs');
103
+ });
104
+
105
+ it('expands query parameters', () => {
106
+ const result = expandTemplate('http://example.com{?q}', { q: 'search term' });
107
+ expect(result).toBe('http://example.com?q=search%20term');
108
+ });
109
+
110
+ it('expands multiple query parameters', () => {
111
+ const result = expandTemplate('http://example.com{?q,sort}', {
112
+ q: 'search term',
113
+ sort: 'descending'
114
+ });
115
+ expect(result).toBe('http://example.com?q=search%20term&sort=descending');
116
+ });
117
+
118
+ it('keeps static parts unchanged', () => {
119
+ const result = expandTemplate('http://example.com/static', {});
120
+ expect(result).toBe('http://example.com/static');
121
+ });
122
+ });
123
+
124
+ describe('isTemplateComplete', () => {
125
+ it('returns true when all variables are filled', () => {
126
+ expect(isTemplateComplete('file:///{path}', { path: 'test.txt' })).toBe(true);
127
+ });
128
+
129
+ it('returns false when a variable is missing', () => {
130
+ expect(isTemplateComplete('db://{schema}/{table}', { schema: 'public' })).toBe(false);
131
+ });
132
+
133
+ it('returns false when a variable is empty', () => {
134
+ expect(isTemplateComplete('file:///{path}', { path: '' })).toBe(false);
135
+ });
136
+
137
+ it('returns false when a variable is whitespace only', () => {
138
+ expect(isTemplateComplete('file:///{path}', { path: ' ' })).toBe(false);
139
+ });
140
+
141
+ it('returns true for template without variables', () => {
142
+ expect(isTemplateComplete('http://example.com/static', {})).toBe(true);
143
+ });
144
+
145
+ it('returns true when all multiple variables are filled', () => {
146
+ expect(isTemplateComplete('db://{schema}/{table}', { schema: 'public', table: 'users' })).toBe(
147
+ true
148
+ );
149
+ });
150
+ });
151
+
152
+ describe('normalizeResourceUri', () => {
153
+ it('passes through a normal URI unchanged', () => {
154
+ expect(normalizeResourceUri('svelte://svelte/$effect.md')).toBe('svelte://svelte/$effect.md');
155
+ });
156
+
157
+ it('normalizes triple-slash URIs from path-style template expansion', () => {
158
+ expect(normalizeResourceUri('svelte:///svelte/$effect.md')).toBe('svelte://svelte/$effect.md');
159
+ });
160
+
161
+ it('normalizes quadruple-slash URIs', () => {
162
+ expect(normalizeResourceUri('svelte:////svelte/$effect.md')).toBe('svelte://svelte/$effect.md');
163
+ });
164
+
165
+ it('handles file:// URIs', () => {
166
+ expect(normalizeResourceUri('file:///home/user/doc.txt')).toBe('file://home/user/doc.txt');
167
+ });
168
+
169
+ it('handles http URIs unchanged', () => {
170
+ expect(normalizeResourceUri('http://example.com/path')).toBe('http://example.com/path');
171
+ });
172
+
173
+ it('returns non-URI strings unchanged', () => {
174
+ expect(normalizeResourceUri('not-a-uri')).toBe('not-a-uri');
175
+ });
176
+ });
backend/llama.cpp/tools/ui/tsconfig.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "extends": "./.svelte-kit/tsconfig.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "checkJs": true,
6
+ "esModuleInterop": true,
7
+ "forceConsistentCasingInFileNames": true,
8
+ "resolveJsonModule": true,
9
+ "skipLibCheck": true,
10
+ "sourceMap": true,
11
+ "strict": true,
12
+ "moduleResolution": "bundler"
13
+ },
14
+ "include": [
15
+ ".svelte-kit/ambient.d.ts",
16
+ ".svelte-kit/non-ambient.d.ts",
17
+ ".svelte-kit/types/**/$types.d.ts",
18
+ "vite.config.js",
19
+ "vite.config.ts",
20
+ "src/**/*.js",
21
+ "src/**/*.ts",
22
+ "src/**/*.svelte",
23
+ "tests/**/*.ts",
24
+ "tests/**/*.svelte",
25
+ ".storybook/**/*.ts",
26
+ ".storybook/**/*.svelte"
27
+ ],
28
+ "exclude": ["src/lib/services/sandbox-worker.js"]
29
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
30
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
31
+ //
32
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
33
+ // from the referenced tsconfig.json - TypeScript does not merge them in
34
+ }
backend/llama.cpp/tools/ui/vite.config.ts ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tailwindcss from '@tailwindcss/vite';
2
+ import { sveltekit } from '@sveltejs/kit/vite';
3
+ import { SvelteKitPWA } from '@vite-pwa/sveltekit';
4
+ import { dirname, resolve } from 'path';
5
+ import { fileURLToPath } from 'url';
6
+
7
+ import { defineConfig, searchForWorkspaceRoot } from 'vite';
8
+ import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
9
+ import { splashScreenPlugin } from './scripts/vite-plugin-splash-screen';
10
+ import { buildInfoPlugin } from './scripts/vite-plugin-build-info';
11
+ import { relativizeBasePlugin } from './scripts/vite-plugin-relativize-base';
12
+ import { playwright } from '@vitest/browser-playwright';
13
+ import { SVELTEKIT_PWA_OPTIONS } from './src/lib/constants/pwa';
14
+
15
+ const __dirname = dirname(fileURLToPath(import.meta.url));
16
+
17
+ const SERVER_ORIGIN = import.meta.env?.VITE_PUBLIC_SERVER_ORIGIN || 'http://localhost:8080';
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ const browserBaseConfig: any = {
21
+ enabled: true,
22
+ provider: playwright({
23
+ launchOptions: {
24
+ args: ['--no-sandbox']
25
+ }
26
+ }),
27
+ instances: [{ browser: 'chromium' }]
28
+ };
29
+
30
+ export default defineConfig({
31
+ resolve: {
32
+ alias: {
33
+ 'katex-fonts': resolve('node_modules/katex/dist/fonts')
34
+ }
35
+ },
36
+
37
+ build: {
38
+ assetsInlineLimit: 32000,
39
+ chunkSizeWarningLimit: 3072,
40
+ minify: true
41
+ },
42
+
43
+ plugins: [
44
+ tailwindcss(),
45
+ sveltekit(),
46
+ SvelteKitPWA(SVELTEKIT_PWA_OPTIONS),
47
+ splashScreenPlugin(),
48
+ buildInfoPlugin(),
49
+ relativizeBasePlugin()
50
+ ],
51
+
52
+ test: {
53
+ projects: [
54
+ {
55
+ extends: './vite.config.ts',
56
+ test: {
57
+ name: 'client',
58
+ browser: browserBaseConfig,
59
+ include: ['tests/client/**/*.svelte.{test,spec}.{js,ts}'],
60
+ setupFiles: ['./vitest-setup-client.ts']
61
+ }
62
+ },
63
+
64
+ {
65
+ extends: './vite.config.ts',
66
+ test: {
67
+ name: 'unit',
68
+ environment: 'node',
69
+ include: ['tests/unit/**/*.{test,spec}.{js,ts}']
70
+ }
71
+ },
72
+
73
+ {
74
+ extends: './vite.config.ts',
75
+ test: {
76
+ name: 'ui',
77
+ browser: { ...browserBaseConfig, instances: [{ browser: 'chromium', headless: true }] },
78
+ setupFiles: ['./.storybook/vitest.setup.ts']
79
+ },
80
+ plugins: [
81
+ storybookTest({
82
+ storybookScript: 'pnpm run storybook --no-open'
83
+ })
84
+ ]
85
+ }
86
+ ]
87
+ },
88
+
89
+ server: {
90
+ proxy: {
91
+ '/v1': SERVER_ORIGIN,
92
+ '/props': SERVER_ORIGIN,
93
+ '/models': SERVER_ORIGIN,
94
+ '/tools': SERVER_ORIGIN,
95
+ '/slots': SERVER_ORIGIN,
96
+ '/cors-proxy': SERVER_ORIGIN
97
+ },
98
+ headers: {
99
+ 'Cross-Origin-Embedder-Policy': 'require-corp',
100
+ 'Cross-Origin-Opener-Policy': 'same-origin'
101
+ },
102
+ fs: {
103
+ allow: [searchForWorkspaceRoot(process.cwd()), resolve(__dirname, 'tests')]
104
+ }
105
+ }
106
+ });
backend/llama.cpp/tools/ui/vitest-setup-client.ts ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /// <reference types="@vitest/browser/matchers" />
2
+ /// <reference types="@vitest/browser/providers/playwright" />
3
+
4
+ import { beforeEach, vi } from 'vitest';
5
+
6
+ // Mock fetch for API calls during client tests.
7
+ // In test environment there is no backend server, so we intercept
8
+ // the specific endpoints the app uses and return valid mock data.
9
+ beforeEach(() => {
10
+ const originalFetch = globalThis.fetch;
11
+
12
+ vi.spyOn(globalThis, 'fetch').mockImplementation(
13
+ async (input: RequestInfo | URL, init?: RequestInit) => {
14
+ const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
15
+
16
+ // Mock server props endpoint
17
+ if (url.includes('/server')) {
18
+ return new Response(
19
+ JSON.stringify({
20
+ mode: 'router',
21
+ version: 'test',
22
+ git_commit: 'test',
23
+ git_branch: 'test'
24
+ }),
25
+ { status: 200, headers: { 'Content-Type': 'application/json' } }
26
+ );
27
+ }
28
+
29
+ // Mock models list endpoint
30
+ if (/\/v1\/models|\/models\b/.test(url)) {
31
+ return new Response(
32
+ JSON.stringify({
33
+ object: 'list',
34
+ data: [
35
+ {
36
+ id: 'test-model.gguf',
37
+ object: 'model',
38
+ owned_by: 'llamacpp',
39
+ created: 0,
40
+ in_cache: false,
41
+ path: 'models/test-model.gguf',
42
+ status: { value: 'unloaded' },
43
+ meta: {}
44
+ }
45
+ ],
46
+ models: [
47
+ {
48
+ model: 'test-model.gguf',
49
+ name: 'Test Model',
50
+ details: {}
51
+ }
52
+ ]
53
+ }),
54
+ { status: 200, headers: { 'Content-Type': 'application/json' } }
55
+ );
56
+ }
57
+
58
+ // Mock /props endpoint (used for modalities)
59
+ if (url.includes('/props')) {
60
+ return new Response(
61
+ JSON.stringify({
62
+ default_generation_settings: { n_ctx: 2048 }
63
+ }),
64
+ { status: 200, headers: { 'Content-Type': 'application/json' } }
65
+ );
66
+ }
67
+
68
+ // Mock /tools endpoint (used for built-in tools list)
69
+ if (url.includes('/tools')) {
70
+ return new Response(JSON.stringify([]), {
71
+ status: 200,
72
+ headers: { 'Content-Type': 'application/json' }
73
+ });
74
+ }
75
+
76
+ // Default: use real fetch
77
+ return originalFetch(input, init);
78
+ }
79
+ );
80
+ });
backend/llama.cpp/tools/ui/vitest.shims.d.ts ADDED
@@ -0,0 +1 @@
 
 
1
+ /// <reference types="@vitest/browser-playwright" />
backend/llama.cpp/ty.toml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [environment]
2
+ extra-paths = ["./gguf-py", "./examples/model-conversion/scripts", "./tools/server/tests", "./scripts/snapdragon/qdc/tests"]
3
+ python-version = "3.10"
4
+
5
+ [rules]
6
+ deprecated = "warn"
7
+
8
+ [src]
9
+ exclude = [
10
+ "./tools/mtmd/legacy-models/**",
11
+ ]
12
+
13
+ [[overrides]]
14
+ include = [
15
+ "./tools/server/tests/**",
16
+ "./scripts/snapdragon/qdc/**",
17
+ "./tools/mtmd/tests/**",
18
+ ]
19
+
20
+ [overrides.rules]
21
+ unresolved-reference = "ignore"
22
+ unresolved-import = "ignore"
23
+ unresolved-attribute = "ignore"
24
+
25
+ [[overrides]]
26
+ include = [
27
+ "./examples/pydantic_models_to_grammar.py",
28
+ ]
29
+
30
+ [overrides.rules]
31
+ unsupported-operator = "ignore"
32
+ not-subscriptable = "ignore"
backend/llama.cpp/vendor/cpp-httplib/CMakeLists.txt ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ set(TARGET cpp-httplib)
2
+ license_add_file("cpp-httplib" "LICENSE")
3
+
4
+ find_package(Threads REQUIRED)
5
+
6
+ llama_add_compile_flags()
7
+
8
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
9
+
10
+ add_library(${TARGET} STATIC httplib.cpp httplib.h)
11
+
12
+ # disable warnings in 3rd party code
13
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
14
+ target_compile_options(${TARGET} PRIVATE /w)
15
+ else()
16
+ target_compile_options(${TARGET} PRIVATE -w)
17
+ endif()
18
+
19
+ target_link_libraries(${TARGET} PRIVATE Threads::Threads)
20
+
21
+ if (WIN32 AND NOT MSVC)
22
+ target_link_libraries(${TARGET} PUBLIC ws2_32)
23
+ endif()
24
+
25
+ target_compile_features(${TARGET} PRIVATE cxx_std_17)
26
+
27
+ target_compile_definitions(${TARGET} PRIVATE
28
+ # increase max payload length to allow use of larger context size
29
+ CPPHTTPLIB_FORM_URL_ENCODED_PAYLOAD_MAX_LENGTH=1048576
30
+ # increase backlog size to avoid connection resets for >> 1 slots
31
+ CPPHTTPLIB_LISTEN_BACKLOG=512
32
+ # increase max URI length to handle longer prompts in query string
33
+ CPPHTTPLIB_REQUEST_URI_MAX_LENGTH=32768
34
+ # disable Nagle's algorithm
35
+ CPPHTTPLIB_TCP_NODELAY=1
36
+ )
37
+
38
+ set(OPENSSL_NO_ASM ON CACHE BOOL "Disable OpenSSL ASM code when building BoringSSL or LibreSSL")
39
+
40
+ if (LLAMA_BUILD_BORINGSSL)
41
+ set(FIPS OFF CACHE BOOL "Enable FIPS (BoringSSL)")
42
+
43
+ set(BORINGSSL_GIT "https://boringssl.googlesource.com/boringssl" CACHE STRING "BoringSSL git repository")
44
+ set(BORINGSSL_VERSION "0.20260616.0" CACHE STRING "BoringSSL version")
45
+
46
+ message(STATUS "Fetching BoringSSL version ${BORINGSSL_VERSION}")
47
+
48
+ set(BORINGSSL_ARGS
49
+ GIT_REPOSITORY ${BORINGSSL_GIT}
50
+ GIT_TAG ${BORINGSSL_VERSION}
51
+ )
52
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
53
+ list(APPEND BORINGSSL_ARGS EXCLUDE_FROM_ALL)
54
+ endif()
55
+
56
+ include(FetchContent)
57
+ FetchContent_Declare(boringssl ${BORINGSSL_ARGS})
58
+
59
+ set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
60
+ set(SAVED_BUILD_TESTING ${BUILD_TESTING})
61
+
62
+ set(BUILD_SHARED_LIBS OFF)
63
+ set(BUILD_TESTING OFF)
64
+
65
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
66
+ FetchContent_MakeAvailable(boringssl)
67
+ else()
68
+ FetchContent_GetProperties(boringssl)
69
+ if(NOT boringssl_POPULATED)
70
+ FetchContent_Populate(boringssl)
71
+ add_subdirectory(${boringssl_SOURCE_DIR} ${boringssl_BINARY_DIR} EXCLUDE_FROM_ALL)
72
+ endif()
73
+ endif()
74
+
75
+ set(BUILD_SHARED_LIBS ${SAVED_BUILD_SHARED_LIBS})
76
+ set(BUILD_TESTING ${SAVED_BUILD_TESTING})
77
+
78
+ license_add_file("BoringSSL" "${boringssl_SOURCE_DIR}/LICENSE")
79
+
80
+ set(CPPHTTPLIB_OPENSSL_SUPPORT TRUE)
81
+ target_link_libraries(${TARGET} PUBLIC ssl crypto)
82
+
83
+ elseif (LLAMA_BUILD_LIBRESSL)
84
+ set(LIBRESSL_VERSION "4.3.2" CACHE STRING "LibreSSL version")
85
+
86
+ message(STATUS "Fetching LibreSSL version ${LIBRESSL_VERSION}")
87
+
88
+ set(LIBRESSL_ARGS
89
+ URL "https://cdn.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz"
90
+ )
91
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.24)
92
+ list(APPEND LIBRESSL_ARGS DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
93
+ endif()
94
+
95
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
96
+ list(APPEND LIBRESSL_ARGS EXCLUDE_FROM_ALL)
97
+ endif()
98
+
99
+ include(FetchContent)
100
+ FetchContent_Declare(libressl ${LIBRESSL_ARGS})
101
+
102
+ set(SAVED_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS})
103
+ set(SAVED_BUILD_TESTING ${BUILD_TESTING})
104
+
105
+ set(BUILD_SHARED_LIBS OFF)
106
+ set(BUILD_TESTING OFF)
107
+
108
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
109
+ FetchContent_MakeAvailable(libressl)
110
+ else()
111
+ FetchContent_GetProperties(libressl)
112
+ if(NOT libressl_POPULATED)
113
+ FetchContent_Populate(libressl)
114
+ add_subdirectory(${libressl_SOURCE_DIR} ${libressl_BINARY_DIR} EXCLUDE_FROM_ALL)
115
+ endif()
116
+ endif()
117
+
118
+ set(BUILD_SHARED_LIBS ${SAVED_BUILD_SHARED_LIBS})
119
+ set(BUILD_TESTING ${SAVED_BUILD_TESTING})
120
+
121
+ license_add_file("LibreSSL" "${libressl_SOURCE_DIR}/COPYING")
122
+
123
+ set(CPPHTTPLIB_OPENSSL_SUPPORT TRUE)
124
+ target_link_libraries(${TARGET} PUBLIC ssl crypto)
125
+
126
+ elseif (LLAMA_OPENSSL)
127
+ find_package(OpenSSL)
128
+ if (OpenSSL_FOUND)
129
+ include(CheckCSourceCompiles)
130
+ set(SAVED_CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES})
131
+ set(CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIR})
132
+ check_c_source_compiles("
133
+ #include <openssl/opensslv.h>
134
+ #if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
135
+ # if OPENSSL_VERSION_NUMBER < 0x1010107f
136
+ # error bad version
137
+ # endif
138
+ #else
139
+ # if OPENSSL_VERSION_NUMBER < 0x30000000L
140
+ # error bad version
141
+ # endif
142
+ #endif
143
+ int main() { return 0; }
144
+ " OPENSSL_VERSION_SUPPORTED)
145
+ set(CMAKE_REQUIRED_INCLUDES ${SAVED_CMAKE_REQUIRED_INCLUDES})
146
+ if (OPENSSL_VERSION_SUPPORTED)
147
+ message(STATUS "OpenSSL found: ${OPENSSL_VERSION}")
148
+ set(CPPHTTPLIB_OPENSSL_SUPPORT TRUE)
149
+ target_link_libraries(${TARGET} PUBLIC OpenSSL::SSL OpenSSL::Crypto)
150
+ endif()
151
+ else()
152
+ message(WARNING "OpenSSL not found, HTTPS support disabled")
153
+ endif()
154
+ endif()
155
+
156
+ # disable warnings in 3rd party code
157
+ if(LLAMA_BUILD_BORINGSSL OR LLAMA_BUILD_LIBRESSL)
158
+ if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
159
+ target_compile_options(ssl PRIVATE /w)
160
+ target_compile_options(crypto PRIVATE /w)
161
+ if(LLAMA_BUILD_BORINGSSL)
162
+ target_compile_options(fipsmodule PRIVATE /w)
163
+ endif()
164
+ if(LLAMA_BUILD_LIBRESSL)
165
+ target_compile_options(ssl_obj PRIVATE /w)
166
+ target_compile_options(bs_obj PRIVATE /w)
167
+ target_compile_options(compat_obj PRIVATE /w)
168
+ target_compile_options(crypto_obj PRIVATE /w)
169
+ endif()
170
+ else()
171
+ target_compile_options(ssl PRIVATE -w)
172
+ target_compile_options(crypto PRIVATE -w)
173
+ if(LLAMA_BUILD_BORINGSSL)
174
+ target_compile_options(fipsmodule PRIVATE -w)
175
+ endif()
176
+ if(LLAMA_BUILD_LIBRESSL)
177
+ target_compile_options(ssl_obj PRIVATE -w)
178
+ target_compile_options(bs_obj PRIVATE -w)
179
+ target_compile_options(compat_obj PRIVATE -w)
180
+ target_compile_options(crypto_obj PRIVATE -w)
181
+ endif()
182
+ endif()
183
+ endif()
184
+
185
+ if (CPPHTTPLIB_OPENSSL_SUPPORT)
186
+ target_compile_definitions(${TARGET} PUBLIC CPPHTTPLIB_OPENSSL_SUPPORT) # used in server.cpp
187
+ if (APPLE AND CMAKE_SYSTEM_NAME STREQUAL "Darwin")
188
+ find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation REQUIRED)
189
+ find_library(SECURITY_FRAMEWORK Security REQUIRED)
190
+ target_link_libraries(${TARGET} PUBLIC ${CORE_FOUNDATION_FRAMEWORK} ${SECURITY_FRAMEWORK})
191
+ endif()
192
+ if (WIN32 AND NOT MSVC)
193
+ target_link_libraries(${TARGET} PUBLIC crypt32)
194
+ endif()
195
+ endif()
backend/llama.cpp/vendor/cpp-httplib/LICENSE ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 yhirose
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
backend/llama.cpp/vendor/cpp-httplib/httplib.cpp ADDED
The diff for this file is too large to render. See raw diff
 
backend/llama.cpp/vendor/cpp-httplib/httplib.h ADDED
The diff for this file is too large to render. See raw diff
 
backend/llama.cpp/vendor/miniaudio/miniaudio.h ADDED
The diff for this file is too large to render. See raw diff
 
backend/llama.cpp/vendor/nlohmann/json.hpp ADDED
The diff for this file is too large to render. See raw diff
 
backend/llama.cpp/vendor/nlohmann/json_fwd.hpp ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // __ _____ _____ _____
2
+ // __| | __| | | | JSON for Modern C++
3
+ // | | |__ | | | | | | version 3.12.0
4
+ // |_____|_____|_____|_|___| https://github.com/nlohmann/json
5
+ //
6
+ // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
7
+ // SPDX-License-Identifier: MIT
8
+
9
+ #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_
10
+ #define INCLUDE_NLOHMANN_JSON_FWD_HPP_
11
+
12
+ #include <cstdint> // int64_t, uint64_t
13
+ #include <map> // map
14
+ #include <memory> // allocator
15
+ #include <string> // string
16
+ #include <vector> // vector
17
+
18
+ // #include <nlohmann/detail/abi_macros.hpp>
19
+ // __ _____ _____ _____
20
+ // __| | __| | | | JSON for Modern C++
21
+ // | | |__ | | | | | | version 3.12.0
22
+ // |_____|_____|_____|_|___| https://github.com/nlohmann/json
23
+ //
24
+ // SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann <https://nlohmann.me>
25
+ // SPDX-License-Identifier: MIT
26
+
27
+
28
+
29
+ // This file contains all macro definitions affecting or depending on the ABI
30
+
31
+ #ifndef JSON_SKIP_LIBRARY_VERSION_CHECK
32
+ #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH)
33
+ #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0
34
+ #warning "Already included a different version of the library!"
35
+ #endif
36
+ #endif
37
+ #endif
38
+
39
+ #define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum)
40
+ #define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum)
41
+ #define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum)
42
+
43
+ #ifndef JSON_DIAGNOSTICS
44
+ #define JSON_DIAGNOSTICS 0
45
+ #endif
46
+
47
+ #ifndef JSON_DIAGNOSTIC_POSITIONS
48
+ #define JSON_DIAGNOSTIC_POSITIONS 0
49
+ #endif
50
+
51
+ #ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
52
+ #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0
53
+ #endif
54
+
55
+ #if JSON_DIAGNOSTICS
56
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag
57
+ #else
58
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS
59
+ #endif
60
+
61
+ #if JSON_DIAGNOSTIC_POSITIONS
62
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp
63
+ #else
64
+ #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS
65
+ #endif
66
+
67
+ #if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON
68
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp
69
+ #else
70
+ #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON
71
+ #endif
72
+
73
+ #ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION
74
+ #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0
75
+ #endif
76
+
77
+ // Construct the namespace ABI tags component
78
+ #define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c
79
+ #define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \
80
+ NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c)
81
+
82
+ #define NLOHMANN_JSON_ABI_TAGS \
83
+ NLOHMANN_JSON_ABI_TAGS_CONCAT( \
84
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \
85
+ NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \
86
+ NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS)
87
+
88
+ // Construct the namespace version component
89
+ #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \
90
+ _v ## major ## _ ## minor ## _ ## patch
91
+ #define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \
92
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch)
93
+
94
+ #if NLOHMANN_JSON_NAMESPACE_NO_VERSION
95
+ #define NLOHMANN_JSON_NAMESPACE_VERSION
96
+ #else
97
+ #define NLOHMANN_JSON_NAMESPACE_VERSION \
98
+ NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \
99
+ NLOHMANN_JSON_VERSION_MINOR, \
100
+ NLOHMANN_JSON_VERSION_PATCH)
101
+ #endif
102
+
103
+ // Combine namespace components
104
+ #define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b
105
+ #define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \
106
+ NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b)
107
+
108
+ #ifndef NLOHMANN_JSON_NAMESPACE
109
+ #define NLOHMANN_JSON_NAMESPACE \
110
+ nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \
111
+ NLOHMANN_JSON_ABI_TAGS, \
112
+ NLOHMANN_JSON_NAMESPACE_VERSION)
113
+ #endif
114
+
115
+ #ifndef NLOHMANN_JSON_NAMESPACE_BEGIN
116
+ #define NLOHMANN_JSON_NAMESPACE_BEGIN \
117
+ namespace nlohmann \
118
+ { \
119
+ inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \
120
+ NLOHMANN_JSON_ABI_TAGS, \
121
+ NLOHMANN_JSON_NAMESPACE_VERSION) \
122
+ {
123
+ #endif
124
+
125
+ #ifndef NLOHMANN_JSON_NAMESPACE_END
126
+ #define NLOHMANN_JSON_NAMESPACE_END \
127
+ } /* namespace (inline namespace) NOLINT(readability/namespace) */ \
128
+ } // namespace nlohmann
129
+ #endif
130
+
131
+
132
+ /*!
133
+ @brief namespace for Niels Lohmann
134
+ @see https://github.com/nlohmann
135
+ @since version 1.0.0
136
+ */
137
+ NLOHMANN_JSON_NAMESPACE_BEGIN
138
+
139
+ /*!
140
+ @brief default JSONSerializer template argument
141
+
142
+ This serializer ignores the template arguments and uses ADL
143
+ ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl))
144
+ for serialization.
145
+ */
146
+ template<typename T = void, typename SFINAE = void>
147
+ struct adl_serializer;
148
+
149
+ /// a class to store JSON values
150
+ /// @sa https://json.nlohmann.me/api/basic_json/
151
+ template<template<typename U, typename V, typename... Args> class ObjectType =
152
+ std::map,
153
+ template<typename U, typename... Args> class ArrayType = std::vector,
154
+ class StringType = std::string, class BooleanType = bool,
155
+ class NumberIntegerType = std::int64_t,
156
+ class NumberUnsignedType = std::uint64_t,
157
+ class NumberFloatType = double,
158
+ template<typename U> class AllocatorType = std::allocator,
159
+ template<typename T, typename SFINAE = void> class JSONSerializer =
160
+ adl_serializer,
161
+ class BinaryType = std::vector<std::uint8_t>, // cppcheck-suppress syntaxError
162
+ class CustomBaseClass = void>
163
+ class basic_json;
164
+
165
+ /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document
166
+ /// @sa https://json.nlohmann.me/api/json_pointer/
167
+ template<typename RefStringType>
168
+ class json_pointer;
169
+
170
+ /*!
171
+ @brief default specialization
172
+ @sa https://json.nlohmann.me/api/json/
173
+ */
174
+ using json = basic_json<>;
175
+
176
+ /// @brief a minimal map-like container that preserves insertion order
177
+ /// @sa https://json.nlohmann.me/api/ordered_map/
178
+ template<class Key, class T, class IgnoredLess, class Allocator>
179
+ struct ordered_map;
180
+
181
+ /// @brief specialization that maintains the insertion order of object keys
182
+ /// @sa https://json.nlohmann.me/api/ordered_json/
183
+ using ordered_json = basic_json<nlohmann::ordered_map>;
184
+
185
+ NLOHMANN_JSON_NAMESPACE_END
186
+
187
+ #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_