Store context documents to Vector Database

Author Mitja Felicijan <mitja.felicijan@gmail.com> 2026-02-13 18:07:45 +0100
Committer Mitja Felicijan <mitja.felicijan@gmail.com> 2026-02-13 18:07:45 +0100
Commit 2a6fd554998c64733e2d97aecf653f6e48e0f8b4 (patch)
-rw-r--r-- .gitignore 4
-rw-r--r-- Makefile 13
-rw-r--r-- context.c 174
-rw-r--r-- minunit.h 391
-rw-r--r-- models.h 110
-rw-r--r-- nonstd.h 835
-rw-r--r-- prompt.c 11
-rw-r--r-- vectordb.c 118
-rw-r--r-- vectordb.h 3
9 files changed, 1581 insertions, 78 deletions
diff --git a/.gitignore b/.gitignore
1
# Build artefacts
1
# Build artefacts
  
2
models/
2
prompt
3
prompt
3
models/
4
context
4
  
5
  
5
# Other files
6
# Other files
6
.DS_Store
7
.DS_Store
7
*.log
8
*.log
  
9
*.vdb
diff --git a/Makefile b/Makefile
...
12
  
12
  
13
help: .help
13
help: .help
14
  
14
  
15
prompt: prompt.c vectordb.c models.h # Build prompt binary for testing
15
build/prompt: prompt.c vectordb.c models.h # Build prompt binary for testing
16
	$(CC) $(CFLAGS) prompt.c vectordb.c -o prompt $(LDFLAGS)
16
	$(CC) $(CFLAGS) prompt.c vectordb.c -o prompt $(LDFLAGS)
17
  
17
  
18
llamacpp: .assure # Build llama.cpp libraries
18
build/context: context.c vectordb.c models.h # Build context binary for testing
  
19
	$(CC) $(CFLAGS) context.c vectordb.c -o context $(LDFLAGS)
  
20
  
  
21
build/llama.cpp: .assure # Build llama.cpp libraries
19
	mkdir $(LLAMA_DIR)/build && \
22
	mkdir $(LLAMA_DIR)/build && \
20
		cd $(LLAMA_DIR)/build && \
23
		cd $(LLAMA_DIR)/build && \
21
		cmake ../ -DBUILD_SHARED_LIBS=OFF && \
24
		cmake ../ -DBUILD_SHARED_LIBS=OFF && \
22
		make -j8
25
		make -j8
23
  
26
  
24
fetchmodels: .assure # Fetch GGUF models
27
run/fetch-models: .assure # Fetch GGUF models
25
	-mkdir -p models
28
	-mkdir -p models
26
	cd models && wget -nc -i ../models.txt
29
	cd models && wget -nc -i ../models.txt
27
  
30
  
28
docker: .assure # Runs prompt in Docker container
31
run/docker: .assure # Runs prompt in Docker container
29
	docker build -t promptd .
32
	docker build -t promptd .
30
	docker run -it promptd
33
	docker run -it promptd
31
  
34
  
32
clean: # Cleans up all the build artefacts
35
run/clean: # Cleans up all the build artefacts
33
	-rm -f prompt
36
	-rm -f prompt
34
	cd $(LLAMA_DIR)/build && make clean
37
	cd $(LLAMA_DIR)/build && make clean
35
	-rm -Rf $(LLAMA_DIR)/build
38
	-rm -Rf $(LLAMA_DIR)/build
diff --git a/context.c b/context.c
  
1
#include "llama.h"
  
2
#include "vectordb.h"
  
3
#include "models.h"
  
4
  
  
5
#define NONSTD_IMPLEMENTATION
  
6
#include "nonstd.h"
  
7
  
  
8
#include <stdio.h>
  
9
#include <stdlib.h>
  
10
#include <string.h>
  
11
#include <getopt.h>
  
12
  
  
13
#define MAX_TOKENS 512
  
14
#define MAX_TOKEN_LEN 32
  
15
  
  
16
typedef struct {
  
17
  
  
18
} Engine;
  
19
  
  
20
static void llama_log_callback(enum ggml_log_level level, const char *text, void *user_data) {
  
21
	(void)level;
  
22
	(void)user_data;
  
23
	(void)text;
  
24
}
  
25
  
  
26
void list_available_models() {
  
27
	printf("Model list:\n");
  
28
	ModelConfig model;
  
29
	static_foreach(ModelConfig, model, models) {
  
30
		printf(" - %s [ctx: %d, temp: %f]\n", model.name, model.n_ctx, model.temperature);
  
31
	}
  
32
}
  
33
  
  
34
static void show_help(const char *prog) {
  
35
	printf("Usage: %s [OPTIONS]\n", prog);
  
36
	printf("Options:\n");
  
37
	printf("  -m, --model <name>    Specify model to use (default: first model)\n");
  
38
	printf("  -i, --in <file>       Specify input context file\n");
  
39
	printf("  -o, --out <file>      Specify output vector database file\n");
  
40
	printf("  -l, --list            Lists all available models\n");
  
41
	printf("  -v, --verbose         Enable verbose logging\n");
  
42
	printf("  -h, --help            Show this help message\n");
  
43
}
  
44
  
  
45
int main(int argc, char **argv) {
  
46
	set_log_level(LOG_DEBUG);
  
47
  
  
48
	const char *model_name = NULL;
  
49
	const char *in_file = NULL;
  
50
	const char *out_file = NULL;
  
51
	int list_models = 0;
  
52
	int verbose = 0;
  
53
  
  
54
	static struct option long_options[] = {
  
55
		{"model", required_argument, 0, 'm'},
  
56
		{"in", required_argument, 0, 'i'},
  
57
		{"out", required_argument, 0, 'o'},
  
58
		{"list", no_argument, 0, 'l'},
  
59
		{"verbose", no_argument, 0, 'v'},
  
60
		{"help", no_argument, 0, 'h'},
  
61
		{0, 0, 0, 0}
  
62
	};
  
63
  
  
64
	int opt;
  
65
	int option_index = 0;
  
66
	while ((opt = getopt_long(argc, argv, "m:i:o:lvh", long_options, &option_index)) != -1) {
  
67
		switch (opt) {
  
68
			case 'm':
  
69
				model_name = optarg;
  
70
				break;
  
71
			case 'i':
  
72
				in_file = optarg;
  
73
				break;
  
74
			case 'o':
  
75
				out_file = optarg;
  
76
				break;
  
77
			case 'l':
  
78
				list_models = 1;
  
79
				break;
  
80
			case 'v':
  
81
				verbose = 1;
  
82
				break;
  
83
			case 'h':
  
84
				show_help(argv[0]);
  
85
				return 0;
  
86
			default:
  
87
				fprintf(stderr, "Usage: %s [-m model] [-i file] [-o file] [-lvh]\n", argv[0]);
  
88
				return 1;
  
89
		}
  
90
	}
  
91
  
  
92
	if (verbose == 0) {
  
93
		llama_log_set(llama_log_callback, NULL);
  
94
	}
  
95
  
  
96
	if (list_models == 1) {
  
97
		list_available_models();
  
98
		return 0;
  
99
	}
  
100
  
  
101
	if (in_file == NULL) {
  
102
		log_message(stderr, LOG_ERROR, "Input context file must be provided. Exiting...");
  
103
		return 1;
  
104
	}
  
105
  
  
106
	if (out_file == NULL) {
  
107
		log_message(stderr, LOG_ERROR, "Output vector context file must be provided. Exiting...");
  
108
		return 1;
  
109
	}
  
110
  
  
111
	llama_backend_init();
  
112
  
  
113
	const ModelConfig *cfg = NULL;
  
114
	if (model_name != NULL) {
  
115
		cfg = get_model_by_name(model_name);
  
116
		if (cfg == NULL) {
  
117
			log_message(stderr, LOG_ERROR, "Unknown model '%s'", model_name);
  
118
			llama_backend_free();
  
119
			return 1;
  
120
		}
  
121
	} else {
  
122
		cfg = &models[0];
  
123
	}
  
124
  
  
125
	struct llama_model *model = llama_model_load_from_file(cfg->filepath, llama_model_default_params());
  
126
	if (model == NULL) {
  
127
		log_message(stderr, LOG_ERROR, "Unable to load embedding model");
  
128
		llama_backend_free();
  
129
		return 1;
  
130
	}
  
131
  
  
132
	struct llama_context_params cparams = llama_context_default_params();
  
133
	cparams.embeddings = true;
  
134
  
  
135
	struct llama_context *embed_ctx = llama_init_from_model(model, cparams);
  
136
	if (embed_ctx == NULL) {
  
137
		log_message(stderr, LOG_ERROR, "Failed to create embedding context");
  
138
		llama_model_free(model);
  
139
		llama_backend_free();
  
140
		return 1;
  
141
	}
  
142
  
  
143
	FILE *context_fp = fopen(in_file, "r");
  
144
	if (context_fp == NULL) {
  
145
		log_message(stderr, LOG_ERROR, "Unable to open context file %s", in_file);
  
146
		return 1;
  
147
	}
  
148
  
  
149
	VectorDB db;
  
150
	vdb_init(&db, embed_ctx);
  
151
  
  
152
	char line[1024];
  
153
	while (fgets(line, sizeof(line), context_fp) != NULL) {
  
154
		size_t len = strlen(line);
  
155
		while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r')) {
  
156
			line[len - 1] = '\0';
  
157
			len--;
  
158
		}
  
159
		if (len == 0) {
  
160
			continue;
  
161
		}
  
162
		vdb_add_document(&db, line);
  
163
	}
  
164
  
  
165
	if (vdb_save(&db, out_file) > 0) {
  
166
		log_message(stderr, LOG_ERROR, "Something went wrong saving file %s", out_file);
  
167
		fclose(context_fp);
  
168
		return 1;
  
169
	}
  
