← Open Source
pgalko

BambooAI

AI data analyst for transparent, reproducible analysis. Works step by step in Python, verifies the evidence behind its reports, and supports a wide selection of models, including local open-weight models.

ApplicationsSpecialistsPython
Open on GitHub
Momentum
+0stars in 24 hours0.0%
794
Stars
88
Forks
+1
This week
2
Contributors
Created 2023-05-07 · Updated 2026-10-15 · #13753 today
Top developers
ApiPolicepgalko
README

BambooAI

Hosted version: bambooai.org — the same analyst, nothing to install.

BambooAI is an LLM-driven data analyst that works in a persistent Python kernel, one cell at a time, and reproduces its own results before it reports them. You give it a dataset and a question; it writes and runs code, reads the output, decides the next step, and ends with a report whose cited numbers have been re-executed in a fresh kernel. It runs on your machine with your own model keys, and the code the model writes runs in a container, not under your account.

Table of Contents

Overview

BambooAI is built for people who analyse data for a living and want the work done the way they would do it: in a notebook, step by step, with every intermediate result visible and every final number checked. It is not a chat interface over a code interpreter. The analyst has a fixed set of actions, a one-page contract that is the same for every model, a budget it can see, and a report format it must fill in. The web app streams the run as it happens, keeps the notebook, and lets you ask follow-up questions against the same kernel state.

It came out of sports science — training and physiology data from Intervals.icu, Endura and SweatStack load directly — and applies to any tabular dataset.

Features

  • One model, one kernel, one notebook. The analysis is a sequence of Python cells in a persistent kernel with a checkpoint per cell; a failed cell is rolled back and the namespace is exactly as before the attempt.
  • A checked report. Before the report is shown, the cells it cites are assembled into a script and executed in a fresh kernel against the original data. The closing card states how many numbers reproduced.
  • Three modes. Quick (a lookup, a definition), Deep (a full analysis within a turn budget), Adaptive (a long run, reviewed after every 8th turn by a separate reviewer on a stronger model that can redirect it or end it).
  • Follow-ups on a warm kernel. A follow-up question continues with the variables the previous run left; a new question starts fresh. Runs form chains you can step through. If the data loaded has changed since the earlier chain (another window, a replaced file), the follow-up starts in a fresh kernel on the data loaded now: the earlier chains stay in its ledger as context, marked as on other data, and its replay and any routine made from it use its own cells. The same file uploaded again (a new session, a favourite reopened) is the same data, and the follow-up continues as before.
  • Ask about a plot. The chat button on a plot sends the picture with your question: the analyst's model sees it when the config's model_properties says it takes images ("multimodal": "true"); otherwise the question goes alone and the pane says so.
  • Web search with sources. With a Gemini key, the analyst searches for published figures to compare against (Google AI grounding) and cites them in the report.
  • Memory. Saving a run distils what was learned about the dataset — column quirks, approaches that worked — into a memory pack the analyst reads in later runs on the same data.
  • Plain-language version. A second, cheaper model rewrites the report for a non-technical reader; both versions are kept.
  • Auxiliary datasets and integrations. Several files per analysis; training data loaded from Intervals.icu, Endura and SweatStack; loaded frames paged in the Data tab.
  • Documents beside the data. Up to four papers, notes or transcripts a thread (PDF, Word, Markdown, text), parsed into passages with locators; the analyst sees a map of each and reads the text with code, so a dataset is analysed in the context of what was written about it.
  • Everything inspectable. Every cell's code and output, every figure, every prompt that produced a turn, the per-call tokens, time and cost.
  • Ten providers, per-seat configuration. OpenRouter, OpenAI, Anthropic, Google, Groq, Mistral, xAI, DeepSeek, Ollama and vLLM; each seat (analyst, reviewer, rewriter, …) has its own model and reasoning effort. Local models keep everything on the machine.
  • Isolation by default. The model-written code runs in the executor container, built from the Dockerfile in the package and reachable only from your machine.
  • Saved chains, labels, usage dashboard, PDF export of the report, light and dark themes.

How It Works

A run is a sequence of turns. In each turn the analyst reads what it has so far — the question, the dataset's description, the cells and their (truncated) outputs, the budget — and takes one action:

