rebornpad

Quickstart

From token to bundle

Ten minutes, one small repository, and whatever embedding model you already have wired up.

1. Get a token

Take one from the home page. It is generated in your browser and shown once, so put it somewhere before you close the tab.

export RP_TOKEN=rp_...

Fresh tokens go through an activation queue and normally start working within 24 hours. Until then every call answers 401 — that is the queue, not a mistake on your side.

2. Create a collection

One collection per body of work. Set dims to your model's output width; it cannot be changed afterwards.

curl -X POST https://rebornpad.dev/api/v1/collections \
  -H "Authorization: Bearer $RP_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"monorepo/api","dims":1024,"metric":"cosine"}'
{"id":"col_9fK2mQ","name":"monorepo/api","chunks":0,"dims":1024}

Repeating this call with the same name returns the existing collection rather than creating a second one, so it is safe to run from a script that does not track state.

3. Chunk and embed

We do not chunk or embed for you. Whatever splitter and model your agent already uses will do — the only requirement is that every vector in a collection has the same width.

A workable default: split on syntactic boundaries, cap chunks at roughly 400 tokens, and keep the file path and revision in metadata so retrieved context can be traced back to source.

import os, json, hashlib

def records(paths, embed):
    for path in paths:
        src = open(path, encoding="utf-8").read()
        for start, end, text in split(src):          # your splitter
            yield {
                "path": path,
                "span": [start, end],
                "text": text,
                "vector": embed(text),               # your model
                "meta": {
                    "lang": os.path.splitext(path)[1].lstrip("."),
                    "rev": revision,
                },
            }

4. Ingest

Stream the records as newline-delimited JSON over a single connection. Opening a new request per batch is the most common reason a first ingest crawls.

import requests

def stream(recs):
    for r in recs:
        yield (json.dumps(r) + "\n").encode()

resp = requests.post(
    "https://rebornpad.dev/api/v1/ingest",
    headers={
        "Authorization": f"Bearer {os.environ['RP_TOKEN']}",
        "Content-Type": "application/x-ndjson",
        "X-Collection": "col_9fK2mQ",
    },
    data=stream(records(paths, embed)),
    stream=True,
)

for line in resp.iter_lines():
    if line:
        print(json.loads(line))    # {"ack": 512, "accepted": 512, "rejected": 0}

Acknowledgements come back while you are still writing, so you can slow down or stop early instead of buffering the whole repository in memory.

Expect a few hundred megabytes and several minutes for a mid-sized repository. Large monorepos run into the gigabytes and can take an hour — that is the shape of the workload, not a stall.

5. Retrieve

Search returns a handle. Fetching the handle returns the bundle.

r = requests.post(
    "https://rebornpad.dev/api/v1/search",
    headers={"Authorization": f"Bearer {token}"},
    json={"collection": "col_9fK2mQ", "vector": embed(question), "k": 24},
)
handle = r.json()["bundle"]

raw = requests.get(
    f"https://rebornpad.dev/api/v1/query/{handle}",
    headers={"Authorization": f"Bearer {token}"},
).text

import base64, zlib
bundle = json.loads(zlib.decompress(base64.b64decode(raw)))

for chunk in bundle["chunks"]:
    print(chunk["path"], round(chunk["score"], 3))

The bundle arrives deflated and base64-encoded rather than as JSON. It is one line of text that can be cached, piped, or handed to a context loader without a parser in the middle.

Where to go next