170
  
  
171
	log_message(stdout, LOG_INFO, "Context vector database file %s successfully written", out_file);
  
172
	fclose(context_fp);
  
173
	return 0;
  
174
}
diff --git a/minunit.h b/minunit.h
  
1
/*
  
2
 * Copyright (c) 2012 David Siñuela Pastor, siu.4coders@gmail.com
  
3
 * 
  
4
 * Permission is hereby granted, free of charge, to any person obtaining
  
5
 * a copy of this software and associated documentation files (the
  
6
 * "Software"), to deal in the Software without restriction, including
  
7
 * without limitation the rights to use, copy, modify, merge, publish,
  
8
 * distribute, sublicense, and/or sell copies of the Software, and to
  
9
 * permit persons to whom the Software is furnished to do so, subject to
  
10
 * the following conditions:
  
11
 * 
  
12
 * The above copyright notice and this permission notice shall be
  
13
 * included in all copies or substantial portions of the Software.
  
14
 * 
  
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  
22
 */
  
23
#ifndef MINUNIT_MINUNIT_H
  
24
#define MINUNIT_MINUNIT_H
  
25
  
  
26
#ifdef __cplusplus
  
27
	extern "C" {
  
28
#endif
  
29
  
  
30
#if defined(_WIN32)
  
31
#include <Windows.h>
  
32
#if defined(_MSC_VER) && _MSC_VER < 1900
  
33
  #define snprintf _snprintf
  
34
  #define __func__ __FUNCTION__
  
35
#endif
  
36
  
  
37
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
  
38
  
  
39
/* Change POSIX C SOURCE version for pure c99 compilers */
  
40
#if !defined(_POSIX_C_SOURCE) || _POSIX_C_SOURCE < 200112L
  
41
#undef _POSIX_C_SOURCE
  
42
#define _POSIX_C_SOURCE 200112L
  
43
#endif
  
44
  
  
45
#include <unistd.h>	/* POSIX flags */
  
46
#include <time.h>	/* clock_gettime(), time() */
  
47
#include <sys/time.h>	/* gethrtime(), gettimeofday() */
  
48
#include <sys/resource.h>
  
49
#include <sys/times.h>
  
50
#include <string.h>
  
51
  
  
52
#if defined(__MACH__) && defined(__APPLE__)
  
53
#include <mach/mach.h>
  
54
#include <mach/mach_time.h>
  
55
#endif
  
56
  
  
57
#if __GNUC__ >= 5 && !defined(__STDC_VERSION__)
  
58
#define __func__ __extension__ __FUNCTION__
  
59
#endif
  
60
  
  
61
#else
  
62
#error "Unable to define timers for an unknown OS."
  
63
#endif
  
64
  
  
65
#include <stdio.h>
  
66
#include <math.h>
  
67
  
  
68
/*  Maximum length of last message */
  
69
#define MINUNIT_MESSAGE_LEN 1024
  
70
/*  Accuracy with which floats are compared */
  
71
#define MINUNIT_EPSILON 1E-12
  
72
  
  
73
/*  Misc. counters */
  
74
static int minunit_run = 0;
  
75
static int minunit_assert = 0;
  
76
static int minunit_fail = 0;
  
77
static int minunit_status = 0;
  
78
  
  
79
/*  Timers */
  
80
static double minunit_real_timer = 0;
  
81
static double minunit_proc_timer = 0;
  
82
  
  
83
/*  Last message */
  
84
static char minunit_last_message[MINUNIT_MESSAGE_LEN];
  
85
  
  
86
/*  Test setup and teardown function pointers */
  
87
static void (*minunit_setup)(void) = NULL;
  
88
static void (*minunit_teardown)(void) = NULL;
  
89
  
  
90
/*  Definitions */
  
91
#define MU_TEST(method_name) static void method_name(void)
  
92
#define MU_TEST_SUITE(suite_name) static void suite_name(void)
  
93
  
  
94
#define MU__SAFE_BLOCK(block) do {\
  
95
	block\
  
96
} while(0)
  
97
  
  
98
/*  Run test suite and unset setup and teardown functions */
  
99
#define MU_RUN_SUITE(suite_name) MU__SAFE_BLOCK(\
  
100
	suite_name();\
  
101
	minunit_setup = NULL;\
  
102
	minunit_teardown = NULL;\
  
103
)
  
104
  
  
105
/*  Configure setup and teardown functions */
  
106
#define MU_SUITE_CONFIGURE(setup_fun, teardown_fun) MU__SAFE_BLOCK(\
  
107
	minunit_setup = setup_fun;\
  
108
	minunit_teardown = teardown_fun;\
  
109
)
  
110
  
  
111
/*  Test runner */
  
112
#define MU_RUN_TEST(test) MU__SAFE_BLOCK(\
  
113
	if (minunit_real_timer==0 && minunit_proc_timer==0) {\
  
114
		minunit_real_timer = mu_timer_real();\
  
115
		minunit_proc_timer = mu_timer_cpu();\
  
116
	}\
  
117
	if (minunit_setup) (*minunit_setup)();\
  
118
	minunit_status = 0;\
  
119
	test();\
  
120
	minunit_run++;\
  
121
	if (minunit_status) {\
  
122
		minunit_fail++;\
  
123
		printf("F");\
  
124
		printf("\n%s\n", minunit_last_message);\
  
125
	}\
  
126
	(void)fflush(stdout);\
  
127
	if (minunit_teardown) (*minunit_teardown)();\
  
128
)
  
129
  
  
130
/*  Report */
  
131
#define MU_REPORT() MU__SAFE_BLOCK(\
  
132
	double minunit_end_real_timer;\
  
133
	double minunit_end_proc_timer;\
  
134
	printf("\n\n%d tests, %d assertions, %d failures\n", minunit_run, minunit_assert, minunit_fail);\
  
135
	minunit_end_real_timer = mu_timer_real();\
  
136
	minunit_end_proc_timer = mu_timer_cpu();\
  
137
	printf("\nFinished in %.8f seconds (real) %.8f seconds (proc)\n\n",\
  
138
		minunit_end_real_timer - minunit_real_timer,\
  
139
		minunit_end_proc_timer - minunit_proc_timer);\
  
140
)
  
141
#define MU_EXIT_CODE minunit_fail
  
142
  
  
143
/*  Assertions */
  
144
#define mu_check(test) MU__SAFE_BLOCK(\
  
145
	minunit_assert++;\
  
146
	if (!(test)) {\
  
147
		(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, #test);\
  
148
		minunit_status = 1;\
  
149
		return;\
  
150
	} else {\
  
151
		printf(".");\
  
152
	}\
  
153
)
  
154
  
  
155
#define mu_fail(message) MU__SAFE_BLOCK(\
  
156
	minunit_assert++;\
  
157
	(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\
  
158
	minunit_status = 1;\
  
159
	return;\
  
160
)
  
161
  
  
162
#define mu_assert(test, message) MU__SAFE_BLOCK(\
  
163
	minunit_assert++;\
  
164
	if (!(test)) {\
  
165
		(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %s", __func__, __FILE__, __LINE__, message);\
  
166
		minunit_status = 1;\
  
167
		return;\
  
168
	} else {\
  
169
		printf(".");\
  
170
	}\
  
171
)
  
172
  
  
173
#define mu_assert_int_eq(expected, result) MU__SAFE_BLOCK(\
  
174
	int minunit_tmp_e;\
  
175
	int minunit_tmp_r;\
  
176
	minunit_assert++;\
  
177
	minunit_tmp_e = (expected);\
  
178
	minunit_tmp_r = (result);\
  
179
	if (minunit_tmp_e != minunit_tmp_r) {\
  
180
		(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %d expected but was %d", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\
  
181
		minunit_status = 1;\
  
182
		return;\
  
183
	} else {\
  
184
		printf(".");\
  
185
	}\
  
186
)
  
187
  
  
188
#define mu_assert_double_eq(expected, result) MU__SAFE_BLOCK(\
  
189
	double minunit_tmp_e;\
  
190
	double minunit_tmp_r;\
  
191
	minunit_assert++;\
  
192
	minunit_tmp_e = (expected);\
  
193
	minunit_tmp_r = (result);\
  
194
	if (fabs(minunit_tmp_e-minunit_tmp_r) > MINUNIT_EPSILON) {\
  
195
		int minunit_significant_figures = 1 - log10(MINUNIT_EPSILON);\
  
196
		(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: %.*g expected but was %.*g", __func__, __FILE__, __LINE__, minunit_significant_figures, minunit_tmp_e, minunit_significant_figures, minunit_tmp_r);\
  
197
		minunit_status = 1;\
  
198
		return;\
  
199
	} else {\
  
200
		printf(".");\
  
201
	}\
  
202
)
  
203
  
  
204
#define mu_assert_string_eq(expected, result) MU__SAFE_BLOCK(\
  
205
	const char* minunit_tmp_e = expected;\
  
206
	const char* minunit_tmp_r = result;\
  
207
	minunit_assert++;\
  
208
	if (!minunit_tmp_e) {\
  
209
		minunit_tmp_e = "<null pointer>";\
  
210
	}\
  
211
	if (!minunit_tmp_r) {\
  
212
		minunit_tmp_r = "<null pointer>";\
  
213
	}\
  
214
	if(strcmp(minunit_tmp_e, minunit_tmp_r) != 0) {\
  
215
		(void)snprintf(minunit_last_message, MINUNIT_MESSAGE_LEN, "%s failed:\n\t%s:%d: '%s' expected but was '%s'", __func__, __FILE__, __LINE__, minunit_tmp_e, minunit_tmp_r);\
  
216
		minunit_status = 1;\
  
217
		return;\
  
218
	} else {\
  
219
		printf(".");\
  
220
	}\
  