action effect
CELL run one Python cell in the kernel; the printed output is the next turn's input
SHOW n re-open the full output of an earlier cell
NAMES list the variables the kernel holds
RECALL consult the memory pack for this dataset
SEARCH web search with Google AI grounding, summarised and cited
ASK put one question to the person and wait for the answer
REPORT write the report; the run ends

The contract that governs this is one page, analyst/contract.md, and is the same for every model. It asks for a report with a best estimate and its uncertainty, the conditions it holds under, how it was established, and the cells it rests on. Cells compute and draw nothing; the figures come after the report.

After REPORT:

  1. The Figures seat is shown the report and the cells and writes one cell that draws the one to three figures the reader needs from what the kernel holds, with Plotly - or answers NONE when a number or a table answers. A cell that fails comes back with its error, three corrections at most; the report gains a Figures section citing the cell, and says so when the figures were not drawn.
  2. The Rewriter seat writes the plain-language version.
  3. The cited cells, the figures cell among them, are assembled in execution order into a script and run in a fresh kernel against the original data. The numbers the report quotes are compared with the fresh output; the closing card says whether they reproduced (e.g. "replay reproduced 41 numbers in a fresh run"), and the figures that run draws are the Plots tab.

Modes and budgets. Each mode sets a turn budget and a dollar budget the analyst can see. The budgets are per preset (tier_properties in the configuration). At the performance preset: Quick 4 turns, Deep 15, Adaptive 50 with a review after every 8th turn. In Adaptive mode the review runs on the Reviewer seat - a stronger model with its own brief - which reads the question, the note, the results so far, the analyst's turns since its last review and the code and output of the cells the current answer rests on (handed to it by the session, one call per review), and answers with one verdict: a test to run, a narrower conclusion, or report now. A verdict to report binds only when every cell it cites has been checked by a review. After the report, in Adaptive, the same reviewer reads the report against the question and adds a short note for the reader.

Memory. When you save a run, the Knowledge Distiller seat writes a card about the dataset — what the columns mean in practice, what to watch for, what worked — into memory//memory_pack.yaml. RECALL reads it in later runs.

Installation

Requirements: Python 3.11 or newer, and Docker (Docker Desktop on macOS and Windows, Docker Engine on Linux), running. Docker is where the model-written code runs; see Where the Code Runs for the alternative.

Option 1 — pip (recommended)

pip install bambooai
bambooai serve

Option 2 — from the repository

git clone https://github.com/pgalko/BambooAI.git && cd BambooAI
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
bambooai serve

Option 3 — hosted. bambooai.org runs the same analyst as a service, with nothing to install.

Quick Start

  1. bambooai serve. On first use it creates the working folder ~/bambooai, writes a .env there, builds the executor image from the Dockerfile in the package (several minutes, once: the image installs pandas, numpy, scipy, statsmodels, scikit-learn, matplotlib, plotly and the rest), starts the container, and opens http://127.0.0.1:5001.
  2. Put at least one model key into ~/bambooai/.env. An OpenRouter key is enough on its own: every model in the default configuration is reachable through OpenRouter. Add GEMINI_API_KEY if you want web search.
  3. bambooai serve again. Upload a CSV, Parquet, JSON or XLSX file and ask a question. Choose Quick, Deep or Adaptive in the composer.
  4. In the app, Models & Compute (gear menu) picks the preset — cost, performance or max — and saves it. Usage shows the calls, tokens and cost per run.

The working folder:

~/bambooai/
├── .env                          keys and settings; read at start, never written by the app
├── LLM_CONFIG_template.json      the seats per preset: model, provider, reasoning effort, max_tokens, prices
├── bambooai.sqlite               chains, labels, usage, the saved preset, integration credentials (encrypted)
├── config//LLM_CONFIG.json the configuration built from the template for the chosen preset
├── storage//threads/       one JSON per thread: runs, cells, reports, reproduction results
├── storage//favourites/    saved chains
├── memory//                the memory pack
└── logs//                  bambooai_run_log.json: every model call with tokens, time and cost

serve reads nothing outside this folder. Upgrading the package does not change it; deleting it starts you fresh. BAMBOO_HOME or bambooai serve --home DIR moves it.

Configuration

Environment Variables

All in ~/bambooai/.env. Lines starting with # are ignored; do not put comments after a value.

