Spaces:
Running
Running
| /* context_store.c — Universal pattern-triggered context injection. | |
| * | |
| * The regex-triggered skill loading pattern generalizes: ANY context | |
| * (skills, tool docs, error recovery, codebase summaries, few-shot examples) | |
| * is a (trigger, text) pair. The store matches all triggers against the | |
| * current input and returns matching entries in priority order. | |
| * | |
| * This replaces static Pri0 with a dynamic lookup: instead of stuffing the | |
| * system prompt with all possible instructions, only inject what matches | |
| * the current request. Information capacity goes from "fits in system | |
| * prompt" to "fits on disk." | |
| * | |
| * Triggers support: exact match, substring, regex (via callback if linked | |
| * with PCRE2; otherwise substring fallback), and keyword intersection. | |
| * | |
| * Compile: | |
| * cc -O3 -std=c11 -fPIC -shared context_store.c -o libcontext_store.so | |
| */ | |
| typedef struct { | |
| char id[32]; | |
| char trigger[MAX_TRIG_LEN]; | |
| char text[MAX_TEXT_LEN]; | |
| int match_type; /* MATCH_EXACT, MATCH_SUBSTR, MATCH_KEYWORD */ | |
| int priority; /* 0=highest (error recovery), 9=lowest (general knowledge) */ | |
| int cooldown; /* skip this entry for N more calls after a hit */ | |
| } entry_t; | |
| static entry_t g_store[MAX_ENTRIES]; | |
| static int g_count = 0; | |
| static int g_hit_counts[MAX_ENTRIES]; /* for cooldown tracking */ | |
| /* ---- Public API ---- */ | |
| /* Add or update an entry. Returns index or -1 if store full. */ | |
| int store_add(const char *id, const char *trigger, const char *text, | |
| int match_type, int priority) { | |
| /* Update existing */ | |
| for (int i = 0; i < g_count; i++) { | |
| if (strcmp(g_store[i].id, id) == 0) { | |
| strncpy(g_store[i].trigger, trigger, MAX_TRIG_LEN-1); | |
| strncpy(g_store[i].text, text, MAX_TEXT_LEN-1); | |
| g_store[i].match_type = match_type; | |
| g_store[i].priority = priority; | |
| return i; | |
| } | |
| } | |
| if (g_count >= MAX_ENTRIES) return -1; | |
| int i = g_count++; | |
| strncpy(g_store[i].id, id, 31); | |
| strncpy(g_store[i].trigger, trigger, MAX_TRIG_LEN-1); | |
| strncpy(g_store[i].text, text, MAX_TEXT_LEN-1); | |
| g_store[i].match_type = match_type; | |
| g_store[i].priority = priority; | |
| g_store[i].cooldown = 0; | |
| g_hit_counts[i] = 0; | |
| return i; | |
| } | |
| void store_remove(const char *id) { | |
| for (int i = 0; i < g_count; i++) { | |
| if (strcmp(g_store[i].id, id) == 0) { | |
| /* Shift remaining entries down */ | |
| memmove(&g_store[i], &g_store[i+1], (g_count - i - 1) * sizeof(entry_t)); | |
| memmove(&g_hit_counts[i], &g_hit_counts[i+1], (g_count - i - 1) * sizeof(int)); | |
| g_count--; | |
| return; | |
| } | |
| } | |
| } | |
| /* Case-insensitive substring match */ | |
| static int ci_substr(const char *haystack, const char *needle) { | |
| if (!*needle) return 1; | |
| while (*haystack) { | |
| const char *h = haystack, *n = needle; | |
| while (*h && *n && tolower(*h) == tolower(*n)) { h++; n++; } | |
| if (!*n) return 1; | |
| haystack++; | |
| } | |
| return 0; | |
| } | |
| /* Check if a word from needle appears in haystack */ | |
| static int keyword_match(const char *haystack, const char *trigger) { | |
| char word[64]; | |
| const char *p = trigger; | |
| while (*p) { | |
| while (*p == ' ' || *p == ',') p++; | |
| if (!*p) break; | |
| int len = 0; | |
| while (p[len] && p[len] != ' ' && p[len] != ',' && len < 63) len++; | |
| memcpy(word, p, len); word[len] = '\0'; | |
| p += len; | |
| /* Check if this word appears in haystack */ | |
| const char *h = haystack; | |
| while (*h) { | |
| while (*h == ' ') h++; | |
| const char *w = word; | |
| const char *s = h; | |
| while (*s && *w && tolower(*s) == tolower(*w)) { s++; w++; } | |
| if (!*w && (!*s || *s == ' ' || *s == '\n')) return 1; | |
| while (*h && *h != ' ') h++; | |
| } | |
| } | |
| return 0; | |
| } | |
| /* Match an entry against input. Returns 1 if match. */ | |
| static int entry_matches(const entry_t *e, const char *input) { | |
| switch (e->match_type) { | |
| case MATCH_EXACT: return strcmp(input, e->trigger) == 0; | |
| case MATCH_SUBSTR: return ci_substr(input, e->trigger); | |
| case MATCH_KEYWORD: return keyword_match(input, e->trigger); | |
| default: return 0; | |
| } | |
| } | |
| /* Query the store. Returns matching entries in priority order as a | |
| * concatenated string. max_tokens limits total output (est. 4 chars/tok). | |
| * excluded_ids is a | separated list of ids to skip (already active). | |
| * Copies result to buf (caller-owned, at least bufsz bytes). */ | |
| int store_query(const char *input, int max_tokens, const char *excluded_ids, | |
| char *buf, int bufsz) { | |
| int budget = max_tokens * 4; | |
| int total = 0; | |
| int included = 0; | |
| buf[0] = '\0'; | |
| /* Collect matching indices, sorted by priority */ | |
| int matches[MAX_ENTRIES]; | |
| int nm = 0; | |
| for (int i = 0; i < g_count; i++) { | |
| if (g_store[i].cooldown > 0) { g_store[i].cooldown--; continue; } | |
| if (!entry_matches(&g_store[i], input)) continue; | |
| /* Check exclusion */ | |
| if (excluded_ids && *excluded_ids) { | |
| char ex_copy[512]; | |
| strncpy(ex_copy, excluded_ids, 511); | |
| char *tok = strtok(ex_copy, "|"); | |
| int skip = 0; | |
| while (tok) { | |
| if (strcmp(tok, g_store[i].id) == 0) { skip = 1; break; } | |
| tok = strtok(NULL, "|"); | |
| } | |
| if (skip) continue; | |
| } | |
| /* Insert sorted by priority */ | |
| int j; | |
| for (j = nm; j > 0 && g_store[matches[j-1]].priority > g_store[i].priority; j--) | |
| matches[j] = matches[j-1]; | |
| matches[j] = i; | |
| nm++; | |
| } | |
| /* Write matching entries */ | |
| for (int m = 0; m < nm; m++) { | |
| int i = matches[m]; | |
| int len = strlen(g_store[i].text); | |
| if (total + len + 2 > budget && included > 0) break; | |
| int to_write = len; | |
| if (total + to_write + 2 > budget) to_write = budget - total - 2; | |
| if (to_write <= 0) break; | |
| if (included > 0) { buf[total++] = '\n'; buf[total] = '\0'; } | |
| memcpy(buf + total, g_store[i].text, to_write); | |
| total += to_write; | |
| buf[total] = '\0'; | |
| included++; | |
| /* Set cooldown to prevent re-triggering */ | |
| g_store[i].cooldown = 3; | |
| g_hit_counts[i]++; | |
| } | |
| return included; | |
| } | |
| /* Get store stats */ | |
| int store_count(void) { return g_count; } | |
| /* Seed the store with default entries. Call once at startup. */ | |
| void store_init_defaults(void) { | |
| store_add("default-terminate", | |
| "quit exit stop shutdown", | |
| "To exit: type /exit or say 'quit'.", | |
| MATCH_KEYWORD, 9); | |
| store_add("error-oom", | |
| "out of memory OOM killed memory error allocation failed", | |
| "MEMORY FULL. Free RAM by: closing unused programs, reducing batch size, or using a smaller model. Current model: Hermes 3B Q4.", | |
| MATCH_KEYWORD, 0); | |
| store_add("error-timeout", | |
| "timed out timeout hang frozen stuck", | |
| "TIMEOUT DETECTED. The last operation took too long. Try: --no-mmap for faster loading, reduce max_tokens, or simplify the request.", | |
| MATCH_KEYWORD, 0); | |
| store_add("error-syntax", | |
| "syntax error compilation error parse error unexpected token", | |
| "SYNTAX ERROR. Check: missing semicolons, unmatched braces, wrong variable types. Compile with -Wall to see all warnings.", | |
| MATCH_KEYWORD, 1); | |
| store_add("tool-terminal", | |
| "terminal shell bash command cmd execute run", | |
| "TERMINAL: use terminal('command') tool. Always quote paths with spaces. Set timeout for long operations. Check exit code before proceeding.", | |
| MATCH_KEYWORD, 3); | |
| store_add("tool-file", | |
| "file read write save open edit", | |
| "FILES: use read_file(path) and write_file(path,content). Never write outside the project directory. Check file exists before reading.", | |
| MATCH_KEYWORD, 3); | |
| store_add("tool-git", | |
| "git commit push pull branch merge rebase", | |
| "GIT: use terminal with git commands. NEVER force push to main. Create feature branches. Commit messages should be descriptive.", | |
| MATCH_KEYWORD, 3); | |
| store_add("pattern-search", | |
| "search find grep locate look for", | |
| "SEARCH: use search_files(pattern) for content, or terminal('find ...') for filenames. Be specific in patterns to reduce noise.", | |
| MATCH_KEYWORD, 4); | |
| } |