221
)
  
222
  
  
223
/*
  
224
 * The following two functions were written by David Robert Nadeau
  
225
 * from http://NadeauSoftware.com/ and distributed under the
  
226
 * Creative Commons Attribution 3.0 Unported License
  
227
 */
  
228
  
  
229
/**
  
230
 * Returns the real time, in seconds, or -1.0 if an error occurred.
  
231
 *
  
232
 * Time is measured since an arbitrary and OS-dependent start time.
  
233
 * The returned real time is only useful for computing an elapsed time
  
234
 * between two calls to this function.
  
235
 */
  
236
static double mu_timer_real(void)
  
237
{
  
238
#if defined(_WIN32)
  
239
	/* Windows 2000 and later. ---------------------------------- */
  
240
	LARGE_INTEGER Time;
  
241
	LARGE_INTEGER Frequency;
  
242
	
  
243
	QueryPerformanceFrequency(&Frequency);
  
244
	QueryPerformanceCounter(&Time);
  
245
	
  
246
	Time.QuadPart *= 1000000;
  
247
	Time.QuadPart /= Frequency.QuadPart;
  
248
	
  
249
	return (double)Time.QuadPart / 1000000.0;
  
250
  
  
251
#elif (defined(__hpux) || defined(hpux)) || ((defined(__sun__) || defined(__sun) || defined(sun)) && (defined(__SVR4) || defined(__svr4__)))
  
252
	/* HP-UX, Solaris. ------------------------------------------ */
  
253
	return (double)gethrtime( ) / 1000000000.0;
  
254
  
  
255
#elif defined(__MACH__) && defined(__APPLE__)
  
256
	/* OSX. ----------------------------------------------------- */
  
257
	static double timeConvert = 0.0;
  
258
	if ( timeConvert == 0.0 )
  
259
	{
  
260
		mach_timebase_info_data_t timeBase;
  
261
		(void)mach_timebase_info( &timeBase );
  
262
		timeConvert = (double)timeBase.numer /
  
263
			(double)timeBase.denom /
  
264
			1000000000.0;
  
265
	}
  
266
	return (double)mach_absolute_time( ) * timeConvert;
  
267
  
  
268
#elif defined(_POSIX_VERSION)
  
269
	/* POSIX. --------------------------------------------------- */
  
270
	struct timeval tm;
  
271
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
  
272
	{
  
273
		struct timespec ts;
  
274
#if defined(CLOCK_MONOTONIC_PRECISE)
  
275
		/* BSD. --------------------------------------------- */
  
276
		const clockid_t id = CLOCK_MONOTONIC_PRECISE;
  
277
#elif defined(CLOCK_MONOTONIC_RAW)
  
278
		/* Linux. ------------------------------------------- */
  
279
		const clockid_t id = CLOCK_MONOTONIC_RAW;
  
280
#elif defined(CLOCK_HIGHRES)
  
281
		/* Solaris. ----------------------------------------- */
  
282
		const clockid_t id = CLOCK_HIGHRES;
  
283
#elif defined(CLOCK_MONOTONIC)
  
284
		/* AIX, BSD, Linux, POSIX, Solaris. ----------------- */
  
285
		const clockid_t id = CLOCK_MONOTONIC;
  
286
#elif defined(CLOCK_REALTIME)
  
287
		/* AIX, BSD, HP-UX, Linux, POSIX. ------------------- */
  
288
		const clockid_t id = CLOCK_REALTIME;
  
289
#else
  
290
		const clockid_t id = (clockid_t)-1;	/* Unknown. */
  
291
#endif /* CLOCK_* */
  
292
		if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 )
  
293
			return (double)ts.tv_sec +
  
294
				(double)ts.tv_nsec / 1000000000.0;
  
295
		/* Fall thru. */
  
296
	}
  
297
#endif /* _POSIX_TIMERS */
  
298
  
  
299
	/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, POSIX, Solaris. ----- */
  
300
	gettimeofday( &tm, NULL );
  
301
	return (double)tm.tv_sec + (double)tm.tv_usec / 1000000.0;
  
302
#else
  
303
	return -1.0;		/* Failed. */
  
304
#endif
  
305
}
  
306
  
  
307
/**
  
308
 * Returns the amount of CPU time used by the current process,
  
309
 * in seconds, or -1.0 if an error occurred.
  
310
 */
  
311
static double mu_timer_cpu(void)
  
312
{
  
313
#if defined(_WIN32)
  
314
	/* Windows -------------------------------------------------- */
  
315
	FILETIME createTime;
  
316
	FILETIME exitTime;
  
317
	FILETIME kernelTime;
  
318
	FILETIME userTime;
  
319
  
  
320
	/* This approach has a resolution of 1/64 second. Unfortunately, Windows' API does not offer better */
  
321
	if ( GetProcessTimes( GetCurrentProcess( ),
  
322
		&createTime, &exitTime, &kernelTime, &userTime ) != 0 )
  
323
	{
  
324
		ULARGE_INTEGER userSystemTime;
  
325
		memcpy(&userSystemTime, &userTime, sizeof(ULARGE_INTEGER));
  
326
		return (double)userSystemTime.QuadPart / 10000000.0;
  
327
	}
  
328
  
  
329
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
  
330
	/* AIX, BSD, Cygwin, HP-UX, Linux, OSX, and Solaris --------- */
  
331
  
  
332
#if defined(_POSIX_TIMERS) && (_POSIX_TIMERS > 0)
  
333
	/* Prefer high-res POSIX timers, when available. */
  
334
	{
  
335
		clockid_t id;
  
336
		struct timespec ts;
  
337
#if _POSIX_CPUTIME > 0
  
338
		/* Clock ids vary by OS.  Query the id, if possible. */
  
339
		if ( clock_getcpuclockid( 0, &id ) == -1 )
  
340
#endif
  
341
#if defined(CLOCK_PROCESS_CPUTIME_ID)
  
342
			/* Use known clock id for AIX, Linux, or Solaris. */
  
343
			id = CLOCK_PROCESS_CPUTIME_ID;
  
344
#elif defined(CLOCK_VIRTUAL)
  
345
			/* Use known clock id for BSD or HP-UX. */
  
346
			id = CLOCK_VIRTUAL;
  
347
#else
  
348
			id = (clockid_t)-1;
  
349
#endif
  
350
		if ( id != (clockid_t)-1 && clock_gettime( id, &ts ) != -1 )
  
351
			return (double)ts.tv_sec +
  
352
				(double)ts.tv_nsec / 1000000000.0;
  
353
	}
  
354
#endif
  
355
  
  
356
#if defined(RUSAGE_SELF)
  
357
	{
  
358
		struct rusage rusage;
  
359
		if ( getrusage( RUSAGE_SELF, &rusage ) != -1 )
  
360
			return (double)rusage.ru_utime.tv_sec +
  
361
				(double)rusage.ru_utime.tv_usec / 1000000.0;
  
362
	}
  
363
#endif
  
364
  
  
365
#if defined(_SC_CLK_TCK)
  
366
	{
  
367
		const double ticks = (double)sysconf( _SC_CLK_TCK );
  
368
		struct tms tms;
  
369
		if ( times( &tms ) != (clock_t)-1 )
  
370
			return (double)tms.tms_utime / ticks;
  
371
	}
  
372
#endif
  
373
  
  
374
#if defined(CLOCKS_PER_SEC)
  
375
	{
  
376
		clock_t cl = clock( );
  
377
		if ( cl != (clock_t)-1 )
  
378
			return (double)cl / (double)CLOCKS_PER_SEC;
  
379
	}
  
380
#endif
  
381
  
  
382
#endif
  
383
  
  
384
	return -1;		/* Failed. */
  
385
}
  
386
  
  
387
#ifdef __cplusplus
  
388
}
  
389
#endif
  
390
  
  
391
#endif /* MINUNIT_MINUNIT_H */
diff --git a/models.h b/models.h
...
6
#include <string.h>
6
#include <string.h>
7
  
7
  
8
typedef struct {
8
typedef struct {
9
    const char *name;
9
	const char *name;
10
    const char *filepath;
10
	const char *filepath;
11
    int n_gpu_layers;
11
	int n_gpu_layers;
12
    bool use_mmap;
12
	bool use_mmap;
13
    int n_ctx;
13
	int n_ctx;
14
    int n_batch;
14
	int n_batch;
15
    bool embeddings;
15
	bool embeddings;
16
    float temperature;
16
	float temperature;
17
    float min_p;
17
	float min_p;
18
    uint32_t seed;
18
	uint32_t seed;
19
} model_config;
19
} ModelConfig;
20
  
20
  