variable meaning
OPENROUTER_API_KEY, OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, XAI_API_KEY, MISTRAL_API_KEY, DEEPSEEK_API_KEY provider keys; only for the providers the seats use
GEMINI_API_KEY also enables web search (Google AI grounding); without it SEARCH is off
REMOTE_OLLAMA, REMOTE_VLLM base URLs of local model servers, e.g. http://localhost:11434
BAMBOO_LEVEL cost / performance / max: the preset before one is saved in the app
BAMBOO_COMPUTE docker (default) or local
EXECUTOR_PORT the container's published port (default 5055)
EXECUTION_MODE, EXECUTOR_API_BASE_URL to use an executor you run yourself: api and its URL. BAMBOO_COMPUTE is then ignored
BAMBOO_USER the identity that names the per-user folders (default local)
SWEATSTACK_CLIENT_ID, SWEATSTACK_CLIENT_SECRET your own SweatStack app registration; without them the SweatStack integration is not offered
APP_PORT the web app's port (default 5001)
SESSION_COOKIE_NAME, FLASK_SECRET, LLM_CONFIG_ENCRYPTION_KEY the server; the last two are generated on first use

Models and Seats

LLM_CONFIG_template.json in the working folder defines the seats for each of four presets (free_agent_configs, cost_agent_configs, performance_agent_configs, max_agent_configs), the turn budgets per preset (tier_properties), and the properties of every model named (model_properties). The performance preset as shipped:

