amber-ai
A local, offline AI co-pilot for Amber. It reads your live tables before it answers — their names, column types, attributes and row counts — so its suggestions are about your session, not about a generic K tutorial.
amber> trades:gentq 1000000
amber> \ai average price per symbol in the last hour
select avg px by sym from trades where time>.z.p-01:00amber> select last px by sym from trads
error[E0101]: Undefined variable `trads`
amber> \ai why
The name `trads` does not exist. This workspace has `trades`
(table: time:P sym:S`g px:F size:I, 1,000,000 rows) — you are one
character off. `sym` already carries the `g attribute, so the
grouped select will use the index.Four things that make it different from a chat window
Local only
The one endpoint it ever contacts is http://127.0.0.1:11434 — a
model server on your machine. No account, no API key, no telemetry, no TLS stack, no hosted
fallback. Your schema and your queries do not leave the box.
It cannot hang your REPL
Every request carries an absolute millisecond deadline covering connect, send and the whole receive loop. Tab completion gets 100 ms; a failed connect arms a 4-second circuit breaker so a REPL with no backend pays one failed connect, not one per keystroke.
It cannot break your REPL
Every entry point is trapped. No backend, no model pulled, a malformed reply, a half-open socket — each is one friendly sentence, never an exception and never a stack trace.
It does not patch Amber
It installs through the published extension
seam: three files into ext/, one into lib/, one rebuild.
./uninstall.sh puts the engine back byte-for-byte.
Install
git clone https://github.com/BonucciAndrea/amber-ai.git
cd amber-ai
./install.sh /path/to/amberThe path is optional — ./install.sh finds a sibling ../amber,
$AMBER_HOME, ~/amber, or an amber on your PATH. It
then checks your toolchain, checks the target's version and extension ABI (and refuses politely if they
do not match), copies src/net.c, src/net.h and src/ai_ext.c into
ext/ plus lib/ai.k into lib/, runs the engine's own
build.sh, verifies the `ai / `aio verbs and \ai
against Amber's own suite, and probes 127.0.0.1:11434 — distinguishing nothing
listening, a live Ollama, a live Ollama with no models pulled, and something
else squatting on the port, each with its own fix.
Then start a model
Any local backend that speaks Ollama, llama.cpp or an OpenAI-compatible /v1 endpoint
will do:
curl -fsSL https://ollama.com/install.sh | sh # linux / wsl2 (macOS: brew install ollama)
ollama serve &
ollama pull qwen2.5-coder:0.5b # ~400 MB, runs on a laptop CPUOr run ./setup-ollama.sh, which walks through the same thing and asks before every
system change. Without a backend the agent is a no-op: it says so once, and the REPL
is exactly Amber.
Shell alias
alias amber-ai='AMBER_NATIVE=1 AMBER_AI=1 \
AMBER_AI_URL="http://127.0.0.1:11434/api/generate" \
AMBER_AI_TIMEOUT_MS=10000 "$AMBER_HOME/a"'
alias amber-noai='AMBER_AI=0 "$AMBER_HOME/a"' # agent off for one sessionlib/ai.k as an argument
amber $AMBER_HOME/lib/ai.k runs the agent library as a script and exits.
repl.k already loads it through lib/ext.k at startup; the alias only has to
start the REPL. And alias the launcher a, not the bare amber binary — the
binary has no stdlib and no repl.k, so \ai does not exist there.
\ai commands
| Command | What it does |
|---|---|
\ai <question> | schema-aware code generation for this session |
\ai explain <expr> | unpack a terse K expression, innermost first |
\ai why [error] | diagnose the last (or a given) error against your real tables |
\ai profile <table> | rows, column types, attributes, distinct counts + indexing advice |
\ai optimize <query> | faster vector paths for a query |
\ai status | endpoint, model, budgets, what it has learned, live workspace |
\ai memory / learn / forget | inspect / extend / erase the persistent memory |
\ai on | off | master switch (default on) |
\ai tab on | off | inline Tab suggestions (default on) |
\ai model <name> | url <endpoint> | switch model / endpoint for this session |
\ai timeout <ms> | tabtimeout <ms> | answer / Tab budgets |
\ai warm | load the model now, on its own budget, and measure your machine's tokens/sec |
\ai retry | forget a dead endpoint and try it again now |
\ai serve | start a local ollama in the background (hosts without systemd) |
\ai profile and \ai why read your actual
workspace first — the row counts, types and attributes in their output are measured, not generated.
Vector execution: Tab is layered so the network is last
And usually never reached at all.
Lexical
Globals in your workspace, table column names, \ commands,
\ai sub-commands, the Amber/K vocabulary. Instant, offline. (Amber's own — present
without this package.)
Memory
Whole lines you have actually run before, mined from
~/.amber_ai_memory.k. Instant, offline, and what makes the REPL feel like it has learned
your habits.
Model
Only when 1 and 2 find nothing, only when the agent is on, and only inside
AMBER_AI_TAB_MS (default 100 ms).
A model suggestion is rendered as dim ghost text after the cursor and is never inserted until you accept it with a second Tab (or →, or Ctrl-F). Any other keystroke discards it. Accepted suggestions are recorded as feedback.
amber> select avg px by sym from tr█ades where sym=`AAPL <- dim = suggested, not typedPersistent memory
A local model cannot be fine-tuned while you type, so "learning" here means persistent
few-shot context. Every line that ran cleanly, every schema this workspace has held and every
accepted Tab suggestion is appended to ~/.amber_ai_memory.k, one self-describing Amber
expression per line:
ai.rec["q";1755512345;3;"select sym,px from trades";"trades quotes"]
/ kind epoch-s hits text contextLoading it is simply evaluating it, so it stays human-readable and hand-editable.
\ai memory shows it, \ai forget erases it,
AMBER_AI_MEMORY=/path moves it (per-project memories work), and deleting the file is
always safe.
Tuning the budget
A 7B on CPU generates a few tokens a second, so a 128-token answer can take 30 s while a 0.5B does it in 4 — and Ollama unloads an idle model after five minutes, so the load is paid again and again inside the same deadline.
amber> \ai warm # load the model now, on its own budget, and measure\ai warm asks for a single token, so it returns as soon as the weights are resident
rather than when an answer is finished. It then times a short generation and, if your measured
tokens/sec cannot produce a full answer inside the current budget, raises the budget and says so. It
measures your machine rather than guessing from the model's name. Every request also carries
keep_alive, so the backend holds the model for 30 minutes after each call.
| Variable | Default | Effect |
|---|---|---|
AMBER_AI_URL | 127.0.0.1:11434 | the model endpoint |
AMBER_AI_TIMEOUT_MS | 10000 | answer budget |
AMBER_AI_TAB_MS | 100 | Tab budget — leave it small, it runs on the keystroke path |
AMBER_AI_NUM_PREDICT | 128 | generation length; the biggest single lever after the load |
AMBER_AI_KEEP_ALIVE | 30m | "0" unloads at once, "-1" keeps forever |
AMBER_AI_MEMORY | ~/.amber_ai_memory.k | where the persistent memory lives |
Each command scales off AMBER_AI_NUM_PREDICT rather than using it directly, because a
one-line answer and a table profile do not need the same room:
| Command | Tokens at the 128 default |
|---|---|
\ai, \ai explain | 1.5× — 192 |
\ai why | 2× — 256 |
\ai profile, \ai optimize | 3.5× — 448 |
Setting AMBER_AI_NUM_PREDICT=48 scales all three down together and
roughly halves the wait. \ai warm sizes the answer budget from the longest of them, so
raising the caps cannot move the timeout to \ai profile.
grep -r socket src/ in the engine finds only src/0.c's IPC support, which
predates 1.9 and is the same code kdb-style hopen uses. Everything on this page lives in
ext/, and ./uninstall.sh removes it.