Point it at a LanceDB database. It says what is in there, what is wrong with it, and what asking cost.
LanceScope is a read-only workbench for LanceDB — a macOS app, or a console you run yourself. It opens a database wherever it lives and gives the format the screens it never had: schema and versions, the access path a query actually took, what a table costs a training run, and ten rules that have already looked. Every screen prints the bytes it spent getting there.
read-only by constructionno telemetry, no accountruns against your data, where it isa model is optional
segments · 162 rows · 7 columns · v16 — this read 23.8 KB and opened none of the 2.65 GB
what it does
Six things, precisely
LanceDB gives you a format and an engine. Neither of them has a face. These are the six things this puts in front of one — each on a screen, each printing what it spent, and none of them asking you to take a number on trust.
01
Reads a database, exactly
Schema, versions, indices, fragments and rows — the five things a Lance table is — each on a screen of its own, and each printing what looking at it cost. A column whose values live in side files is marked as one, and the ratio between the two halves is stated rather than left for you to work out. Nothing on these screens opens a data file.
The screen at the top of this page is the first of those five. Here is the second: every write a table has ever taken, with what each one did to the row, fragment and file counts. Every write makes a version and the old ones stay — a model trained on version 37 can be pointed back at version 37, and this is where you find out that version 37 exists.
sixteen versions, newest first · this read 67.1 KB over 47 IOs
02
Answers “why is this slow”
Run a scalar, full-text, vector or hybrid search and read the diagnosis: which access path Lance chose, whether the filter was pushed down, how many bytes and IOs it actually moved, and the Python that reproduces it somewhere this console is not. The bytes are not an estimate — the handle’s own IO counters are drained before and after that query and nothing else.
mnist-lance · read live over hf:// — a full pass would weigh 23.4 MB; this question cost 14.7 KB
A full pass over that table weighs 23.4 MB. The question cost 14.7 KB over 4 IOs, and the card says why — “A plain read. No index was involved and none was needed.” An index that exists and is not used gets a sentence of its own, naming the metric it was built for and the metric you asked for.
03
Checks the data, and charges for it out loud
Everything else here is derived from manifests. These six read your columns, so they are the one surface that is priced before it runs — weighed from the file footers without opening a page of them — and the run button spends it only when you press it. Cancelling stops the work rather than the wait.
the bill, before anything runs — 937,957 rows of video
priced every check
196,608 B · 6.4 s
missing-content
29.7 MB · and none of the video it points at
exact-duplicates
69.4 MB · and you knew that before you spent it
vector-health
3.89 GB · quoted, not run
near-duplicates
will not run · no vector index, so this would be a full pass per row sampled
class-balance
name the label column · a guess would answer a question nobody asked
What none of them can tell you is on the panel too: whether a label is right, whether two rows mean the same thing when the embedding does not say so, and what made a distribution move. A panel of green ticks implies otherwise, so it says so where the ticks are.
Ten rules run over the same manifests the other screens read, before you ask anything. Each finding carries its evidence on the same row — the fragment count, the rows, the bytes per vector — and each also appears under the panel holding the numbers it came from. Some of them are reasons not to act.
two findings, and the numbers each was derived from · no model was asked, and nothing here cost a token
No model is involved in any of the ten, and nothing here costs a token. That is worth saying plainly on a page that also has a section about language models: the part of this tool that tells you what is wrong with a table is the part with no model in it.
05
Lets the answer leave
The whole diagnosis comes out as one document — the findings with their evidence, the plan, the exact cost, the reader underneath — and it says what assembling it spent. Markdown to paste into an issue, or JSON another console opens and renders with the same cards, from the file, with nothing uploaded.
what does not travel
the rows
left behind · the reproduction re-runs against the reader's own copy
credentials
dropped by key · wherever they appear
the database root
<root> · a local path carries a username and a bucket carries an employer
The document records which mode produced it and reports the scheme separately, so the substitution costs no meaning. Pass paths=kept when the reader is you.
06
Adds language, optionally
With a local model or an API key, a question typed in English lands in the filter box as a predicate — in it, for you to read, before anything runs. The console is useful with nothing configured: findings, the query workspace and compare mode all work without a model and cost nothing.
the question above, the predicate it drafted below — nothing runs until you press filter
Refusing is a first-class outcome. Ask for something the columns cannot express and it says so, rather than producing a filter that runs and means something else. What that costs, where it runs and what leaves the machine is the next section.
Read off demo.lancescope.mlynn.dev on 6 September 2026, against the public mnist-lance table over hf://. The bytes are the handle's own IO counters, drained before and after that query and nothing else. content/guide/howto-check-data.md, measured against hf://datasets/lance-format/openvid-lance/data on 5 September 2026 with pylance 11.0.0. The last figure is a quote rather than a spend — vector-health was priced at 3.89 GB and not run.
for a training run
What a table’s shape costs you before the maths starts
The part of a training run that is taking too long is usually not the maths. It is the loop before it — assembling a dataset, looking at it, deciding whether it is any good, and discovering three hours in that your retrieval eval is scanning every vector you own. There is a screen for that, and it answers four questions from metadata alone.
what each column weighs, from the file footers · click one out of the projection and the epoch figure follows
one epoch reads
23.4 MB
loader ceiling
1 worker
rows
10,000
version to pin
v4
Those four are the whole screen. The bar chart above them is what each column weighs on disk, measured from the file footers rather than predicted — click a column out of the projection and the epoch figure follows, with no further reads, because the weights are already here.
The four questions
How many workers your fragments can feed
A fragment is the unit a reader parallelises over: one fragment to each worker. The fragment count is therefore the ceiling on useful workers, and nothing about the row count reveals it. A table written as a single fragment is single-threaded whatever you pass to num_workers.
Which fragment everyone else is waiting for
Uneven fragments cost an epoch the largest fragment rather than the average one. A query planner never notices — it reads what it needs and stops — so this is a problem that exists only for you. On a blob table it measures bytes rather than rows, because rows can be even while the media hanging off them is not.
What one pass actually reads
Two numbers on a table with a large half: the metadata pass, and the pass that opens the media. Knowing which one your job is doing is most of a GPU budget.
What the embeddings cost you
Vectors are usually the largest ordinary column and the one most likely to be rewritten. A 768-dimension float32 vector over 1,114 rows is 3.42 MB, about a sixth of that table — so an embedding-model swap is a cheaper decision than it feels like.
the same four questions on the reference corpus · derived from manifests — no model, no tokens
The index you think you have
If your retrieval eval retrieves before it scores, an unindexed vector column is a silent tax. Every similarity search over 1,114 rows reads all of them — 3.42 MB per query — so a 1,000-query eval moves 3.4 GB to answer questions an ANN index would answer from a fraction of it.
Worse than no index is a partial one: an index built, then rows appended. Queries still return the new rows, by scanning them. There is no error, and the only symptom is that things got slower. The console names the access path Lance’s own plan reports — KNNVectorDistance for a brute-force scan, ANNSubIndex for an index that engaged — before the query runs, so you can see which you are about to do.
what a run must pin
The dataset URI and the exact version, the columns the run reads, what those columns weigh, how many loader workers the split can actually feed, and the findings outstanding when it was generated — as one block to keep beside the code that runs it.
lancescope run-config test
Derived from the table, never written by a model. A run config that drifts from the table it describes is worse than none, because it is believed. The same block comes back from the table_run_config tool, so an agent can put it in your run for you.
All of this is the layout — how the table is split, what a pass moves, what a query costs on top of it. Whether the labels are right, whether a train/eval split leaks, whether the embeddings came back dead: those are properties of the data, and they live on a different screen that reads your columns and tells you what that will cost first.
content/guide/path-build-models.md for the reference corpus, and the live console at demo.lancescope.mlynn.dev on 6 September 2026 for the MNIST figures — 10,000 rows at v4, one epoch 23.4 MB, image_emb 20.5 MB of it, and a loader ceiling of one worker because the table is one fragment.
the model, and the absence of one
Which answers were computed, and which were generated
Both kinds are here and they are labelled. The findings, the byte counters, the access paths and the training figures are derived — no model, no tokens, and they work with nothing configured. The language layer is a separate, optional thing that you press, and every response it gives comes back with what it cost and what left the machine to get it.
no model
What it works out
The 10 rules, the byte counters, the access paths, the fragment layout, the training figures and the version history. All of it is derived from manifests and IO counters. It runs with nothing configured, costs nothing, and is the part of this tool that tells you what is wrong with a table.
your model
What it asks
Two jobs, both optional and both something you pressed: turn a question into a filter, and describe a table in a few sentences. A local model does it free and offline; a key does it faster. Nothing is asked on load — a panel that spends money because somebody opened a tab is a panel that spends money nobody agreed to spend.
your agent
What it hands over
The same read-only surface over MCP, so the intelligence is your agent's and the evidence is the console's. There is no key of ours and no model in that loop, and no tool in it can spend your API budget.
Two ways in, and one of them is free
Pull a local model and the console probes localhost:11434 and comes up local. That is the whole setup, and it costs nothing per response forever.
localollama pull qwen3:8b
hostedexport ANTHROPIC_API_KEY=sk-…
A key beats a local model when both are present, and an explicit choice in settings beats both. The hosted default is claude-opus-5; choosing a cheaper one is your call, made in settings, and not something the tool does quietly to save money. Any OpenAI-compatible endpoint works too — vLLM, LM Studio, Groq, llama.cpp — with a base URL and a model name.
Every response comes back with its receipt
settings → intelligence → test the model
answered, and honoured the schema
gemma3:27b · 11.8s · 72 in / 41 out · no cost — this ran on your machine
asked in English
“moments in the Go devroom more than ten minutes in”
dry-run count
matches 44 of 1,114 rows
landed in the filter box
track = 'Go' AND ts_s > 600
describe this table
47 seconds cold · 0.06s warm — the answer is kept against the table version, so it is asked once
A failure answers in the same shape, with the reason: a stale key, a model that was deleted, an endpoint answering prose where a schema was promised. Better a sentence there than a mystery three features later.
What it costs, counted honestly
Tokens and dollars, per response, beside the bytes it read — and a running total for the session. A cache hit is counted as a call that did not happen. A local model costs zero. A model with no published price reports its cost as unknown rather than as zero, because those are different claims.
Set a ceiling and it is checked before each call. Refusing after the money is gone is a receipt, not a limit.
What leaves your machine
Schema and statistics, by default. A table summary is written from the shape of a table, never its contents. Blob columns, vectors and binary columns are never sent under any setting.
The one exception is deliberate and visible. Translating a question is much more accurate when the model knows the actual values of low-cardinality string columns — so a local model gets them, because nothing leaves the machine, and a hosted one does not unless you ask. Either way the response names the columns whose values were sent.
Prices in the registry carry the date they were read — 2026-06-24 — and the two local models named here are the ones that got every one of the repository’s own natural-language-to-filter cases right: qwen3:8b, gemma3:27b. A local model not on that list still runs; it is assumed to handle structured output and not to handle multi-turn tool use, which is the cautious assumption rather than the convenient one.
content/guide/howto-intelligence.md and content/guide/reference-models.md. Prices in the registry carry the date they were read, 2026-06-24, and a model with no published price reports its cost as unknown rather than as zero.
for agents
Point a model at it
The same read-only surface over MCP, so an agent works from evidence instead of guesses. There is no key of ours in this and no model of ours in the loop: the intelligence is your agent's, the numbers are the console's.
Eleven tools, every one of them read-only and declared as such, and every one the HTTP route called in process rather than a second implementation of it — so the two surfaces cannot drift. Ask what is in a database and what is wrong with it, and the answer comes back with the unindexed vector column and what a search therefore costs, carrying the numbers those conclusions were derived from rather than a summary of them.
It cannot materialise a blob
read_rows has no expand parameter at all, so an agent cannot spend a turn discovering that the route underneath would refuse it. Reading every row of a table holding gigabytes of video costs kilobytes, however many times something asks.
It cannot write
Nothing under the console's read routes writes, so nothing here does either. The ingest wizard, which is the one thing in the project that creates a table, is not exposed as a tool.
It cannot spend your budget
No summarise tool and no ask tool. It can quote what checking a table's data would read; running that scan is a button in your console, which is where a decision to spend megabytes belongs.
content/guide/reference-mcp.md, generated from the code and vendored at the commit in content/.upstream.json. Every tool is declared read-only, and every one is the HTTP route called in process rather than a second implementation of it.
wherever it lives
A folder, a bucket, a Hub repo, or LanceDB Cloud
The same screens and the same byte counts, whatever the database is sitting on. Nothing is downloaded to look at a remote one — the URI is saved, not the corpus.
a local directoryany folder holding .lance tables
hf://the datasets LanceDB publishes on HuggingFace
s3://measured against a real bucket — same bytes, more latency
gs:// · az://Google Cloud Storage and Azure
LanceDB Clouda hosted database, read the same way
Measured against a real bucket, the byte counts are the same as on disk and only the latency differs — which is the point of counting bytes rather than seconds. A store nothing installed can list is saved and reported as unbrowsable rather than shown as an empty database, and adding support for one is an installable package rather than a wait.
someone else's dataset, opened over hf:// · listing every table cost 7.4 KB and nothing was downloaded
Try it on a dataset nobody here wrote
LanceDB publishes datasets on HuggingFace that open directly over hf:// with no download. Paste this into settings and watch the counter — the URI is saved, not the corpus.
937,957
rows
each carrying an MP4 beside its embedding
24,568 bytes
to open it
two IO operations, over the network, in under a second
about 73 KB
to browse five rows
and no video at all
urihf://datasets/lance-format/openvid-lance/data
content/guide/howto-connect.md. A store nothing installed can list is saved and reported as unbrowsable rather than shown as an empty database, and adding support for one is an installable package rather than a wait. Re-measured on the reference corpus, 2 September 2026: 2.651 GB in .blob side files against 20.04 MB for the moments table a search actually reads.
why every screen carries a number
Six operations, and what each of them read
The reference corpus is 1,114 moments and 162 segments across 16 talks. The rail is logarithmic, because at this scale a proportional bar renders as nothing at all. Zero is not plotted: an operation that reads no video prints NONE rather than a bar of length zero, which would look like a small amount.
semantic search over every moment
1 / 6
the rail runs from 1 KB to the whole corpus, 2.65 GB
index3.45 MB
index
3.45 MB
video
video
NONE
NONE
1 KB1 MB1 GB
bytes an index readbytes of video read
Re-measured by `make verify` on 2 September 2026, on the 16-talk corpus (1,114 moments, 162 segments): vector search 3.45 MB index and 0 B video, FTS 0.11 MB and 0 B, blob handle 2,722 B, cold segment 17.0 MB, warm seek 262,144 B. The check that asserts search reads zero video bytes passes.
the same fact as area
2.65 GB of video in .blob side files, against 20.0 MB of everything a search reads. One square is lit.
the limitation that made the tool
The manifest and the disk disagree, correctly
Two numbers, both true, answering different questions — and a tool that merged them into one would be lying to you. This is on the landing page because it is the reason the thing exists.
43,424 bytes
what the manifest reports
Lance's own tracked_files(), asked how big this table is.
2.65 GB
what is actually on disk
The same table, measured by walking it.
tracked_files() lists no .blob paths, so the manifest cannot see the side files where the video lives. Neither number is wrong; they answer different questions, and which one you want depends on whether you are asking what a query will cost or what the storage bill will say. LanceScope shows both, and says which is which.
docs/guide/explain-blobs.md. Lance says the same thing in its own API documentation — “Blob v2 payloads live in separate blob files and are not counted” (lance/optimize.py) — so this is the format behaving as designed, not a defect.
the write boundary
What it will and will not touch
It will build you a database. It will never edit one.
browsing
Reading a table cannot change it. That is checked rather than asserted: a test drives the entire read API and every MCP tool over a real corpus and then checks that not one byte on disk moved.
building
The ingest wizard creates a new table from your own files, and that is the only thing in the project that writes one. It is create-only by construction: it refuses a destination that already exists, only ever appends into a table it made itself during that same run, and has no reachable path to an overwrite.
The whole write surface is one module, and CI fails if a dataset mutation appears anywhere else. Deleting only ever happens when you ask for it: the button that clears a finished job from the list and the button that deletes the table it produced are deliberately two different buttons.
also included
the demo it grew out of
Ctrl-F for Video
Multimodal search over conference talks, where the video and its index are the same table. Searching the whole corpus reads zero bytes of video — not very little, zero.
24 moments in 193 ms · 3.81 MB to find them, and none of the 2.65 GB of video
It needs a built corpus and a local embedding model, so it is not in the packaged app: the DMG deliberately ships without torch, and the demo screen says so rather than failing quietly. Build it from the repo to run it.
try it
Or look at one first, in a browser
The console itself, on a public LanceDB dataset, with nothing to install. Every number on it is real — the schema, the findings, the access path a query took, and the bytes each answer cost, read from Lance’s own IO counters.
Read-only, and pinned to one dataset it does not host: pylance opens hf:// lazily, so the demo stores nothing and reads only what you look at. Queries are rate limited, because each one is paid for in range requests against somebody else’s server — which is the one number a hosted demo cannot show you honestly, and the reason the interesting version of this runs on your own disk.
Signed and notarised. There is no telemetry and no account. It reaches the network in four cases: a dataset you pointed it at over hf://, which it reads from huggingface.co; a language model, if you configured one; this page, to find the current version; and once a day on launch, to ask GitHub whether there is a newer release. That last one is the only request it makes without being asked, it sends nothing but the request itself, and it never installs anything — it tells you, and the button opens the release page.
On an Intel Mac? Open an issue — that is how the demand gets measured.