"performance_agent_configs": [
  {"agent": "Analyst",                  "details": {"model": "x-ai/grok-4.7",               "provider": "openrouter", "reasoning_effort": "high", "max_tokens": 32000, "temperature": 0}},
  {"agent": "Figures",                  "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "low",  "max_tokens": 16000, "temperature": 0}},
  {"agent": "Reviewer",                 "details": {"model": "openai/gpt-6.1-sol",          "provider": "openrouter", "reasoning_effort": "high", "max_tokens": 48000, "temperature": 0}},
  {"agent": "Rewriter",                 "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "low",  "max_tokens": 16000, "temperature": 0}},
  {"agent": "Reader",                   "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "low",  "max_tokens": 16000, "temperature": 0}},
  {"agent": "Promoter",                 "details": {"model": "x-ai/grok-4.7",               "provider": "openrouter", "reasoning_effort": "medium", "max_tokens": 32000, "temperature": 0}},
  {"agent": "Knowledge Distiller",      "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "none", "max_tokens": 24000, "temperature": 0}},
  {"agent": "Google Search Executor",   "details": {"model": "gemini-flash-latest",          "provider": "gemini",     "reasoning_effort": "none", "max_tokens": 8000,  "temperature": 0}},
  {"agent": "Google Search Summarizer", "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "none", "max_tokens": 12000, "temperature": 0}},
  {"agent": "Image Generator",          "details": {"model": "deepseek/deepseek-v4.1-flash", "provider": "openrouter", "reasoning_effort": "none", "max_tokens": 28000, "temperature": 0}}
],
"tier_properties": {
  "performance": {"analyst_turns_quick": 4, "analyst_turns_deep": 15, "analyst_turns_adaptive": 50, "analyst_review_every": 8}
},
"model_properties": {
  "x-ai/grok-4.7": {"capability": "reasoning", "multimodal": "true", "templ_formating": "xml",
                    "prompt_tokens": 0.002, "completion_tokens": 0.006,
                    "reasoning_style": "effort", "reasoning_efforts": ["low", "medium", "high", "xhigh"]}
}

multimodal says whether the model takes a picture with a question (Ask about a plot); the analyst's seat needs it "true" for the figure to be sent.

The seats:

seat role
Analyst runs the analysis: every turn, every cell, the report
Reviewer the reviews - after every 8th turn in Adaptive mode, and after its report; its own brief, a stronger model
Figures the figures, drawn once after the report: one cell from the report and the kernel's objects (a short code reply, so a low effort - a seat that thinks long can spend its allowance and send nothing; the analyst's seat when absent)
Rewriter the plain-language version of the report
Reader reads the thread's documents for the analyst (the READ action): a cheaper model, called once per read over the passages that match the question
Promoter Save as routine: reads a finished chain once and names its inputs as find/replace edits (a JSON reply, so a moderate effort and a roomy max_tokens; the analyst's seat when absent)
Knowledge Distiller writes the memory card when a run is saved
Google Search Executor the grounded search itself (a Gemini model)
Google Search Summarizer condenses search results into a cited digest
Image Generator turns the report into an infographic (a YAML spec rendered to SVG) shown with the answer

Seat fields: model, provider, reasoning_effort (none, low, medium, high, xhigh; mapped to each provider's own vocabulary, or to a thinking budget for models that use one), max_tokens, temperature. Every model a seat names must appear in model_properties, which supplies its prices per 1K tokens (used for the running cost), its formatting (xml or text) and its reasoning interface.

Ollama

BambooAI can use models running locally through Ollama, or hosted in Ollama's cloud.

Model configuration

In LLM_CONFIG_template.json, set the agent's provider to ollama and name the model. For example, to use gpt-oss:20b as the Analyst:

{
  "agent": "Analyst",
  "details": {
    "model": "gpt-oss:20b",
    "provider": "ollama",
    "reasoning_effort": "high",
    "max_tokens": 16000,
    "temperature": 0
  }
}

Add a matching entry under model_properties in the same file:

"gpt-oss:20b": {
  "capability": "reasoning",
  "templ_formating": "text",
  "prompt_tokens": 0.0,
  "completion_tokens": 0.0,
  "context_window": 65536,
  "reasoning_efforts": ["low", "medium", "high"]
}

Context length. For local models, context_window sets the context length used for each request (Ollama's num_ctx). Set it to at least 32,768 tokens for the Analyst; the example uses 65,536. Without it, Ollama applies its own default, which depends on the machine's memory and can be as low as 4,096 tokens. A prompt longer than the limit is truncated rather than rejected; BambooAI shows a warning when it detects this. Cloud models manage their own context length and do not need the setting.

Reasoning. List the thinking levels the model supports in reasoning_efforts, and pick one with the agent's reasoning_effort. ollama show lists the levels a model defines. For models that can only switch thinking on or off, BambooAI sends the switch instead; reasoning_effort: none turns thinking off where the model allows it. Models without thinking support receive no setting.

Repetition penalty. Ollama penalises recently used tokens by default (repeat_penalty 1.1), which degrades code and structured output, so BambooAI sends 1.0 (no penalty) with each request. To use a different value for a model, add repeat_penalty to its model_properties entry.

Costs. Set prompt_tokens and completion_tokens to 0.0 for local models. Cloud usage is covered by your Ollama account and plan.

Connecting to Ollama

By default, BambooAI connects to Ollama on your own computer at http://localhost:11434. To use Ollama on another machine, set its address in your .env file:

REMOTE_OLLAMA=http://:11434

All agents using the ollama provider share this connection. Pull local models on the machine running that Ollama server, and to use cloud models through it, sign in there with ollama signin.

For access from another machine, start the Ollama server with OLLAMA_HOST=0.0.0.0, and restrict access to a trusted network.

You can also connect to Ollama's cloud directly, without running an Ollama server. Add these settings to your .env file and choose cloud models in your agent configuration:

REMOTE_OLLAMA=https://ollama.com
OLLAMA_API_KEY=your_api_key

vLLM

BambooAI can use models served by vLLM on a machine with a suitable GPU. vLLM serves a model as an OpenAI-compatible API; BambooAI connects to it the way it connects to any provider.

Serving a model

The command below serves Qwen3.8-27B in 8-bit on a single 48 GB GPU, with a 64k context and the model's thinking returned separately from its answers. It is the configuration BambooAI was tested against (vLLM 0.30, an RTX A6000):

vllm serve ~/models/Qwen3.8-27B \
  --quantization fp8 --linear-backend marlin \
  --language-model-only \
  --compilation-config '{"mode": 0, "cudagraph_mode": "FULL"}' \
  --speculative-config '{"method": "mtp", "num_speculative_tokens": 2}' \
  --max-model-len 65536 --max-num-seqs 8 \
  --reasoning-parser qwen3 \
  --served-model-name qwen3.8-27b \
  --host 0.0.0.0 --port 8000

A few of these flags are worth knowing about. --reasoning-parser is what lets BambooAI show the model's thinking apart from the answer. --served-model-name gives the model the short name you use in the agent configuration. --linear-backend marlin selects the 8-bit kernels that run on Ampere GPUs such as the A6000 and A100; on Ada and Hopper GPUs it is not needed. --language-model-only skips a model's vision components, which BambooAI does not use. The --compilation-config line runs the model with CUDA graphs but without torch.compile, which fails on this model; it is markedly faster than --enforce-eager. The --speculative-config line uses the model's own multi-token-prediction head to draft tokens: the same output, noticeably faster. Drop it for a model without an MTP head.

Model configuration

In LLM_CONFIG_template.json, set the agent's provider to vllm and use the served model name:

{
  "agent": "Analyst",
  "details": {
    "model": "qwen3.8-27b",
    "provider": "vllm",
    "reasoning_effort": "high",
    "max_tokens": 16000,
    "temperature": 0
  }
}

Add a matching entry under model_properties:

"qwen3.8-27b": {
  "capability": "reasoning",
  "templ_formating": "text",
  "prompt_tokens": 0.0,
  "completion_tokens": 0.0,
  "context_window": 65536,
  "reasoning_efforts": ["low", "medium", "xhigh"]
}

Context length. vLLM sets the context length when the server starts (--max-model-len) and refuses a prompt that does not fit, rather than truncating it. Set context_window to the same value and BambooAI warns before sending a prompt that cannot fit, with the options for fixing it.

Reasoning. With --reasoning-parser, the agent's reasoning_effort is passed to the model; list the levels it supports in reasoning_efforts, as the model names them (Qwen3.8 takes low, medium and xhigh). An agent's effort the model does not define is mapped to the nearest level it does. reasoning_effort: none turns thinking off.

Costs. Set prompt_tokens and completion_tokens to 0.0; the server does not bill.

Connecting to vLLM

Set the server's address in your .env file:

REMOTE_VLLM=http://:8000/v1

If the server was started with --api-key, add VLLM_API_KEY with the same value. With every agent on Ollama or vLLM and no Gemini key, no request leaves your network.

The app builds config//LLM_CONFIG.json from the template for the chosen preset, and rebuilds it when the template file is newer than the built one.

Data and Integrations

  • Primary dataset: CSV, Parquet, JSON or XLSX, loaded into the kernel as df. The Data tab pages it.
  • Auxiliary datasets: further files uploaded alongside; available to the cells by path.
  • Intervals.icu and Endura: an API key entered in the app (stored encrypted in the working folder's database). Choose a date range; the data is loaded into the executor and described to the analyst, with the Intervals data model attached as an OWL ontology.
  • SweatStack: OAuth; needs your own app registration with SweatStack (SWEATSTACK_CLIENT_ID, SWEATSTACK_CLIENT_SECRET).
  • Web search: GEMINI_API_KEY.

Documents

A thread can carry up to four documents — papers, notes, meeting or interview transcripts — as PDF, Word (.docx), Markdown or plain text, each up to 10 MB. Attach one from the paperclip menu (Document). The file is parsed once, into passages with locators: the page for a PDF, the heading trail for the other formats, each passage numbered D1.17 (document 1, passage 17). Tables become rows the kernel can load. A blue Documents pill in the top bar shows how many are attached; hover it to see them and remove one, click it to open the Documents view, which shows each document's map and its text passage by passage.

The analyst does not receive the text in its prompt. It receives a short map of each document — the outline, the tables, the figure captions — and in the kernel each document is an object: D1.text, D1.page(n), D1.grep(pattern, context=1), D1.units(a, b), D1.table(n) (a dataframe), D1.outline(). A cell can search a transcript for a name, read a section, or load a table and check it against the paragraph that describes it, and the report cites the cell as usual. For what a search of the text cannot do, the analyst has one more action, READ: it names a document (or all of them, or a stretch of one) and what it is looking for; the passages that match go to the Reader seat, which quotes the ones that answer, each checked word for word against the document and returned with its locator. The report cites a passage as [D1.17] beside [cell n]; on the page the citation shows the passage on hover and opens the Documents view at it on click. A READ is a turn, like a web search; three per run.

Documents are kept as files under the working folder's storage//documents//, not in the database; every chain in the thread sees them, and they are removed with the thread. The parsers are pdfplumber and python-docx, installed with the package. A template from before the Reader seat keeps working: add the seat from the shipped template to yours to turn READ on. A PDF without a text layer (a scan) is refused: OCR is not supported. Parsed text above 2 MB is refused as well; the pill shows the reason.

Where the Code Runs

The analysis kernel, the reproduction run and the integrations' data fetching all run in the executor container: a Flask API in front of the persistent kernel, from containers/executor/. bambooai serve builds the image (bambooai-executor:) on first use, starts the container published on 127.0.0.1:5055, waits for its health, and stops it when the app stops (--keep-executor leaves it running). The Restart control in the app restarts it.

Alternatives:

  • bambooai serve --compute local (or BAMBOO_COMPUTE=local): the kernel runs in a subprocess of the web app. No Docker needed; no isolation; the integrations are not available, since they fetch inside the executor.
  • An executor you run yourself — docker run -p 5055:5000 bambooai-executor:2.0.0 on this or another machine — with EXECUTION_MODE=api and EXECUTOR_API_BASE_URL in .env.

The web app listens on 127.0.0.1 unless started with --host 0.0.0.0. Keys live in .env and are read at start; the app has no key entry and sends nothing anywhere but the providers you configure.

Command Line

bambooai serve [--home DIR] [--port N] [--host H] [--compute docker|local] [--keep-executor] [--no-browser]
bambooai init  [--home DIR]      create the working folder and its files without starting
bambooai where                   print the working folder

Architecture

browser ── HTTP / SSE ── web_app (Flask) ── HTTP ── executor container (Flask API + persistent kernel)
                            │
                            ├── analyst/     the turn loop (session.py), the contract, the notebook,
                            │                the report, the reproduction run
                            ├── bambooai/    model adapters (models/), log manager, config builder,
                            │                the local store (db/local_store.py, SQLite)
                            ├── delve/       the persistent kernel: a subprocess with a checkpoint per
                            │                committed cell and rollback of a failed one
                            └── containers/executor/   the executor image
  • analyst/contract.md is the only prompt the analyst sees; analyst/session.py is the loop.
  • delve/kernel.py runs cells in a subprocess and keeps a checkpoint per committed cell.
  • bambooai/models/ adapts each provider, including reasoning-effort vocabularies and prompt caching.
  • bambooai/db/local_store.py is a SQLite client with the same query interface the hosted edition uses against Supabase, so the routes above it are shared between the two.
  • tools/stack/ runs the app, an executor and stand-ins for the external services with a scripted analyst, so the product can be driven end to end offline - including Intervals.icu and Endura, served by fake_apis.py from a calendar with the loaders' own variation (FIT files the executor's parser reads, windows without a sensor, a wellness log that ends, a race without power).

Logging and Usage

  • logs//bambooai_run_log.json: every model call of the current thread — seat, model, prompt, response, tokens, time, cost — overwritten when a new thread starts.
  • logs//consolidated_logs.json: the same across threads, rotated at 5 MB.
  • In the app: Usage (gear menu) aggregates calls, tokens and cost by seat, model and run; the closing card of every run shows its cells, cost and time and opens the per-call telemetry; the prompt button on any turn shows the exact prompt that produced it.

Development

git clone https://github.com/pgalko/BambooAI.git && cd BambooAI
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]" && python -m playwright install chromium

./run_battery.sh                                                  # unit suites: analyst, kernel, notebook, report, replay
python tests/e2e/test_stack.py --edition local --compute local    # the real app, page and kernel; a scripted model, no network
python tests/e2e/test_stack.py --edition local --compute direct   # the same against an executor process
python tests/e2e/test_integrations.py                             # the loaders against the fake Intervals.icu and Endura; a routine run on the next window and the next race
python tests/e2e/test_local_store.py                              # the store survives a server restart
python tests/e2e/test_package.py                                  # the wheel in a fresh virtualenv; serve with a docker stand-in
python tools/render_gallery.py --out /tmp/gallery                 # every page state, both themes, for visual comparison

CI (.github/workflows/ci.yml) runs the battery and builds the executor image on every push; the browser suites and the package test are run locally before a push. A tag v* publishes the package to PyPI (.github/workflows/release.yml).

Design notes: docs/DESIGN_CHECKLIST.md (what every change must keep true), docs/OSS_DESIGN.md (the decisions behind this edition), HANDOVER.md (the state of the code).

Contributing

Issues and pull requests are welcome. Keep changes small and readable, run the battery and the end-to-end story before opening a pull request, and say in the description what you tested.

Contact

palo@bambooai.io · Issues · Discussions


BambooAI 1.x was a Python library used from notebooks (from bambooai import BambooAI). Version 2 replaces it. The 1.x releases remain installable with pip install "bambooai<2"; the source is on the v1 branch.

Nearby in the ranking
Similar items