21
model_config models[] = {
21
ModelConfig models[] = {
22
    {
22
	{
23
        .name = "flan-t5-small",
23
		.name = "tinyllama-1",
24
        .filepath = "models/flan-t5-small.F16.gguf",
24
		.filepath = "models/TinyLlama-1.1B-intermediate-step-1431k-3T-Q2_K.gguf",
25
        .n_gpu_layers = 0,
25
		.n_gpu_layers = 0,
26
        .use_mmap = false,
26
		.use_mmap = false,
27
        .n_ctx = 512,
27
		.n_ctx = 2048,
28
        .n_batch = 512,
28
		.n_batch = 4096,
29
        .embeddings = false,
29
		.embeddings = false,
30
        .temperature = 0.8f,
30
		.temperature = 0.8f,
31
        .min_p = 0.05f,
31
		.min_p = 0.05f,
32
        .seed = LLAMA_DEFAULT_SEED,
32
		.seed = LLAMA_DEFAULT_SEED,
33
    },
33
	},
34
    {
34
	{
35
        .name = "phi-4-mini-instruct",
35
		.name = "flan-t5-small",
36
        .filepath = "models/Phi-4-mini-instruct.Q2_K.gguf",
36
		.filepath = "models/flan-t5-small.F16.gguf",
37
        .n_gpu_layers = 0,
37
		.n_gpu_layers = 0,
38
        .use_mmap = false,
38
		.use_mmap = false,
39
        .n_ctx = 131072,
39
		.n_ctx = 512,
40
        .n_batch = 4096,
40
		.n_batch = 512,
41
        .embeddings = false,
41
		.embeddings = false,
42
        .temperature = 0.8f,
42
		.temperature = 0.8f,
43
        .min_p = 0.05f,
43
		.min_p = 0.05f,
44
        .seed = LLAMA_DEFAULT_SEED,
44
		.seed = LLAMA_DEFAULT_SEED,
45
    },
45
	},
46
    {
46
	{
47
        .name = "tinyllama-1",
47
		.name = "phi-4-mini-instruct",
48
        .filepath = "models/TinyLlama-1.1B-intermediate-step-1431k-3T-Q2_K.gguf",
48
		.filepath = "models/Phi-4-mini-instruct.Q2_K.gguf",
49
        .n_gpu_layers = 0,
49
		.n_gpu_layers = 0,
50
        .use_mmap = false,
50
		.use_mmap = false,
51
        .n_ctx = 2048,
51
		.n_ctx = 131072,
52
        .n_batch = 4096,
52
		.n_batch = 4096,
53
        .embeddings = false,
53
		.embeddings = false,
54
        .temperature = 0.8f,
54
		.temperature = 0.8f,
55
        .min_p = 0.05f,
55
		.min_p = 0.05f,
56
        .seed = LLAMA_DEFAULT_SEED,
56
		.seed = LLAMA_DEFAULT_SEED,
57
    },
57
	},
58
};
58
};
59
  
59
  
60
const model_config *get_model_by_name(const char *name) {
60
const ModelConfig *get_model_by_name(const char *name) {
61
    for (size_t i = 0; i < sizeof(models) / sizeof(models[0]); i++) {
61
	for (size_t i = 0; i < sizeof(models) / sizeof(models[0]); i++) {
62
        if (models[i].name != NULL && strcmp(models[i].name, name) == 0) {
62
		if (models[i].name != NULL && strcmp(models[i].name, name) == 0) {
63
            return &models[i];
63
			return &models[i];
64
        }
64
		}
65
    }
65
	}
66
    return NULL;
66
	return NULL;
67
}
67
}
68
  
68
  
69
#endif
69
#endif
diff --git a/nonstd.h b/nonstd.h
  
1
// nonstd.h
  
2
// A collection of useful functions and macros.
  
3
// This library is licensed under the BSD 2-Clause License.
  
4
// 
  
5
// This file provides both the interface and the implementation.
  
6
// To instantiate the implementation,
  
7
//     #define NONSTD_IMPLEMENTATION
  
8
// before including this file.
  
9
  
  
10
#ifdef NONSTD_IMPLEMENTATION
  
11
#ifndef _POSIX_C_SOURCE
  
12
#define _POSIX_C_SOURCE 200809L
  
13
#endif
  
14
#endif
  
15
  
  
16
#ifndef NONSTD_H
  
17
#define NONSTD_H
  
18
  
  
19
#include <stdarg.h>
  
20
#include <stddef.h>
  
21
#include <stdint.h>
  
22
#include <stdio.h>
  
23
#include <stdlib.h>
  
24
#include <string.h>
  
25
#include <sys/time.h>
  
26
#include <time.h>
  
27
#include <unistd.h>
  
28
  
  
29
#ifndef NONSTD_DEF
  
30
#ifdef NONSTD_STATIC
  
31
#define NONSTD_DEF static
  
32
#else
  
33
#define NONSTD_DEF extern
  
34
#endif
  
35
#endif
  
36
  
  
37
typedef int8_t i8;
  
38
typedef uint8_t u8;
  
39
typedef int16_t i16;
  
40
typedef uint16_t u16;
  
41
typedef int32_t i32;
  
42
typedef uint32_t u32;
  
43
typedef int64_t i64;
  
44
typedef uint64_t u64;
  
45
typedef unsigned int uint;
  
46
typedef unsigned long ulong;
  
47
typedef intptr_t isize;
  
48
typedef uintptr_t usize;
  
49
typedef char c8;
  
50
  
  
51
#define countof(a) (sizeof(a) / sizeof((a)[0]))
  
52
  
  
53
#define static_foreach(type, var, array)                 \
  
54
	for (size_t _i_##var = 0, _n_##var = countof(array); \
  
55
		 _i_##var < _n_##var && ((var) = (array)[_i_##var], 1); ++_i_##var)
  
56
  
  
57
#define ALLOC(type, n) ((type *)safe_malloc(sizeof(type), (n)))
  
58
#define REALLOC(ptr, type, n) ((type *)safe_realloc((ptr), sizeof(type), (n)))
  
59
#define FREE(ptr)   \
  
60
	do {            \
  
61
		free(ptr);  \
  
62
		ptr = NULL; \
  
63
	} while (0)
  
64
  
  
65
NONSTD_DEF void *safe_malloc(size_t item_size, size_t count);
  
66
NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count);
  
67
  
  
68
#define MIN(a, b) ((a) < (b) ? (a) : (b))
  
69
#define MAX(a, b) ((a) > (b) ? (a) : (b))
  
70
#define CLAMP(x, lo, hi) (MIN((hi), MAX((lo), (x))))
  
71
  
  
72
// From https://github.com/tsoding/nob.h/blob/e2c9a46f01d052ab740140e74453665dc3334832/nob.h#L205-L206.
  
73
#define UNUSED(value) (void)(value)
  
74
#define TODO(message)                                                      \
  
75
	do {                                                                   \
  
76
		fprintf(stderr, "%s:%d: TODO: %s\n", __FILE__, __LINE__, message); \
  
77
		abort();                                                           \
  
78
	} while (0)
  
79
#define UNREACHABLE(message)                                                      \
  
80
	do {                                                                          \
  
81
		fprintf(stderr, "%s:%d: UNREACHABLE: %s\n", __FILE__, __LINE__, message); \
  
82
		abort();                                                                  \
  
83
	} while (0)
  
84
  
  
85
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
  
86
#define STATIC_ASSERT(expr, msg) _Static_assert((expr), msg)
  
87
#else
  
88
#define STATIC_ASSERT(expr, msg) \
  
89
	typedef char static_assertion_##msg[(expr) ? 1 : -1]
  
90
#endif
  
91
  
  
92
// String view - read-only, non-owning reference to a string
  
93
typedef struct {
  
94
	const char *data;
  
95
	size_t length;
  
96
} stringv;
  
97
  
  
98
NONSTD_DEF stringv sv_from_cstr(const char *s);
  
99
NONSTD_DEF stringv sv_from_parts(const char *data, size_t length);
  
100
NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end);
  
101
NONSTD_DEF int sv_equals(stringv a, stringv b);
  
102
NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix);
  
103
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix);
  
104
  
  
105
// String builder - owning, mutable, dynamically growing string buffer
  
106
typedef struct {
  
107
	char *data;
  
108
	size_t length;
  
109
	size_t capacity;
  
110
} stringb;
  
111
  
  
112
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap);
  
113
NONSTD_DEF void sb_free(stringb *sb);
  
114
NONSTD_DEF void sb_ensure(stringb *sb, size_t additional);
  
115
NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s);
  
116
NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv);
  
117
NONSTD_DEF void sb_append_char(stringb *sb, char c);
  
118
NONSTD_DEF stringv sb_as_sv(const stringb *sb);
  
119
  
  
120
// Slice - generic non-owning view into an array
  
121
// Usage: SLICE_DEF(int); slice(int) view = ...;
  
122
#define SLICE_DEF(T)   \
  
123
	typedef struct {   \
  
124
		T *data;       \
  
125
		size_t length; \
  
126
	} slice_##T
  
127
  
  
128
#define slice(T) slice_##T
  
129
  
  
130
#define make_slice(T, ptr, len) ((slice(T)){.data = (ptr), .length = (len)})
  
131
  
  
132
#define array_as_slice(T, arr) \
  
133
	((slice(T)){.data = (arr).data, .length = (arr).length})
  
134
  
  
135
// Dynamic array - generic type-safe growable array using macros
  
136
// Usage: array(int) numbers; array_init(numbers);
  
137
#define array(T)         \
  
138
	struct {             \
  
139
		T *data;         \
  
140
		size_t length;   \
  
141
		size_t capacity; \
  
142
	}
  
143
  
  
144
#define array_init(arr)     \
  
145
	do {                    \
  
146
		(arr).capacity = 0; \
  
147
		(arr).data = NULL;  \
  
148
		(arr).length = 0;   \
  
149
	} while (0)
  
150
  
  
151
#define array_init_cap(arr, initial_cap)                             \
  
152
	do {                                                             \
  
153
		(arr).capacity = (initial_cap) ? (initial_cap) : 16;         \
  
154
		(arr).data = ALLOC(__typeof__(*(arr).data), (arr).capacity); \
  
155
		(arr).length = 0;                                            \
  
156
	} while (0)
  
157
  
  
158
#define array_free(arr)     \
  
159
	do {                    \
  
160
		FREE((arr).data);   \
  
161
		(arr).length = 0;   \
  
162
		(arr).capacity = 0; \
  
163
	} while (0)
  
164
  
  
165
#define array_ensure(arr, additional)                                    \
  
