33#include <readline/readline.h>
34#include <readline/history.h>
40#define CK_CLI_VERSION "6.6.0"
41#define CK_CLI_DEFAULT_MAX_TOKENS 256
42#define CK_CLI_EOS_MAX 8
43#define CK_CLI_OUTPUT_BUF_SIZE 4096
44#define CK_CLI_MAX_CONTEXT 32768
45#define CK_CLI_HISTORY_FILE ".ck_cli_history"
70typedef int (*
init_t)(
const char *weights_path);
71typedef int (*
embed_t)(
const int32_t *tokens,
int num_tokens);
77typedef float *(*get_logits_t)(void);
79typedef void *(*get_ptr_t)(void);
118 const char *system_prefix;
119 const char *system_suffix;
120 const char *user_prefix;
121 const char *user_suffix;
122 const char *assistant_prefix;
123 const char *assistant_suffix;
129 .system_prefix =
"", .system_suffix =
"\n",
130 .user_prefix =
"", .user_suffix =
"\n",
131 .assistant_prefix =
"", .assistant_suffix =
"",
135 .system_prefix =
"<|im_start|>system\n",
136 .system_suffix =
"<|im_end|>\n",
137 .user_prefix =
"<|im_start|>user\n",
138 .user_suffix =
"<|im_end|>\n",
139 .assistant_prefix =
"<|im_start|>assistant\n",
140 .assistant_suffix =
"<|im_end|>",
144 .system_prefix =
"[INST] <<SYS>>\n",
145 .system_suffix =
"\n<</SYS>>\n\n",
147 .user_suffix =
" [/INST]",
148 .assistant_prefix =
" ",
149 .assistant_suffix =
" </s><s>[INST] ",
153 .system_prefix =
"<|im_start|>system\n",
154 .system_suffix =
"<|im_end|>\n",
155 .user_prefix =
"<|im_start|>user\n",
156 .user_suffix =
"<|im_end|>\n",
157 .assistant_prefix =
"<|im_start|>assistant\n",
158 .assistant_suffix =
"<|im_end|>",
163 .system_suffix =
"\n\n",
164 .user_prefix =
"[INST] ",
165 .user_suffix =
" [/INST]",
166 .assistant_prefix =
"",
167 .assistant_suffix =
"</s> ",
176 const char *model_name;
177 const char *lib_path;
178 const char *weights_path;
179 const char *prompt_once;
180 const char *system_prompt;
182 int context_override;
189 bool no_chat_template;
200 static char cache_path[4096];
201 const char *home = getenv(
"HOME");
202 if (!home) home =
"/tmp";
203 snprintf(cache_path,
sizeof(cache_path),
"%s/.cache/ck-engine-v6.6/models", home);
207static bool find_model_in_cache(
const char *model_name,
char *lib_out,
char *weights_out,
size_t out_size) {
209 DIR *dir = opendir(cache_dir);
210 if (!dir)
return false;
212 struct dirent *entry;
213 while ((entry = readdir(dir)) != NULL) {
214 if (entry->d_name[0] ==
'.')
continue;
217 if (strstr(entry->d_name, model_name) != NULL) {
218 char model_dir[4096];
219 snprintf(model_dir,
sizeof(model_dir),
"%s/%s", cache_dir, entry->d_name);
222 char so_path[4096], bump_path[4096];
223 snprintf(so_path,
sizeof(so_path),
"%s/ck-kernel-inference.so", model_dir);
224 snprintf(bump_path,
sizeof(bump_path),
"%s/weights.bump", model_dir);
227 if (stat(so_path, &st) == 0 && stat(bump_path, &st) == 0) {
228 strncpy(lib_out, so_path, out_size - 1);
229 strncpy(weights_out, bump_path, out_size - 1);
244 if (!weights_path || !opt)
return false;
247 char vocab_path[4096];
248 const char *slash = strrchr(weights_path,
'/');
249 if (!slash)
return false;
251 size_t dir_len = (size_t)(slash - weights_path);
252 if (dir_len + 12 >=
sizeof(vocab_path))
return false;
254 memcpy(vocab_path, weights_path, dir_len);
255 vocab_path[dir_len] =
'\0';
256 strcat(vocab_path,
"/vocab.json");
258 FILE *f = fopen(vocab_path,
"r");
259 if (!f)
return false;
263 size_t n = fread(buf, 1,
sizeof(buf) - 1, f);
268 const char *st = strstr(buf,
"\"special_tokens\"");
269 if (!st)
return false;
272 const char *
eos = strstr(st,
"\"eos\"");
274 const char *colon = strchr(
eos,
':');
276 int eos_id = atoi(colon + 1);
278 opt->eos_ids[0] = eos_id;
285 const char *
bos = strstr(st,
"\"bos\"");
287 const char *colon = strchr(
bos,
':');
289 int bos_id = atoi(colon + 1);
290 if (bos_id > 0 && bos_id != opt->eos_ids[0]) {
291 opt->eos_ids[opt->eos_count++] = bos_id;
296 return opt->eos_count > 0;
301 DIR *dir = opendir(cache_dir);
303 fprintf(stderr,
"No models found in %s\n", cache_dir);
307 printf(
"Available models in %s:\n", cache_dir);
308 struct dirent *entry;
310 while ((entry = readdir(dir)) != NULL) {
311 if (entry->d_name[0] ==
'.')
continue;
313 char model_dir[4096];
314 snprintf(model_dir,
sizeof(model_dir),
"%s/%s", cache_dir, entry->d_name);
317 snprintf(so_path,
sizeof(so_path),
"%s/ck-kernel-inference.so", model_dir);
320 if (stat(so_path, &st) == 0) {
321 printf(
" - %s\n", entry->d_name);
328 printf(
" (none found)\n");
337 if (temperature <= 0.0f || top_p <= 0.0f) {
340 float best_val = logits[0];
342 if (logits[i] > best_val) {
343 best_val = logits[i];
351 float max_logit = logits[0];
353 if (logits[i] > max_logit) max_logit = logits[i];
358 logits[i] = expf((logits[i] - max_logit) / temperature);
370 float threshold = (float)rand() / (float)RAND_MAX * top_p;
373 int *indices = (
int *)malloc(
vocab_size *
sizeof(
int));
374 float *probs = (
float *)malloc(
vocab_size *
sizeof(
float));
377 probs[i] = logits[i];
383 if (probs[j] > probs[i]) {
384 float tmp_p = probs[i]; probs[i] = probs[j]; probs[j] = tmp_p;
385 int tmp_i = indices[i]; indices[i] = indices[j]; indices[j] = tmp_i;
389 if (cumsum >= top_p)
break;
393 float r = (float)rand() / (float)RAND_MAX * cumsum;
395 int result = indices[0];
396 for (
int i = 0; cumsum > 0 && i <
vocab_size; i++) {
402 if (acc >= cumsum)
break;
430 if (!
token || max <= 0)
return 0;
432 const unsigned char *src = (
const unsigned char *)
token;
435 while (*src &&
out_len < max - 1) {
436 unsigned int codepoint;
440 if ((src[0] & 0x80) == 0) {
444 }
else if ((src[0] & 0xE0) == 0xC0 && (src[1] & 0xC0) == 0x80) {
446 codepoint = ((src[0] & 0x1F) << 6) | (src[1] & 0x3F);
448 }
else if ((src[0] & 0xF0) == 0xE0 && (src[1] & 0xC0) == 0x80 && (src[2] & 0xC0) == 0x80) {
450 codepoint = ((src[0] & 0x0F) << 12) | ((src[1] & 0x3F) << 6) | (src[2] & 0x3F);
452 }
else if ((src[0] & 0xF8) == 0xF0 && (src[1] & 0xC0) == 0x80 &&
453 (src[2] & 0xC0) == 0x80 && (src[3] & 0xC0) == 0x80) {
455 codepoint = ((src[0] & 0x07) << 18) | ((src[1] & 0x3F) << 12) |
456 ((src[2] & 0x3F) << 6) | (src[3] & 0x3F);
466 if (codepoint >= 0x100 && codepoint <= 0x120) {
468 out[
out_len++] = (char)(codepoint - 0x100);
469 }
else if (codepoint >= 0x17F && codepoint <= 0x1A0) {
471 out[
out_len++] = (char)(codepoint - 0x100);
472 }
else if (codepoint < 0x80) {
474 out[
out_len++] = (char)codepoint;
475 }
else if (codepoint == 0x2581) {
480 for (
int i = 0; i < bytes &&
out_len < max - 1; i++) {
493 if (*len == 0)
return;
494 fwrite(buf, 1, *len, stdout);
500 size_t n = strlen(
text);
505 fwrite(
text, 1, n, stdout);
508 memcpy(buf + *len,
text, n);
513 if (!
token)
return false;
514 const unsigned char *p = (
const unsigned char *)
token;
516 if ((p[0] & 0x80) == 0) {
520 if ((p[0] & 0xE0) == 0xC0 && (p[1] & 0xC0) == 0x80) {
521 unsigned int cp = ((p[0] & 0x1F) << 6) | (p[1] & 0x3F);
522 if ((cp >= 0x100 && cp <= 0x120) || (cp >= 0x17F && cp <= 0x1A0)) {
528 if ((p[0] & 0xF0) == 0xE0 && (p[1] & 0xC0) == 0x80 && (p[2] & 0xC0) == 0x80) {
532 if ((p[0] & 0xF8) == 0xF0 && (p[1] & 0xC0) == 0x80 &&
533 (p[2] & 0xC0) == 0x80 && (p[3] & 0xC0) == 0x80) {
543 if (!tokenizer ||
vocab_size <= 0)
return false;
545 for (
int i = 0; i < limit; i++) {
574static bool resolve_symbol(
void *handle,
const char *name,
void **out_ptr,
bool required) {
575 void *sym = dlsym(handle, name);
576 if (!sym && required) {
577 fprintf(stderr,
"Error: missing symbol %s\n", name);
580 if (out_ptr) *out_ptr = sym;
585 if (!lib_path || !api)
return false;
586 memset(api, 0,
sizeof(*api));
587 api->handle = dlopen(lib_path, RTLD_NOW);
589 fprintf(stderr,
"Error: dlopen failed: %s\n", dlerror());
593 if (!
resolve_symbol(api->handle,
"ck_model_init", (
void **)&api->init,
true))
return false;
594 if (!
resolve_symbol(api->handle,
"ck_model_embed_tokens", (
void **)&api->embed,
true))
return false;
595 if (!
resolve_symbol(api->handle,
"ck_model_forward", (
void **)&api->forward,
true))
return false;
596 if (!
resolve_symbol(api->handle,
"ck_model_decode", (
void **)&api->decode,
true))
return false;
597 resolve_symbol(api->handle,
"ck_model_sample_argmax", (
void **)&api->sample,
false);
598 resolve_symbol(api->handle,
"ck_model_get_logits", (
void **)&api->get_logits,
false);
599 resolve_symbol(api->handle,
"ck_model_get_logits_stride", (
void **)&api->get_logits_stride,
false);
600 resolve_symbol(api->handle,
"ck_model_kv_cache_enable", (
void **)&api->kv_enable,
false);
601 resolve_symbol(api->handle,
"ck_model_kv_cache_reset", (
void **)&api->kv_reset,
false);
602 resolve_symbol(api->handle,
"ck_model_get_context_window", (
void **)&api->get_context,
false);
603 resolve_symbol(api->handle,
"ck_model_get_vocab_size", (
void **)&api->get_vocab_size,
false);
604 resolve_symbol(api->handle,
"ck_model_get_num_merges", (
void **)&api->get_num_merges,
false);
605 resolve_symbol(api->handle,
"ck_model_get_vocab_strings_size", (
void **)&api->get_vocab_bytes,
false);
606 resolve_symbol(api->handle,
"ck_model_get_active_tokens", (
void **)&api->get_active_tokens,
false);
607 resolve_symbol(api->handle,
"ck_model_get_vocab_offsets", (
void **)&api->get_offsets,
false);
608 resolve_symbol(api->handle,
"ck_model_get_vocab_strings", (
void **)&api->get_strings,
false);
609 resolve_symbol(api->handle,
"ck_model_get_vocab_merges", (
void **)&api->get_merges,
false);
610 resolve_symbol(api->handle,
"ck_model_free", (
void **)&api->free_fn,
false);
612 if (!api->get_vocab_size || !api->get_offsets || !api->get_strings) {
613 fprintf(stderr,
"Error: vocab accessors missing from model\n");
628 strncpy(lower, model_name,
sizeof(lower) - 1);
629 for (
char *p = lower; *p; p++) *p = (*p >=
'A' && *p <=
'Z') ? *p + 32 : *p;
640 if (system && *system) {
641 needed += strlen(tmpl->system_prefix) + strlen(system) + strlen(tmpl->system_suffix);
643 needed += strlen(tmpl->user_prefix) + strlen(user) + strlen(tmpl->user_suffix);
644 needed += strlen(tmpl->assistant_prefix);
647 char *result = (
char *)malloc(needed);
648 if (!result)
return NULL;
651 if (system && *system) {
652 strcat(result, tmpl->system_prefix);
653 strcat(result, system);
654 strcat(result, tmpl->system_suffix);
656 strcat(result, tmpl->user_prefix);
657 strcat(result, user);
658 strcat(result, tmpl->user_suffix);
659 strcat(result, tmpl->assistant_prefix);
669 if (!opt || opt->ignore_eos)
return false;
670 for (
int i = 0; i < opt->eos_count; i++) {
671 if (opt->eos_ids[i] ==
token)
return true;
685#define EOS_PATTERN_BUF_SIZE 64
686#define EOS_PENDING_MAX 8
693 const char *target_pattern;
694 const char *partial_prefix;
702 for (
int i = 0; i <
g_eos_state.pending_count; i++) {
736 size_t tlen = strlen(
token);
741 if (target_len == 0)
return false;
747 memcpy(temp + plen,
token, tlen);
748 temp[plen + tlen] =
'\0';
752 size_t temp_len = plen + tlen;
755 for (
size_t i = 0; i < temp_len; i++) {
756 size_t remaining = temp_len - i;
757 if (remaining > target_len) remaining = target_len;
758 if (strncmp(temp + i, target, remaining) == 0) {
776 void (*output_fn)(
char*,
size_t*,
const char*),
780 if (token_text && output_fn) output_fn(out_buf,
out_len, token_text);
785 size_t tlen = strlen(token_text);
810 for (
int i = 0; i <
g_eos_state.pending_count; i++) {
819 if (output_fn) output_fn(out_buf,
out_len, token_text);
824 if (!arg || !opt)
return false;
829 long v = strtol(p, &
end, 10);
831 opt->eos_ids[opt->eos_count++] = (int)v;
835 return opt->eos_count > 0;
842static int run_prompt(ModelAPI *api, CKTrueBPE *tokenizer, CLIOptions *opt,
const char *input) {
843 if (!api || !tokenizer || !opt || !input)
return -1;
846 int ctx = opt->context_override;
847 if (ctx <= 0 && api->get_context) ctx = api->get_context();
848 if (ctx <= 0) ctx = 4096;
857 fprintf(stderr,
"Error: failed to format prompt\n");
862 printf(
"[DEBUG] Formatted prompt:\n%s\n", formatted);
865 int32_t *
ids = (int32_t *)malloc((
size_t)ctx *
sizeof(int32_t));
867 fprintf(stderr,
"Error: failed to allocate token buffer\n");
876 fprintf(stderr,
"[Tokenizer] failed to encode prompt\n");
880 if (n > ctx - max_tokens) {
881 n = ctx - max_tokens;
883 printf(
"[DEBUG] Truncated prompt to %d tokens\n", n);
892 if (api->kv_reset) api->kv_reset();
894 if (api->embed(
ids, n) != 0) {
895 fprintf(stderr,
"[Model] embed failed\n");
900 struct timespec t0, t1;
901 clock_gettime(CLOCK_MONOTONIC, &t0);
902 if (api->forward(NULL) != 0) {
903 fprintf(stderr,
"[Model] forward failed\n");
907 clock_gettime(CLOCK_MONOTONIC, &t1);
909 (t1.tv_nsec - t0.tv_nsec) / 1000000.0;
912 int vocab_size = api->get_vocab_size ? api->get_vocab_size() : 0;
915 #define SAMPLE_NEXT_TOKEN() do { \
916 if (api->get_logits && vocab_size > 0) { \
917 float *logits = api->get_logits(); \
919 int stride = api->get_logits_stride ? api->get_logits_stride() : vocab_size; \
920 int active = api->get_active_tokens ? api->get_active_tokens() : 1; \
921 float *last_logits = logits; \
923 if (active < 1) active = 1; \
924 last_logits = logits + (size_t)(active - 1) * (size_t)stride; \
926 float *logits_copy = (float *)malloc(vocab_size * sizeof(float)); \
927 memcpy(logits_copy, last_logits, vocab_size * sizeof(float)); \
928 next_token = sample_top_p(logits_copy, vocab_size, opt->temperature, opt->top_p); \
930 } else if (api->sample) { \
931 next_token = api->sample(); \
935 } else if (api->sample) { \
936 next_token = api->sample(); \
955 if (next_token < 0)
break;
959 fprintf(stderr,
"[DEBUG] Token %d: %d (%s)\n", generated, next_token, tok_str ? tok_str :
"NULL");
964 fprintf(stderr,
"[DEBUG] EOS detected (token ID), stopping\n");
972 if (!opt->ignore_eos &&
975 fprintf(stderr,
"[DEBUG] EOS detected (text pattern), stopping\n");
988 if (generated + 1 >= max_tokens)
break;
990 clock_gettime(CLOCK_MONOTONIC, &t0);
991 if (api->decode(next_token, NULL) != 0) {
992 fprintf(stderr,
"\n[Model] decode failed\n");
995 clock_gettime(CLOCK_MONOTONIC, &t1);
997 (t1.tv_nsec - t0.tv_nsec) / 1000000.0;
1004 #undef SAMPLE_NEXT_TOKEN
1017 printf(
"decode: %3d tok / %7.1f ms (%5.1f tok/s, %5.1f ms/tok)\033[0m\n",
1032 printf(
" \033[1;36mC-Kernel-Engine v%s\033[0m\n",
CK_CLI_VERSION);
1033 printf(
" Native inference CLI with true-BPE tokenization\n");
1039 fprintf(stderr,
"Usage:\n");
1040 fprintf(stderr,
" %s --model <name> Auto-discover model from cache\n", prog);
1041 fprintf(stderr,
" %s <libmodel.so> <weights.bump> Direct paths\n", prog);
1042 fprintf(stderr,
" %s --lib <.so> --weights <.bump> Named arguments\n", prog);
1043 fprintf(stderr,
"\nOptions:\n");
1044 fprintf(stderr,
" --model, -m NAME Model name (searches in cache)\n");
1045 fprintf(stderr,
" --lib PATH Path to compiled model .so\n");
1046 fprintf(stderr,
" --weights PATH Path to weights .bump file\n");
1047 fprintf(stderr,
" --prompt, -p TEXT Run single prompt (non-interactive)\n");
1048 fprintf(stderr,
" --system, -S TEXT System prompt\n");
1050 fprintf(stderr,
" --context, -c N Override context/KV cache size\n");
1051 fprintf(stderr,
" --temperature, -T F Sampling temperature (default: 0.0 = greedy)\n");
1052 fprintf(stderr,
" --top-p F Nucleus sampling top-p (default: 0.9)\n");
1053 fprintf(stderr,
" --stream, -s Stream tokens as generated\n");
1054 fprintf(stderr,
" --timing, -t Show timing breakdown\n");
1055 fprintf(stderr,
" --no-chat-template Disable chat template formatting\n");
1056 fprintf(stderr,
" --eos IDS Comma-separated EOS token IDs\n");
1057 fprintf(stderr,
" --ignore-eos Do not stop on EOS tokens\n");
1058 fprintf(stderr,
" --list List available models\n");
1059 fprintf(stderr,
" --verbose, -v Verbose output\n");
1060 fprintf(stderr,
" --help, -h Show this help\n");
1061 fprintf(stderr,
"\nREPL Commands:\n");
1062 fprintf(stderr,
" /exit, /quit Exit the REPL\n");
1063 fprintf(stderr,
" /reset Reset KV cache\n");
1064 fprintf(stderr,
" /timing Toggle timing display\n");
1065 fprintf(stderr,
" /temp <value> Set temperature\n");
1066 fprintf(stderr,
" /system <text> Set system prompt\n");
1067 fprintf(stderr,
" /help Show help\n");
1071 if (!opt)
return false;
1072 memset(opt, 0,
sizeof(*opt));
1074 opt->temperature = 0.0f;
1079 opt->eos_ids[0] = 151643;
1080 opt->eos_ids[1] = 151645;
1081 opt->eos_ids[2] = 151644;
1084 for (
int i = 1; i < argc; i++) {
1085 const char *arg = argv[i];
1087 if (!strcmp(arg,
"--help") || !strcmp(arg,
"-h")) {
1090 }
else if (!strcmp(arg,
"--list")) {
1093 }
else if ((!strcmp(arg,
"--model") || !strcmp(arg,
"-m")) && i + 1 < argc) {
1094 opt->model_name = argv[++i];
1095 }
else if (!strcmp(arg,
"--lib") && i + 1 < argc) {
1096 opt->lib_path = argv[++i];
1097 }
else if (!strcmp(arg,
"--weights") && i + 1 < argc) {
1098 opt->weights_path = argv[++i];
1099 }
else if ((!strcmp(arg,
"--prompt") || !strcmp(arg,
"-p")) && i + 1 < argc) {
1100 opt->prompt_once = argv[++i];
1101 }
else if ((!strcmp(arg,
"--system") || !strcmp(arg,
"-S")) && i + 1 < argc) {
1102 opt->system_prompt = argv[++i];
1103 }
else if ((!strcmp(arg,
"--max-tokens") || !strcmp(arg,
"-n")) && i + 1 < argc) {
1104 opt->max_tokens = atoi(argv[++i]);
1105 }
else if ((!strcmp(arg,
"--context") || !strcmp(arg,
"-c")) && i + 1 < argc) {
1106 opt->context_override = atoi(argv[++i]);
1107 }
else if ((!strcmp(arg,
"--temperature") || !strcmp(arg,
"-T")) && i + 1 < argc) {
1108 opt->temperature = (float)atof(argv[++i]);
1109 }
else if (!strcmp(arg,
"--top-p") && i + 1 < argc) {
1110 opt->top_p = (float)atof(argv[++i]);
1111 }
else if (!strcmp(arg,
"--stream") || !strcmp(arg,
"-s")) {
1113 }
else if (!strcmp(arg,
"--no-stream")) {
1114 opt->stream =
false;
1115 }
else if (!strcmp(arg,
"--timing") || !strcmp(arg,
"-t")) {
1117 }
else if (!strcmp(arg,
"--no-timing")) {
1118 opt->timing =
false;
1119 }
else if (!strcmp(arg,
"--no-chat-template")) {
1120 opt->no_chat_template =
true;
1121 }
else if (!strcmp(arg,
"--eos") && i + 1 < argc) {
1123 }
else if (!strcmp(arg,
"--ignore-eos")) {
1124 opt->ignore_eos =
true;
1125 }
else if (!strcmp(arg,
"--verbose") || !strcmp(arg,
"-v")) {
1126 opt->verbose =
true;
1127 }
else if (arg[0] !=
'-') {
1128 if (!opt->lib_path) opt->lib_path = arg;
1129 else if (!opt->weights_path) opt->weights_path = arg;
1131 fprintf(stderr,
"Unknown argument: %s\n", arg);
1135 fprintf(stderr,
"Unknown option: %s\n", arg);
1141 if (opt->model_name && (!opt->lib_path || !opt->weights_path)) {
1142 static char lib_buf[4096], weights_buf[4096];
1144 opt->lib_path = lib_buf;
1145 opt->weights_path = weights_buf;
1147 fprintf(stderr,
"Error: model '%s' not found in cache\n", opt->model_name);
1148 fprintf(stderr,
"Run with --list to see available models\n");
1153 if (!opt->lib_path || !opt->weights_path) {
1159 const char *name_for_template = opt->model_name ? opt->model_name : opt->lib_path;
1165 printf(
"[DEBUG] Loaded %d EOS tokens: ", opt->eos_count);
1166 for (
int i = 0; i < opt->eos_count; i++) {
1167 printf(
"%d ", opt->eos_ids[i]);
1181 if (!line || line[0] !=
'/')
return false;
1183 if (!strncmp(line,
"/exit", 5) || !strncmp(line,
"/quit", 5)) {
1187 if (!strncmp(line,
"/help", 5)) {
1188 printf(
"REPL Commands:\n");
1189 printf(
" /exit, /quit Exit\n");
1190 printf(
" /reset Reset KV cache\n");
1191 printf(
" /timing Toggle timing display\n");
1192 printf(
" /temp <value> Set temperature (0 = greedy)\n");
1193 printf(
" /top-p <value> Set top-p\n");
1194 printf(
" /system <text> Set system prompt\n");
1195 printf(
" /clear Clear system prompt\n");
1196 printf(
" /verbose Toggle verbose mode\n");
1199 if (!strncmp(line,
"/reset", 6)) {
1200 if (api->kv_reset) {
1202 printf(
"[KV cache reset]\n");
1206 if (!strncmp(line,
"/timing", 7)) {
1207 opt->timing = !opt->timing;
1208 printf(
"[Timing %s]\n", opt->timing ?
"enabled" :
"disabled");
1211 if (!strncmp(line,
"/verbose", 8)) {
1212 opt->verbose = !opt->verbose;
1213 printf(
"[Verbose %s]\n", opt->verbose ?
"enabled" :
"disabled");
1216 if (!strncmp(line,
"/temp ", 6)) {
1217 opt->temperature = (float)atof(line + 6);
1218 printf(
"[Temperature set to %.2f]\n", opt->temperature);
1221 if (!strncmp(line,
"/top-p ", 7)) {
1222 opt->top_p = (float)atof(line + 7);
1223 printf(
"[Top-p set to %.2f]\n", opt->top_p);
1226 if (!strncmp(line,
"/system ", 8)) {
1227 opt->system_prompt = strdup(line + 8);
1228 printf(
"[System prompt set]\n");
1231 if (!strncmp(line,
"/clear", 6)) {
1232 opt->system_prompt = NULL;
1233 printf(
"[System prompt cleared]\n");
1237 printf(
"Unknown command: %s\n", line);
1247 srand((
unsigned int)time(NULL));
1255 printf(
"Loading: %s\n", opt.lib_path);
1262 printf(
"Initializing model...\n");
1263 if (api.init(opt.weights_path) != 0) {
1264 fprintf(stderr,
"Error: ck_model_init failed\n");
1268 int ctx = opt.context_override;
1269 if (ctx <= 0 && api.get_context) ctx = api.get_context();
1270 if (api.kv_enable && ctx > 0) {
1276 fprintf(stderr,
"[Tokenizer] failed to create\n");
1280 int vocab_size = api.get_vocab_size ? api.get_vocab_size() : 0;
1281 int vocab_bytes = api.get_vocab_bytes ? api.get_vocab_bytes() : 0;
1282 int num_merges = api.get_num_merges ? api.get_num_merges() : 0;
1283 const int32_t *
offsets = (
const int32_t *)api.get_offsets();
1284 const char *
strings = (
const char *)api.get_strings();
1285 const int32_t *
merges = api.get_merges ? (
const int32_t *)api.get_merges() : NULL;
1288 fprintf(stderr,
"[Tokenizer] missing vocab data in model\n");
1294 fprintf(stderr,
"[Tokenizer] failed to load vocab\n");
1306 static const char *special_tokens[] = {
1308 "<|im_start|>",
"<|im_end|>",
"<|endoftext|>",
1310 "<|eot_id|>",
"<|begin_of_text|>",
"<|end_of_text|>",
1311 "<|start_header_id|>",
"<|end_header_id|>",
1313 "</s>",
"<s>",
"<pad>",
"<unk>",
1317 for (
int i = 0; special_tokens[i] != NULL; i++) {
1321 if (check && strcmp(check, special_tokens[i]) == 0) {
1325 printf(
"[Tokenizer] Registered special: %s -> %d\n", special_tokens[i],
id);
1330 printf(
"[Tokenizer] Registered %d special tokens for pre-BPE matching\n", registered);
1339 printf(
"Ready! Vocab: %d, Context: %d, Template: %s\n",
1341 opt.no_chat_template ?
"none" :
1348 printf(
"[Hardware] %s | Vector: %d-bit | FMA: %s | AI Accel: %s | Kernel: %s\n",
1349 cap.name, cap.width, cap.has_fma ?
"Yes" :
"No",
1350 cap.has_ai_accel ?
"Yes" :
"No", cap.best_kernel);
1352 printf(
"Type /help for commands, Ctrl+C to stop generation\n\n");
1354 setvbuf(stdout, NULL, _IOFBF, 1 << 20);
1356 if (opt.prompt_once) {
1357 run_prompt(&api, tokenizer, &opt, opt.prompt_once);
1361 char *home = getenv(
"HOME");
1362 char history_path[4096];
1365 read_history(history_path);
1371 char *line = readline(
"\033[1;32mYou:\033[0m ");
1373 if (*line) add_history(line);
1375 printf(
"\033[1;32mYou:\033[0m ");
1377 char line_buf[4096];
1378 if (!fgets(line_buf,
sizeof(line_buf), stdin)) {
1380 if (errno == EINTR)
break;
1384 size_t len = strlen(line_buf);
1385 if (len > 0 && line_buf[len-1] ==
'\n') line_buf[len-1] =
'\0';
1386 char *line = line_buf;
1389 if (line[0] ==
'\0') {
1396 if (line[0] ==
'/') {
1404 printf(
"\033[1;34mAssistant:\033[0m ");
1415 write_history(history_path);
1421 if (api.free_fn) api.free_fn();
1422 if (api.handle) dlclose(api.handle);
1424 printf(
"\nGoodbye!\n");
#define EOS_PATTERN_BUF_SIZE
static bool parse_eos_ids(const char *arg, CLIOptions *opt)
int(* init_t)(const char *weights_path)
float *(* get_logits_t)(void)
static bool detect_gpt2_byte_fallback(CKTrueBPE *tokenizer, int vocab_size)
static bool resolve_symbol(void *handle, const char *name, void **out_ptr, bool required)
static double g_decode_time_ms
static void handle_sigint(int sig)
int(* embed_t)(const int32_t *tokens, int num_tokens)
int(* kv_enable_t)(int capacity)
static int sample_top_p(float *logits, int vocab_size, float temperature, float top_p)
static bool load_eos_from_vocab_json(const char *weights_path, CLIOptions *opt)
static double g_prefill_time_ms
static bool find_model_in_cache(const char *model_name, char *lib_out, char *weights_out, size_t out_size)
int main(int argc, char **argv)
static void print_help(const char *prog)
static ChatTemplateType detect_chat_template(const char *model_name)
static char * apply_chat_template(const ChatTemplate *tmpl, const char *system, const char *user)
#define CK_CLI_HISTORY_FILE
static int g_decode_count
static bool process_repl_command(const char *line, CLIOptions *opt, ModelAPI *api)
static bool is_eos_token(const CLIOptions *opt, int token)
static bool eos_is_potential_prefix(const char *token)
int(* forward_t)(float *logits_out)
static void eos_pattern_init(ChatTemplateType tmpl)
static void output_append(char *buf, size_t *len, const char *text)
static void list_available_models(void)
static volatile sig_atomic_t g_generation_active
#define CK_CLI_MAX_CONTEXT
static int decode_bpe_token(const char *token, char *out, int max)
static bool g_use_byte_decoder
int(* decode_t)(int32_t token, float *logits_out)
#define EOS_PATTERN_BUF_SIZE
static void print_banner(void)
static bool parse_args(int argc, char **argv, CLIOptions *opt)
static int run_prompt(ModelAPI *api, CKTrueBPE *tokenizer, CLIOptions *opt, const char *input)
static void eos_pattern_reset(void)
static volatile sig_atomic_t g_exit_requested
static EOSPatternState g_eos_state
#define CK_CLI_OUTPUT_BUF_SIZE
#define CK_CLI_DEFAULT_MAX_TOKENS
static void output_flush(char *buf, size_t *len)
static const ChatTemplate g_templates[]
static bool load_model_api(const char *lib_path, ModelAPI *api)
#define SAMPLE_NEXT_TOKEN()
static void output_token(char *buf, size_t *len, const char *token)
static bool eos_pattern_process(const char *token_text, char *out_buf, size_t *out_len, void(*output_fn)(char *, size_t *, const char *), ChatTemplateType tmpl)
static const char * get_cache_dir(void)
static bool token_has_gpt2_bytes(const char *token)
void *(* get_ptr_t)(void)
int(* sample_argmax_t)(void)
static int g_prompt_tokens
CPU feature detection and dispatch macros.
static ck_capability_t ck_get_capabilities(void)
Get current platform capabilities.
CPU capability information structure.
int32_t int32_t int32_t eos
const int32_t int int * out_len
int ck_true_bpe_encode(CKTrueBPE *bpe, const char *text, int text_len, int32_t *ids, int max_ids)
void ck_true_bpe_free(CKTrueBPE *bpe)
CKTrueBPE * ck_true_bpe_create(void)
int ck_true_bpe_add_special_token(CKTrueBPE *bpe, const char *token, int32_t id)
const char * ck_true_bpe_id_to_token(const CKTrueBPE *bpe, int32_t id)
int ck_true_bpe_load_binary(CKTrueBPE *bpe, int vocab_size, const int32_t *offsets, const char *strings, int num_merges, const int32_t *merges)
int32_t ck_true_bpe_lookup(const CKTrueBPE *bpe, const char *token)
int const int32_t const char int num_merges
int const int32_t const char * strings
int const int32_t const char int const int32_t * merges
int const int32_t * offsets