File-native model and experiment registry
Project description
Koobi
File-native model and experiment registry for local workflows.
Koobi stores experiment metadata as plain Markdown cards, builds a disposable DuckDB index, and serves an interactive Marimo UI for browsing, filtering, visualizing, and editing cards. The source of truth is always your files.
Why Koobi
Most experiment trackers assume a hosted service and database-backed runs. Koobi is designed for local-first users who want:
- readable, versionable records in Git
- zero-server operation
- domain-agnostic metrics and parameters
- workflows friendly to both humans and coding agents
Current MVP Capabilities
- Markdown card parsing and serialization with preserved body/key ordering
- additive card updates (only targeted fields are rewritten)
- DuckDB index build with:
cardstable (metadata + body)measurestable (long-format metrics)series_registrytable (series pointers)
- Marimo app with:
- project/status/search filters
- model table view
- card detail view (metrics/params/tags/body)
- edit form (name/status/tags/body) with write-back
- one measure bar chart
- Execution model (Mode B) — optionally let Koobi run an experiment instead of just recording it:
- one function registry with
component/series/measure/itemskinds; functions live in your package and self-register on import (never embedded in cards) - components declared as cards (a model, a framework) backed by registered functions, composed into a typed-port dataflow
koobi run <experiment>resolves the components, runs them in dependency order, computes the declared measures, persists a result series, and stamps provenance — writing the same card a human would have authored by hand- opt-in input-hash caching (off by default, so research runs always re-execute)
- a finance pack providing Sharpe / Calmar / max-drawdown measures
- one function registry with
- CLI commands:
koobi initkoobi newkoobi indexkoobi runkoobi ui
Architecture Overview
cards/*.md --> index.duckdb --> Marimo app
^ |
|---------- edit/write-back -------|
- Cards are authoritative.
- Index is derived and disposable.
- UI reads from index and writes edits back to cards.
Koobi works two ways over one data model: you can record results yourself (Mode A), or declare runnable components and let koobi run produce the card (Mode B). Either way the card is the contract; execution is just an optional producer of it. The engine stays domain-free — it wires named ports and runs functions; finance knowledge (the Sharpe formula, date slicing) lives only in packs and your own project code.
experiment card --> resolve components --> run in dependency order --> measures + series --> write card back --> reindex
(model + framework, by typed ports) (provenance stamped)
Installation
Install into any project or environment directly from Git:
pip install git+https://github.com/blu3c0ral/koobi.git
This installs the koobi command and the importable koobi package.
For local development on Koobi itself, install editable with dev extras:
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"
Quickstart
Koobi discovers its configuration automatically: every command searches the
current directory and its parents for a koobi.toml. Work from inside your
workspace and no --config flag is needed.
1) Create (or enter) a workspace
koobi init --path my-registry
cd my-registry
Or use the bundled demo workspace:
python examples/local_example_to_cards.py --overwrite
cd examples/local
2) Build the index
koobi index
3) Launch the UI
koobi ui
The UI opens at http://localhost:2718.
Running from outside the workspace
If you prefer not to cd, point Koobi at the config explicitly or via an
environment variable (precedence: --config > KOOBI_CONFIG > current dir):
koobi ui --config examples/local/koobi.toml
# or
export KOOBI_CONFIG=examples/local/koobi.toml
koobi ui
CLI Reference
Initialize a new workspace:
koobi init --path /path/to/workspace
Create a new card (run from inside the workspace):
koobi new demo_project.my_model
Rebuild index:
koobi index
Run an experiment (Mode B):
koobi run my_project.exp_001 # --force to ignore the cache
koobi run my_project.exp_001 --dry-run # validate wiring only — runs nothing, pulls no data
run exits non-zero on a degraded run (a component raised, a port produced no output, or a
declared measure didn't compute), printing the reasons to stderr — so a scheduled run can't
mistake a silent partial failure for success. -v/-vv add per-node progress and (at -vv)
tracebacks, and may go before or after the subcommand.
Run UI:
koobi ui
Running Experiments (Mode B)
An experiment is a composition of components — e.g. a model and an entry/exit
framework. You declare each as a card backed by a registered function, then declare
an experiment card that wires them together; koobi run executes the graph and fills
in the results.
Point config at your function module and any datasets (a name → loader binding — the engine only sees the name; your loader gives it meaning):
# koobi.toml
[koobi]
function_modules = ["my_project.koobi_functions"] # imported to register runners/measures
cache = false # opt-in; default off
[datasets.prices]
loader = "my_project.koobi_functions:load_prices"
params = { universe = "16etf" }
[display]
packs = ["finance"] # Sharpe / Calmar / max-drawdown
Register the functions in your own package (never code in cards):
# my_project/koobi_functions.py
from koobi import registry
@registry.component("my_project.model", produces="predictions")
def run_model(ctx):
return predict(ctx.artifact("model"), ctx.data("prices")) # resources pulled in
@registry.component("my_project.framework", needs=["predictions"], produces="equity_curve")
def run_framework(ctx):
return backtest(ctx.get("predictions"), ctx.data("prices"), **ctx.params)
Declare the cards. needs/produces list ports only (the node-to-node dataflow);
external inputs like prices are pulled via ctx.data, not wired as ports:
# model card — produces a "predictions" port
id: my_project.model
type: model
runner: my_project.model
produces: predictions
# framework card — consumes "predictions", produces "equity_curve"
id: my_project.framework
type: framework
runner: my_project.framework
needs: [predictions]
produces: equity_curve
# experiment card — composes the two by role and pins params
id: my_project.exp_001
type: experiment
components:
model: my_project.model
framework: my_project.framework
params: { entry_threshold: 0.025 }
measures: [sharpe, calmar, max_drawdown]
Then koobi run my_project.exp_001 writes metrics, a result series, and a
provenance block back into the experiment card. See
docs/backend_technical_design.md §16 for the full model.
Use as a Library
Koobi's core is importable, so other projects and coding agents can read, query, and write cards programmatically:
from koobi import load_config, build, connect, query_cards
from koobi import Card, load_card, save_card
# Resolve config by searching upward from a path (file or directory)
cfg = load_config("path/to/workspace")
# Build the disposable DuckDB index from the Markdown cards
build(cfg)
# Query cards joined with wide-format measures
con = connect(cfg)
df = query_cards(con, project="demo_project", status="candidate")
# Read, mutate, and write back a single card (only targeted fields change)
card = load_card("path/to/workspace/cards/demo_project.my_model.md")
card.status = "promoted"
save_card(card, fields=["status"])
The execution engine is importable too:
from koobi import load_config, bootstrap, resolve_card, run_experiment
cfg = load_config("path/to/workspace")
bootstrap(cfg) # load packs + import function_modules
exp = resolve_card(cfg, "my_project.exp_001")
run_experiment(exp, cfg, ran_at="2026-06-26T00:00:00") # writes results back to the card
The full public API is exported from the top-level koobi package; see
koobi.__all__.
Repository Layout
src/koobi/ # core package (schema, cards, config, index, charts, app, cli)
src/koobi/engine.py # execution engine (run_experiment, Context, toposort)
src/koobi/registry.py # component/series/measure function registry + bootstrap
src/koobi/packs/ # domain packs (finance: Sharpe/Calmar/max-drawdown)
tests/ # unit tests
examples/local/ # self-contained demo workspace
examples/local_example_to_cards.py
docs/ # product, technical, and future feature docs
Development
Run tests:
pytest
Recommended pre-PR checklist:
- run
pytest - regenerate local example cards if example inputs changed
- verify
koobi indexandkoobi uiagainstexamples/local/koobi.toml
Documentation
docs/product_design.md- product framing and roadmapdocs/backend_technical_design.md- architecture and data contractsdocs/status.md- living snapshot of what is built vs. planneddocs/future_features.md- planned automation and extensions
Status
Phase 0 (the file-native registry) is built, reviewed, and stable. The §16 B1
execution slice — koobi run composing a model + framework experiment — is now
implemented and tested. Next: card types (framework/strategy) as first-class
declared types, then parameter sweeps and the measure-vs-parameter dashboards. See
docs/status.md.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file koobi-0.0.6.tar.gz.
File metadata
- Download URL: koobi-0.0.6.tar.gz
- Upload date:
- Size: 493.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
912a9574122627d7f67871e5f10c27b20620aa20d41fae9a32172efd516a8311
|
|
| MD5 |
8909713e5088beaa456ac538966261ea
|
|
| BLAKE2b-256 |
2919ef369cd8d9aab2b61d3c4066eb741dcafbc5cee75816469e645f0bdc9edf
|
File details
Details for the file koobi-0.0.6-py3-none-any.whl.
File metadata
- Download URL: koobi-0.0.6-py3-none-any.whl
- Upload date:
- Size: 483.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46dee8f5f2b9f4564b385725a09387b6640c8c51a66fbc2694057f5391a68933
|
|
| MD5 |
b7cf9631f9166470926230c1dbf44a1b
|
|
| BLAKE2b-256 |
8faff48bdd80366c1e41562cd036a8cbbfc15ba99c9c616289dbafdc87d315f2
|