166
	do {                                                                 \
  
167
		size_t _needed = (arr).length + (additional);                    \
  
168
		if (_needed > (arr).capacity) {                                  \
  
169
			size_t _new_cap = (arr).capacity ? (arr).capacity : 16;      \
  
170
			while (_new_cap < _needed) {                                 \
  
171
				if (_new_cap > SIZE_MAX / 2) {                           \
  
172
					_new_cap = SIZE_MAX;                                 \
  
173
					break;                                               \
  
174
				}                                                        \
  
175
				_new_cap *= 2;                                           \
  
176
			}                                                            \
  
177
			if (_new_cap < _needed) { /* Overflow or OOM */              \
  
178
				break;                                                   \
  
179
			}                                                            \
  
180
			void *_new_data =                                            \
  
181
				safe_realloc((arr).data, sizeof(*(arr).data), _new_cap); \
  
182
			if (_new_data) {                                             \
  
183
				(arr).data = _new_data;                                  \
  
184
				(arr).capacity = _new_cap;                               \
  
185
			}                                                            \
  
186
		}                                                                \
  
187
	} while (0)
  
188
  
  
189
#define array_push(arr, value)                    \
  
190
	do {                                          \
  
191
		array_ensure((arr), 1);                   \
  
192
		if ((arr).length < (arr).capacity) {      \
  
193
			(arr).data[(arr).length++] = (value); \
  
194
		}                                         \
  
195
	} while (0)
  
196
  
  
197
#define array_pop(arr) ((arr).length > 0 ? (arr).data[--(arr).length] : 0)
  
198
  
  
199
#define array_get(arr, index) ((arr).data[index])
  
200
  
  
201
#define array_set(arr, index, value)     \
  
202
	do {                                 \
  
203
		if ((index) < (arr).length) {    \
  
204
			(arr).data[index] = (value); \
  
205
		}                                \
  
206
	} while (0)
  
207
  
  
208
#define array_insert(arr, index, value)                              \
  
209
	do {                                                             \
  
210
		if ((index) <= (arr).length) {                               \
  
211
			array_ensure((arr), 1);                                  \
  
212
			if ((arr).length < (arr).capacity) {                     \
  
213
				for (size_t _i = (arr).length; _i > (index); --_i) { \
  
214
					(arr).data[_i] = (arr).data[_i - 1];             \
  
215
				}                                                    \
  
216
				(arr).data[index] = (value);                         \
  
217
				(arr).length++;                                      \
  
218
			}                                                        \
  
219
		}                                                            \
  
220
	} while (0)
  
221
  
  
222
#define array_remove(arr, index)                                     \
  
223
	do {                                                             \
  
224
		if ((index) < (arr).length) {                                \
  
225
			for (size_t _i = (index); _i < (arr).length - 1; ++_i) { \
  
226
				(arr).data[_i] = (arr).data[_i + 1];                 \
  
227
			}                                                        \
  
228
			(arr).length--;                                          \
  
229
		}                                                            \
  
230
	} while (0)
  
231
  
  
232
#define array_clear(arr)  \
  
233
	do {                  \
  
234
		(arr).length = 0; \
  
235
	} while (0)
  
236
  
  
237
#define array_reserve(arr, new_capacity)                                       \
  
238
	do {                                                                       \
  
239
		if ((new_capacity) > (arr).capacity) {                                 \
  
240
			void *_new_data =                                                  \
  
241
				safe_realloc((arr).data, sizeof(*(arr).data), (new_capacity)); \
  
242
			if (_new_data) {                                                   \
  
243
				(arr).data = _new_data;                                        \
  
244
				(arr).capacity = (new_capacity);                               \
  
245
			}                                                                  \
  
246
		}                                                                      \
  
247
	} while (0)
  
248
  
  
249
#define array_foreach(arr, var)                                        \
  
250
	for (size_t _i_##var = 0;                                          \
  
251
		 _i_##var < (arr).length && ((var) = (arr).data[_i_##var], 1); \
  
252
		 ++_i_##var)
  
253
  
  
254
#define array_foreach_idx(arr, var, index) \
  
255
	for (size_t index = 0;                 \
  
256
		 index < (arr).length && ((var) = (arr).data[index], 1); ++index)
  
257
  
  
258
// Arena - block-based memory allocator
  
259
typedef struct {
  
260
	char *ptr;
  
261
	char *end;
  
262
	array(char *) blocks;
  
263
} Arena;
  
264
  
  
265
#define ARENA_DEFAULT_BLOCK_SIZE (4096)
  
266
  
  
267
NONSTD_DEF Arena arena_make(void);
  
268
NONSTD_DEF void arena_grow(Arena *a, size_t min_size);
  
269
NONSTD_DEF void *arena_alloc(Arena *a, size_t size);
  
270
NONSTD_DEF void arena_free(Arena *a);
  
271
  
  
272
// Image - simple RGB image structure
  
273
typedef struct {
  
274
	u8 r, g, b;
  
275
} Color;
  
276
  
  
277
typedef struct {
  
278
	u32 width;
  
279
	u32 height;
  
280
	Color *pixels;
  
281
} Canvas;
  
282
  
  
283
#define COLOR_RGB(r, g, b) ((Color){(u8)(r), (u8)(g), (u8)(b)})
  
284
#define COLOR_HEX(hex) ((Color){(u8)(((hex) >> 16) & 0xFF), (u8)(((hex) >> 8) & 0xFF), (u8)((hex) & 0xFF)})
  
285
  
  
286
#define COLOR_BLACK COLOR_RGB(0, 0, 0)
  
287
#define COLOR_WHITE COLOR_RGB(255, 255, 255)
  
288
#define COLOR_RED COLOR_RGB(255, 0, 0)
  
289
#define COLOR_GREEN COLOR_RGB(0, 255, 0)
  
290
#define COLOR_BLUE COLOR_RGB(0, 0, 255)
  
291
#define COLOR_YELLOW COLOR_RGB(255, 255, 0)
  
292
#define COLOR_MAGENTA COLOR_RGB(255, 0, 255)
  
293
#define COLOR_CYAN COLOR_RGB(0, 255, 255)
  
294
  
  
295
NONSTD_DEF Canvas ppm_init(u32 width, u32 height);
  
296
NONSTD_DEF void ppm_free(Canvas *img);
  
297
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color);
  
298
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y);
  
299
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename);
  
300
NONSTD_DEF Canvas ppm_read(const char *filename);
  
301
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color);
  
302
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color);
  
303
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color);
  
304
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 x, i32 y, i32 r, Color color);
  
305
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color);
  
306
  
  
307
// File I/O helpers
  
308
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size);
  
309
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size);
  
310
NONSTD_DEF stringb read_entire_file_sb(const char *filepath);
  
311
NONSTD_DEF int write_file_sv(const char *filepath, stringv sv);
  
312
NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb);
  
313
  
  
314
// Logging
  
315
typedef enum {
  
316
	LOG_ERROR,
  
317
	LOG_WARN,
  
318
	LOG_INFO,
  
319
	LOG_DEBUG,
  
320
} LogLevel;
  
321
  
  
322
NONSTD_DEF void set_log_level(LogLevel level);
  
323
NONSTD_DEF LogLevel get_log_level_from_env(void);
  
324
NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...);
  
325
  
  
326
#define LOG_INFO_MSG(...) log_message(stdout, LOG_INFO, __VA_ARGS__)
  
327
#define LOG_DEBUG_MSG(...) log_message(stdout, LOG_DEBUG, __VA_ARGS__)
  
328
#define LOG_WARN_MSG(...) log_message(stderr, LOG_WARN, __VA_ARGS__)
  
329
#define LOG_ERROR_MSG(...) log_message(stderr, LOG_ERROR, __VA_ARGS__)
  
330
  
  
331
#define COLOR_RESET "\033[0m"
  
332
#define COLOR_INFO "\033[32m"
  
333
#define COLOR_DEBUG "\033[36m"
  
334
#define COLOR_WARNING "\033[33m"
  
335
#define COLOR_ERROR "\033[31m"
  
336
  
  
337
#endif // NONSTD_H
  
338
  
  
339
#ifdef NONSTD_IMPLEMENTATION
  
340
  
  
341
NONSTD_DEF void *safe_malloc(size_t item_size, size_t count) {
  
342
	if (count != 0 && item_size > SIZE_MAX / count) {
  
343
		return NULL;
  
344
	}
  
345
	return malloc(item_size * count);
  
346
}
  
347
  
  
348
NONSTD_DEF void *safe_realloc(void *ptr, size_t item_size, size_t count) {
  
349
	if (count != 0 && item_size > SIZE_MAX / count) {
  
350
		return NULL;
  
351
	}
  
352
	return realloc(ptr, item_size * count);
  
353
}
  
354
  
  
355
// String View Implementation
  
356
  
  
357
NONSTD_DEF stringv sv_from_cstr(const char *s) {
  
358
	return (stringv){.data = s, .length = s ? strlen(s) : 0};
  
359
}
  
360
  
  
361
NONSTD_DEF stringv sv_from_parts(const char *data, size_t length) {
  
362
	return (stringv){.data = data, .length = length};
  
363
}
  
364
  
  
365
NONSTD_DEF stringv sv_slice(stringv sv, size_t start, size_t end) {
  
366
	if (start > sv.length) {
  
367
		start = sv.length;
  
368
	}
  
369
	if (end > sv.length) {
  
370
		end = sv.length;
  
371
	}
  
372
	if (start > end) {
  
373
		start = end;
  
374
	}
  
375
	return (stringv){.data = sv.data + start, .length = end - start};
  
376
}
  
377
  
  
378
NONSTD_DEF int sv_equals(stringv a, stringv b) {
  
379
	return a.length == b.length && (a.length == 0 || memcmp(a.data, b.data, a.length) == 0);
  
380
}
  
381
  
  
382
NONSTD_DEF int sv_starts_with(stringv sv, stringv prefix) {
  
383
	return sv.length >= prefix.length && memcmp(sv.data, prefix.data, prefix.length) == 0;
  
384
}
  
385
  
  
386
NONSTD_DEF int sv_ends_with(stringv sv, stringv suffix) {
  
387
	return sv.length >= suffix.length && memcmp(sv.data + sv.length - suffix.length, suffix.data, suffix.length) == 0;
  
388
}
  
389
  
  
390
// String Builder Implementation
  
391
  
  
392
NONSTD_DEF void sb_init(stringb *sb, size_t initial_cap) {
  
393
	sb->capacity = initial_cap ? initial_cap : 16;
  
394
	sb->data = ALLOC(char, sb->capacity);
  
395
	sb->length = 0;
  
396
	if (sb->data) {
  
397
		sb->data[0] = '\0';
  
398
	}
  
399
}
  
400
  
  
401
NONSTD_DEF void sb_free(stringb *sb) {
  
402
	FREE(sb->data);
  
403
	sb->length = 0;
  
404
	sb->capacity = 0;
  
405
}
  
406
  
  
407
NONSTD_DEF void sb_ensure(stringb *sb, size_t additional) {
  
408
	size_t needed = sb->length + additional + 1;
  
409
	size_t new_cap = sb->capacity;
  
410
  
  
411
	if (needed > new_cap) {
  
412
		while (new_cap < needed) {
  
413
			if (new_cap > SIZE_MAX / 2) {
  
414
				new_cap = SIZE_MAX;
  
415
				break;
  
416
			}
  
417
			new_cap *= 2;
  
418
		}
  
419
		if (new_cap < needed)
  
420
			return; // Overflow
  
421
  
  
422
		char *new_data = safe_realloc(sb->data, sizeof(char), new_cap);
  
423
		if (new_data) {
  
424
			sb->data = new_data;
  
425
			sb->capacity = new_cap;
  
426
		}
  
427
	}
  
428
}
  
429
  
  
430
NONSTD_DEF void sb_append_cstr(stringb *sb, const char *s) {
  
431
	if (!s) {
  
432
		return;
  
433
	}
  
434
	size_t slength = strlen(s);
  
435
	sb_ensure(sb, slength);
  
436
	if (sb->length + slength + 1 <= sb->capacity) {
  
437
		memcpy(sb->data + sb->length, s, slength);
  
438
		sb->length += slength;
  
439
		sb->data[sb->length] = '\0';
  
440
	}
  
441
}
  
442
  
  
443
NONSTD_DEF void sb_append_sv(stringb *sb, stringv sv) {
  
444
	if (!sv.data || sv.length == 0) {
  
445
		return;
  
446
	}
  
447
	sb_ensure(sb, sv.length);
  
448
	if (sb->length + sv.length + 1 <= sb->capacity) {
  
449
		memcpy(sb->data + sb->length, sv.data, sv.length);
  
450
		sb->length += sv.length;
  
451
		sb->data[sb->length] = '\0';
  
452
	}
  
453
}
  
454
  
  
455
NONSTD_DEF void sb_append_char(stringb *sb, char c) {
  
456
	sb_ensure(sb, 1);
  
457
	if (sb->length + 2 <= sb->capacity) {
  
458
		sb->data[sb->length++] = c;
  
459
		sb->data[sb->length] = '\0';
  
460
	}
  
461
}
  
462
  
  
463
NONSTD_DEF stringv sb_as_sv(const stringb *sb) {
  
464
	return (stringv){.data = sb->data, .length = sb->length};
  
465
}
  
466
  
  
467
NONSTD_DEF Arena arena_make(void) {
  
468
	Arena a = {0};
  
469
	array_init(a.blocks);
  
470
	return a;
  
471
}
  
472
  
  
473
// Arena Implementation
  
474
  
  
475
NONSTD_DEF void arena_grow(Arena *a, size_t min_size) {
  
476
	size_t size = MAX(ARENA_DEFAULT_BLOCK_SIZE, min_size);
  
477
	char *block = ALLOC(char, size);
  
478
	a->ptr = block;
  
479
	a->end = block + size;
  
480
	array_push(a->blocks, block);
  
481
}
  
482
  
  
483
NONSTD_DEF void *arena_alloc(Arena *a, size_t size) {
  
484
	// Align to 8 bytes basically
  
485
	size_t align = sizeof(void *);
  
486
	uintptr_t current = (uintptr_t)a->ptr;
  
487
	uintptr_t aligned = (current + align - 1) & ~(align - 1);
  
488
	uintptr_t end = (uintptr_t)a->end;
  
489
  
  
490
	// Check for overflow (aligned wrapped around) or out of bounds (aligned >= end)
  
491
	// or not enough space ((end - aligned) < size)
  
492
	if (aligned < current || aligned >= end || (end - aligned) < size) {
  
493
		arena_grow(a, size);
  
494
		current = (uintptr_t)a->ptr;
  
495
		aligned = (current + align - 1) & ~(align - 1);
  
496
		end = (uintptr_t)a->end;
  
497
	}
  
498
  
  
499
	// Double check after grow (in case grow failed or size is just too huge)
  
500
	if (aligned < current || aligned >= end || (end - aligned) < size) {
  
501
		return NULL;
  
502
	}
  
503
  
  
504
	a->ptr = (char *)(aligned + size);
  
505
	return (void *)aligned;
  
506
}
  
507
  
  
508
NONSTD_DEF void arena_free(Arena *a) {
  
509
	char *block;
  
510
	array_foreach(a->blocks, block) { FREE(block); }
  
511
	array_free(a->blocks);
  
512
	a->ptr = NULL;
  
513
	a->end = NULL;
  
514
}
  
515
  
  
516
// File I/O Implementation
  
517
  
  
518
NONSTD_DEF char *read_entire_file(const char *filepath, size_t *out_size) {
  
519
	FILE *f = fopen(filepath, "rb");
  
520
	if (!f) {
  
521
		return NULL;
  
522
	}
  
523
  
  
524
	fseek(f, 0, SEEK_END);
  
525
	long length = ftell(f);
  
526
	fseek(f, 0, SEEK_SET);
  
527
  
  
528
	if (length < 0) {
  
529
		fclose(f);
  
530
		return NULL;
  
531
	}
  
532
  
  
533
	size_t size = (size_t)length;
  
534
	char *buffer = ALLOC(char, size + 1);
  
535
	if (!buffer) {
  
536
		fclose(f);
  
537
		return NULL;
  
538
	}
  
539
  
  
540
	size_t read = fread(buffer, 1, size, f);
  
541
	fclose(f);
  
542
  
  
543
	if (read != size) {
  
544
		FREE(buffer);
  
545
		return NULL;
  
546
	}
  
547
  
  
548
	buffer[size] = '\0';
  
549
	if (out_size) {
  
550
		*out_size = size;
  
551
	}
  
552
  
  
553
	return buffer;
  
554
}
  
555
  
  
556
NONSTD_DEF int write_entire_file(const char *filepath, const void *data, size_t size) {
  
557
	FILE *f = fopen(filepath, "wb");
  
558
	if (!f) {
  
559
		return 0;
  
560
	}
  
561
  
  
562
	size_t written = fwrite(data, 1, size, f);
  
563
	fclose(f);
  
564
  
  
565
	return written == size;
  
566
}
  
567
  
  
568
NONSTD_DEF stringb read_entire_file_sb(const char *filepath) {
  
569
	size_t size = 0;
  
570
	char *data = read_entire_file(filepath, &size);
  
571
	stringb sb = {0};
  
572
	if (data) {
  
573
		sb.data = data;
  
574
		sb.length = size;
  
575
		sb.capacity = size + 1;
  
576
	}
  
577
	return sb;
  
578
}
  
579
  
  
580
NONSTD_DEF int write_file_sv(const char *filepath, stringv sv) {
  
581
	return write_entire_file(filepath, sv.data, sv.length);
  
582
}
  
583
  
  
584
NONSTD_DEF int write_file_sb(const char *filepath, const stringb *sb) {
  
585
	return write_entire_file(filepath, sb->data, sb->length);
  
586
}
  
587
  
  
588
// Logging Implementation
  
589
  
  
590
static LogLevel max_level = LOG_INFO;
  
591
  
  
592
static const char *level_strings[] = {
  
593
	"ERROR",
  
594
	"WARN",
  
595
	"INFO",
  
596
	"DEBUG",
  
597
};
  
598
  
  
599
static const char *level_colors[] = {
  
600
	COLOR_ERROR,
  
601
	COLOR_WARNING,
  
602
	COLOR_INFO,
  
603
	COLOR_DEBUG,
  
604
};
  
605
  
  
606
NONSTD_DEF void set_log_level(LogLevel level) {
  
607
	max_level = level;
  
608
}
  
609
  
  
610
NONSTD_DEF LogLevel get_log_level_from_env(void) {
  
611
	const char *env = getenv("LOG_LEVEL");
  
612
	if (env) {
  
613
		int level = atoi(env);
  
614
		if (level >= 0 && level <= 3) {
  
615
			return (LogLevel)level;
  
616
		}
  
617
	}
  
618
  
  
619
	return max_level;
  
620
}
  
621
  
  
622
NONSTD_DEF void log_message(FILE *stream, LogLevel level, const char *format, ...) {
  
623
	if (max_level < level)
  
624
		return;
  
625
  
  
626
	struct timeval tv;
  
627
	gettimeofday(&tv, NULL);
  
628
	struct tm *tm_info = localtime(&tv.tv_sec);
  
629
  
  
630
	char time_str[24];
  
631
	strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);
  
632
  
  
633
	const char *color = isatty(fileno(stream)) ? level_colors[level] : "";
  
634
	const char *reset = isatty(fileno(stream)) ? COLOR_RESET : "";
  
635
  
  
636
	const char *log_format = "%s[%s.%03d] [%-5s] ";
  
637
	fprintf(stream, log_format, color, time_str, (int)(tv.tv_usec / 1000), level_strings[level]);
  
638
  
  
639
	va_list args;
  
640
	va_start(args, format);
  
641
	vfprintf(stream, format, args);
  
642
	va_end(args);
  
643
  
  
644
	fprintf(stream, "%s\n", reset);
  
645
	fflush(stream);
  
646
}
  
647
  
  
648
// PPM Image Implementation
  
649
  
  
650
NONSTD_DEF Canvas ppm_init(u32 width, u32 height) {
  
651
	Canvas img = {0};
  
652
	img.width = width;
  
653
	img.height = height;
  
654
	img.pixels = ALLOC(Color, width * height);
  
655
	if (img.pixels) {
  
656
		memset(img.pixels, 0, sizeof(Color) * width * height);
  
657
	}
  
658
	return img;
  
659
}
  
660
  
  
661
NONSTD_DEF void ppm_free(Canvas *img) {
  
662
	if (img->pixels) {
  
663
		FREE(img->pixels);
  
664
	}
  
665
	img->width = 0;
  
666
	img->height = 0;
  
667
}
  
668
  
  
669
NONSTD_DEF void ppm_set_pixel(Canvas *img, u32 x, u32 y, Color color) {
  
670
	if (x < img->width && y < img->height) {
  
671
		img->pixels[y * img->width + x] = color;
  
672
	}
  
673
}
  
674
  
  
675
NONSTD_DEF Color ppm_get_pixel(const Canvas *img, u32 x, u32 y) {
  
676
	if (x < img->width && y < img->height) {
  
677
		return img->pixels[y * img->width + x];
  
678
	}
  
679
	return (Color){0, 0, 0};
  
680
}
  
681
  
  
682
NONSTD_DEF int ppm_save(const Canvas *img, const char *filename) {
  
683
	FILE *f = fopen(filename, "w");
  
684
	if (!f) {
  
685
		return 0;
  
686
	}
  
687
  
  
688
	fprintf(f, "P3\n%u %u\n255\n", img->width, img->height);
  
689
	for (u32 y = 0; y < img->height; ++y) {
  
690
		for (u32 x = 0; x < img->width; ++x) {
  
691
			Color c = ppm_get_pixel(img, x, y);
  
692
			fprintf(f, "%d %d %d ", c.r, c.g, c.b);
  
693
		}
  
694
		fprintf(f, "\n");
  
695
	}
  
696
  
  
697
	fclose(f);
  
698
	return 1;
  
699
}
  
700
  
  
701
NONSTD_DEF Canvas ppm_read(const char *filename) {
  
702
	Canvas img = {0};
  
703
	FILE *f = fopen(filename, "r");
  
704
	if (!f) {
  
705
		return img;
  
706
	}
  
707
  
  
708
	char magic[3];
  
709
	if (fscanf(f, "%2s", magic) != 1 || strcmp(magic, "P3") != 0) {
  
710
		fclose(f);
  
711
		return img;
  
712
	}
  
713
  
  
714
	u32 w, h, max_val;
  
715
	if (fscanf(f, "%u %u %u", &w, &h, &max_val) != 3) {
  
716
		fclose(f);
  
717
		return img;
  
718
	}
  
719
  
  
720
	img = ppm_init(w, h);
  
721
	if (!img.pixels) {
  
722
		fclose(f);
  
723
		return img;
  
724
	}
  
725
  
  
726
	for (u32 i = 0; i < w * h; ++i) {
  
727
		int r, g, b;
  
728
		if (fscanf(f, "%d %d %d", &r, &g, &b) != 3) {
  
729
			ppm_free(&img);
  
730
			fclose(f);
  
731
			return (Canvas){0};
  
732
		}
  
733
		img.pixels[i] = (Color){(u8)r, (u8)g, (u8)b};
  
734
	}
  
735
  
  
736
	fclose(f);
  
737
	return img;
  
738
}
  
739
  
  
740
NONSTD_DEF void ppm_fill(Canvas *canvas, Color color) {
  
741
	for (u32 i = 0; i < canvas->width * canvas->height; ++i) {
  
742
		canvas->pixels[i] = color;
  
743
	}
  
744
}
  
745
  
  
746
NONSTD_DEF void ppm_draw_rect(Canvas *canvas, u32 x, u32 y, u32 w, u32 h, Color color) {
  
747
	if (w == 0 || h == 0) {
  
748
		return;
  
749
	}
  
750
	for (u32 i = x; i < x + w; ++i) {
  
751
		ppm_set_pixel(canvas, i, y, color);
  
752
		ppm_set_pixel(canvas, i, y + h - 1, color);
  
753
	}
  
754
	for (u32 j = y; j < y + h; ++j) {
  
755
		ppm_set_pixel(canvas, x, j, color);
  
756
		ppm_set_pixel(canvas, x + w - 1, j, color);
  
757
	}
  
758
}
  
759
  
  
760
NONSTD_DEF void ppm_draw_line(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, Color color) {
  
761
	i32 dx = abs(x1 - x0);
  
762
	i32 dy = -abs(y1 - y0);
  
763
	i32 sx = x0 < x1 ? 1 : -1;
  
764
	i32 sy = y0 < y1 ? 1 : -1;
  
765
	i32 err = dx + dy;
  
766
  
  
767
	while (1) {
  
768
		ppm_set_pixel(canvas, (u32)x0, (u32)y0, color);
  
769
		if (x0 == x1 && y0 == y1) {
  
770
			break;
  
771
		}
  
772
  
  
773
		i32 e2 = 2 * err;
  
774
		if (e2 >= dy) {
  
775
			err += dy;
  
776
			x0 += sx;
  
777
		}
  
778
		if (e2 <= dx) {
  
779
			err += dx;
  
780
			y0 += sy;
  
781
		}
  
782
	}
  
783
}
  
784
  
  
785
NONSTD_DEF void ppm_draw_circle(Canvas *canvas, i32 xm, i32 ym, i32 r, Color color) {
  
786
	i32 x = -r, y = 0, err = 2 - 2 * r;
  
787
	do {
  
788
		ppm_set_pixel(canvas, (u32)(xm - x), (u32)(ym + y), color);
  
789
		ppm_set_pixel(canvas, (u32)(xm - y), (u32)(ym - x), color);
  
790
		ppm_set_pixel(canvas, (u32)(xm + x), (u32)(ym - y), color);
  
791
		ppm_set_pixel(canvas, (u32)(xm + y), (u32)(ym + x), color);
  
792
		r = err;
  
793
		if (r <= y) {
  
794
			err += ++y * 2 + 1;
  
795
		}
  
796
		if (r > x || err > y) {
  
797
			err += ++x * 2 + 1;
  
798
		}
  
799
	} while (x < 0);
  
800
}
  
801
  
  
802
NONSTD_DEF void ppm_draw_triangle(Canvas *canvas, i32 x0, i32 y0, i32 x1, i32 y1, i32 x2, i32 y2, Color color) {
  
803
	ppm_draw_line(canvas, x0, y0, x1, y1, color);
  
804
	ppm_draw_line(canvas, x1, y1, x2, y2, color);
  
805
	ppm_draw_line(canvas, x2, y2, x0, y0, color);
  
806
}
  
807
  
  
808
#endif // NONSTD_IMPLEMENTATION
  
809
  
  
810
/*
  
811
BSD 2-Clause License
  
812
  
  
813
Copyright (c) 2026, Mitja Felicijan <mitja.felicijan@gmail.com>
  
814
  
  
815
Redistribution and use in source and binary forms, with or without
  
816
modification, are permitted provided that the following conditions are met:
  
817
  
  
818
1. Redistributions of source code must retain the above copyright notice, this
  
819
   list of conditions and the following disclaimer.
  
820
  
  
821
2. Redistributions in binary form must reproduce the above copyright notice,
  
822
   this list of conditions and the following disclaimer in the documentation
  
823
   and/or other materials provided with the distribution.
  
824
  
  
825
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  
826
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  
827
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  
828
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  
829
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  
830
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  
831
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  
832
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  
833
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  
834
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
835
*/
diff --git a/prompt.c b/prompt.c
...
11
#define MAX_TOKENS 512
11
#define MAX_TOKENS 512
12
#define MAX_TOKEN_LEN 32
12
#define MAX_TOKEN_LEN 32
13
  
13
  
  
14
typedef struct {
  
15
  
  
16
} Engine;
  
17
  
14
static const char *refusal_text = "I don't have that information.";
18
static const char *refusal_text = "I don't have that information.";
15
  
19
  
16
static void llama_log_callback(enum ggml_log_level level, const char *text, void *user_data) {
20
static void llama_log_callback(enum ggml_log_level level, const char *text, void *user_data) {
...
58
                buf[len] = '\0';
62
                buf[len] = '\0';
59
                if (len >= 4 && !is_stopword(buf, (size_t)len)) {
63
                if (len >= 4 && !is_stopword(buf, (size_t)len)) {
60
                    if (!token_exists(tokens, count, buf) && count < MAX_TOKENS) {
64
                    if (!token_exists(tokens, count, buf) && count < MAX_TOKENS) {
61
                        strncpy(tokens[count], buf, MAX_TOKEN_LEN - 1);
65
                        memcpy(tokens[count], buf, (size_t)len + 1);
62
                        tokens[count][MAX_TOKEN_LEN - 1] = '\0';
66
                        tokens[count][MAX_TOKEN_LEN - 1] = '\0';
63
                        count++;
67
                        count++;
64
                    }
68
                    }
...
412
    printf("Options:\n");
416
    printf("Options:\n");
413
    printf("  -m, --model <name>    Specify model to use (default: first model)\n");
417
    printf("  -m, --model <name>    Specify model to use (default: first model)\n");
414
    printf("  -p, --prompt <text>   Specify prompt text (default: \"What is 2+2?\")\n");
418
    printf("  -p, --prompt <text>   Specify prompt text (default: \"What is 2+2?\")\n");
  
419
    printf("  -b, --build <file>    Specify context file\n");
415
    printf("  -c, --context <text>  Specify context file\n");
420
    printf("  -c, --context <text>  Specify context file\n");
416
    printf("  -v, --verbose         Enable verbose logging\n");
421
    printf("  -v, --verbose         Enable verbose logging\n");
417
    printf("  -h, --help            Show this help message\n");
422
    printf("  -h, --help            Show this help message\n");
418
}
423
}
419
  
424
  
420
int main(int argc, char **argv) {
425
int main(int argc, char **argv) {
  
426
	/* Engine engine = {}; */
  
427
  
  
428
  
421
    const char *model_name = NULL;
429
    const char *model_name = NULL;
422
    const char *prompt = NULL;
430
    const char *prompt = NULL;
423
    const char *context_file = NULL;
431
    const char *context_file = NULL;
...
429
        {"model", required_argument, 0, 'm'},
437
        {"model", required_argument, 0, 'm'},
430
        {"prompt", required_argument, 0, 'p'},
438
        {"prompt", required_argument, 0, 'p'},
431
        {"context", required_argument, 0, 'c'},
439
        {"context", required_argument, 0, 'c'},
  
440
        {"build", required_argument, 0, 'b'},
432
        {"verbose", no_argument, 0, 'v'},
441
        {"verbose", no_argument, 0, 'v'},
433
        {"help", no_argument, 0, 'h'},
442
        {"help", no_argument, 0, 'h'},
434
        {0, 0, 0, 0}
443
        {0, 0, 0, 0}
...
diff --git a/vectordb.c b/vectordb.c
1
#include <stdio.h>
1
#include <stdio.h>
2
#include <string.h>
2
#include <string.h>
3
#include <math.h>
3
#include <math.h>
  
4
#include <stdint.h>
4
  
5
  
5
#include "llama.h"
6
#include "llama.h"
6
#include "vectordb.h"
7
#include "vectordb.h"
  
8
#include "nonstd.h"
  
9
  
  
10
#define VDB_MAGIC 0x31424456u /* "VDB1" */
  
11
#define VDB_VERSION 1u
  
12
  
  
13
typedef struct {
  
14
	uint32_t magic;
  
15
	uint32_t version;
  
16
	uint32_t embed_size;
  
17
	uint32_t max_text;
  
18
	uint32_t count;
  
19
} VdbFileHeader;
7
  
20
  
8
static float cosine_similarity(float *a, float *b, int n) {
21
static float cosine_similarity(float *a, float *b, int n) {
9
	float dot = 0, normA = 0, normB = 0;
22
	float dot = 0, norm_a = 0, norm_b = 0;
10
	for (int i = 0; i < n; i++) {
23
	for (int i = 0; i < n; i++) {
11
		dot += a[i] * b[i];
24
		dot += a[i] * b[i];
12
		normA += a[i] * a[i];
25
		norm_a += a[i] * a[i];
13
		normB += b[i] * b[i];
26
		norm_b += b[i] * b[i];
14
	}
27
	}
15
	return dot / (sqrtf(normA) * sqrtf(normB) + 1e-8f);
28
	return dot / (sqrtf(norm_a) * sqrtf(norm_b) + 1e-8f);
16
}
29
}
17
  
30
  
18
static void embed_text(struct llama_context *ctx, const char *text, float *out) {
31
static void embed_text(struct llama_context *ctx, const char *text, float *out) {
19
	llama_token tokens[512];
32
	llama_token tokens[512];
20
	const struct llama_model *model = llama_get_model(ctx);
33
	const struct llama_model *model = llama_get_model(ctx);
21
	const struct llama_vocab *vocab = llama_model_get_vocab(model);
34
	const struct llama_vocab *vocab = llama_model_get_vocab(model);
22
	int n_tokens = llama_tokenize(
35
	int n_tokens = llama_tokenize(vocab, text, strlen(text), tokens, 512, true, true);
23
			vocab,
  
24
			text,
  
25
			strlen(text),
  
26
			tokens,
  
27
			512,
  
28
			true,
  
29
			true
  
30
			);
  
31
	if (n_tokens < 0) {
36
	if (n_tokens < 0) {
32
		return;
37
		return;
33
	}
38
	}
...
46
}
51
}
47
  
52
  
48
void vdb_free(VectorDB *db) {
53
void vdb_free(VectorDB *db) {
49
	(void)db; // nothing yet (future persistence etc.)
54
	(void)db;
50
}
55
}
51
  
56
  
52
void vdb_add_document(VectorDB *db, const char *text) {
57
void vdb_add_document(VectorDB *db, const char *text) {
53
	if (db->count >= VDB_MAX_DOCS) {
58
	if (db->count >= VDB_MAX_DOCS) {
54
		printf("VectorDB full!\n");
59
		log_message(stdout, LOG_INFO, "Vector database full");
55
		return;
60
		return;
56
	}
61
	}
57
  
62
  
...
59
	strncpy(doc->text, text, VDB_MAX_TEXT - 1);
64
	strncpy(doc->text, text, VDB_MAX_TEXT - 1);
60
	doc->text[VDB_MAX_TEXT - 1] = 0;
65
	doc->text[VDB_MAX_TEXT - 1] = 0;
61
  
66
  
62
	printf("Embedding doc %d...\n", db->count);
67
	log_message(stdout, LOG_INFO, "Embedding doc %d...", db->count);
63
	embed_text(db->embed_ctx, text, doc->embedding);
68
	embed_text(db->embed_ctx, text, doc->embedding);
64
}
69
}
65
  
70
  
...
90
		}
95
		}
91
	}
96
	}
92
}
97
}
  
98
  
  
99
int vdb_save(const VectorDB *db, const char *path) {
  
100
	FILE *fp = fopen(path, "wb");
  
101
	if (!fp) {
  
102
		return 1;
  
103
	}
  
104
  
  
105
	VdbFileHeader header = {
  
106
		.magic = VDB_MAGIC,
  
107
		.version = VDB_VERSION,
  
108
		.embed_size = VDB_EMBED_SIZE,
  
109
		.max_text = VDB_MAX_TEXT,
  
110
		.count = (uint32_t)db->count,
  
111
	};
  
112
  
  
113
	if (fwrite(&header, sizeof(header), 1, fp) != 1) {
  
114
		fclose(fp);
  
115
		return 2;
  
116
	}
  
117
  
  
118
	if (db->count > 0) {
  
119
		size_t wrote = fwrite(db->docs, sizeof(VectorDoc), (size_t)db->count, fp);
  
120
		if (wrote != (size_t)db->count) {
  
121
			fclose(fp);
  
122
			return 3;
  
123
		}
  
124
	}
  
125
  
  
126
	if (fclose(fp) != 0) {
  
127
		return 4;
  
128
	}
  
129
  
  
130
	return 0;
  
131
}
  
132
  
  
133
int vdb_load(VectorDB *db, const char *path) {
  
134
	struct llama_context *ctx = db->embed_ctx;
  
135
	FILE *fp = fopen(path, "rb");
  
136
	if (!fp) {
  
137
		return -1;
  
138
	}
  
139
  
  
140
	VdbFileHeader header = {0};
  
141
	if (fread(&header, sizeof(header), 1, fp) != 1) {
  
142
		fclose(fp);
  
143
		return -2;
  
144
	}
  
145
  
  
146
	if (header.magic != VDB_MAGIC || header.version != VDB_VERSION) {
  
147
		fclose(fp);
  
148
		return -3;
  
149
	}
  
150
  
  
151
	if (header.embed_size != VDB_EMBED_SIZE || header.max_text != VDB_MAX_TEXT) {
  
152
		fclose(fp);
  
153
		return -4;
  
154
	}
  
155
  
  
156
	if (header.count > VDB_MAX_DOCS) {
  
157
		fclose(fp);
  
158
		return -5;
  
159
	}
  
160
  
  
161
	memset(db, 0, sizeof(VectorDB));
  
162
	db->embed_ctx = ctx;
  
163
	db->count = (int)header.count;
  
164
  
  
165
	if (db->count > 0) {
  
166
		size_t read = fread(db->docs, sizeof(VectorDoc), (size_t)db->count, fp);
  
167
		if (read != (size_t)db->count) {
  
168
			fclose(fp);
  
169
			return -6;
  
170
		}
  
171
	}
  
172
  
  
173
	if (fclose(fp) != 0) {
  
174
		return -7;
  
175
	}
  
176
  
  
177
	return 0;
  
178
}
diff --git a/vectordb.h b/vectordb.h
...
26
void vdb_embed_query(VectorDB *db, const char *text, float *out_embedding);
26
void vdb_embed_query(VectorDB *db, const char *text, float *out_embedding);
27
void vdb_search(VectorDB *db, float *query_embedding, int top_k, int *results);
27
void vdb_search(VectorDB *db, float *query_embedding, int top_k, int *results);
28
  
28
  
  
29
int vdb_save(const VectorDB *db, const char *path);
  
30
int vdb_load(VectorDB *db, const char *path);
  
31
  
29
#endif
32
#endif