The first successful API call is not the milestone. It's a coincidence. The milestone is the second one — same input, same output shape, same exit code — on a machine that isn't yours.
So here's the tutorial I wish I'd had: take a narrow feature (ticket triage), bring it up in four gated stages, and make every stage fail loudly. You'll end Stage 3 with the same code running locally and on a remote free server, with a hash diff as the proof.
Two notes before we start. The remote stages use MonkeyCode's free model access and free server option; the operator currently advertises a 10M-token free allowance, and I'm treating that as their claim, not a measurement — quotas and availability move, so check the current terms yourself. Also, I'm not asserting license details here; read the repo before you depend on it.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Stage 0 — Freeze the contract before you freeze the prompt
Most people write the prompt first. That's backwards. The prompt is the thing most likely to change; the shape of the input and output is the thing you want bolted down.
Create a 10-line fixture. Hand-label it. Never let the model generate it.
mkdir -p gates fixtures out
cat > fixtures/tickets.jsonl <<'EOF'
{"id":"t01","text":"Charged twice for the same invoice","label":"billing"}
{"id":"t02","text":"App crashes on upload over 20MB","label":"bug"}
{"id":"t03","text":"Cannot reset password, email never arrives","label":"account"}
{"id":"t04","text":"Need to change the tax ID on receipts","label":"billing"}
{"id":"t05","text":"Sidebar renders blank in Safari 17","label":"bug"}
{"id":"t06","text":"How do I add a second admin?","label":"account"}
{"id":"t07","text":"Refund request for last month","label":"billing"}
{"id":"t08","text":"Webhook retries hammer our endpoint","label":"bug"}
{"id":"t09","text":"SSO login loops back to the login page","label":"account"}
{"id":"t10","text":"Is there a status page?","label":"other"}
EOF
Now the gate. No network, no model, just structure.
# gates/contract.py
import json, pathlib, sys
ALLOWED = {"billing", "bug", "account", "other"}
def load(path):
rows = []
for i, line in enumerate(pathlib.Path(path).read_text().splitlines(), 1):
if not line.strip():
continue
row = json.loads(line)
assert set(row) >= {"id", "text", "label"}, f"line {i}: missing keys"
assert row["text"].strip(), f"line {i}: empty text"
assert row["label"] in ALLOWED, f"line {i}: bad label {row['label']!r}"
rows.append(row)
ids = [r["id"] for r in rows]
assert len(ids) == len(set(ids)), "duplicate ids"
return rows
if __name__ == "__main__":
rows = load(sys.argv[1])
print(f"CONTRACT_OK {len(rows)}/{len(rows)}")
Verify: python gates/contract.py fixtures/tickets.jsonl prints CONTRACT_OK 10/10 and exits 0. Corrupt one line on purpose — an assert should stop you, not a stack trace three stages later.
Stage 1 — One call, one ledger line
Single call. No retries. No batching. The only goal is to learn what your provider actually returns, and to record it so you can argue with yourself later.
# gates/call.py
import hashlib, json, os, time, urllib.request
ENDPOINT = os.environ["LLM_BASE_URL"].rstrip("/") + "/chat/completions" # verify path in provider docs
def call(prompt: str):
body = json.dumps({
"messages": [{"role": "system", "content": "Reply with one word: billing, bug, account, or other."},
{"role": "user", "content": prompt}],
"temperature": 0,
}).encode()
req = urllib.request.Request(ENDPOINT, data=body, headers={
"Content-Type": "application/json",
"Authorization": f"Bearer {os.environ['LLM_API_KEY']}",
})
t0 = time.monotonic()
with urllib.request.urlopen(req, timeout=60) as resp:
raw = resp.read().decode()
return raw, round(time.monotonic() - t0, 3)
def ledger(rec, path="out/ledger.jsonl"):
with open(path, "a") as f:
f.write(json.dumps(rec, sort_keys=True) + "\n")
The endpoint path and env var names above are placeholders — read the provider's docs and fix them before you trust anything downstream. Then run one item:
LLM_BASE_URL=... LLM_API_KEY=... python - <<'PY'
from gates.call import call, ledger
import hashlib, json, pathlib
row = json.loads(pathlib.Path("fixtures/tickets.jsonl").read_text().splitlines()[0])
raw, lat = call(row["text"])
ledger({"id": row["id"], "sha256": hashlib.sha256(raw.encode()).hexdigest(),
"latency_s": lat, "raw": raw})
print(raw)
PY
Verify: out/ledger.jsonl has exactly one line, and tail -1 out/ledger.jsonl | jq -r .latency_s is a number. If the response has no usage block, store null — don't estimate tokens by dividing characters. Guessed accounting is worse than missing accounting.
Stage 2 — The batch gate that can say no
Now run all ten. A gate is only a gate if it can exit non-zero.
# gates/batch.py
import json, pathlib, re, sys
from collections import Counter
from gates.call import call, ledger
MAX_RETRIES, MIN_PASS, MAX_P95_S = 1, 9, 8.0
def label(raw):
txt = json.loads(raw)["choices"][0]["message"]["content"].strip().lower()
m = re.search(r"\b(billing|bug|account|other)\b", txt)
return m.group(1) if m else None
rows = [json.loads(l) for l in pathlib.Path("fixtures/tickets.jsonl").read_text().splitlines() if l.strip()]
results, lat = [], []
for r in rows:
for attempt in range(MAX_RETRIES + 1):
raw, sec = call(r["text"])
lat.append(sec)
got = label(raw)
ledger({"id": r["id"], "attempt": attempt, "parsed": got, "latency_s": sec})
if got:
break
results.append((r["id"], r["label"], got))
passed = sum(exp == got for _, exp, got in results)
p95 = sorted(lat)[max(0, int(len(lat) * 0.95) - 1)]
print(json.dumps({"passed": passed, "of": len(results), "p95_s": p95}))
sys.exit(0 if passed >= MIN_PASS and p95 <= MAX_P95_S else 2)
Verify: echo $? prints 0 on a good run and 2 after you deliberately break one label in label(). Try that break. A gate you've never seen fail is decoration.
Stage 3 — Move the same code to a free server and hash-diff it
Here's where the free server earns its keep: it's a second machine, which is the only honest test of "it works." Same fixture, same temperature: 0, same code.
# local
python gates/batch.py && jq -r '[.id,.parsed]|@tsv' <(grep -v '"attempt": 1' out/ledger.jsonl) | sort > out/local.tsv
sha256sum out/local.tsv
# remote (free server), same repo, same command
python gates/batch.py && jq -r '[.id,.parsed]|@tsv' <(grep -v '"attempt": 1' out/ledger.jsonl) | sort > out/remote.tsv
sha256sum out/remote.tsv
Verify: diff out/local.tsv out/remote.tsv is empty. If the hashes diverge, the cause is almost always one of three things: the remote picked a different model than you assumed, the fixture file drifted, or an unset env var silently fell back to a default. Diff the label sets first — that tells you which.
When free access is the right runtime
| Situation | Free tier + free server | Notes |
|---|---|---|
| Prototype, internal demo, contract test | Yes | Stage 0–2 is exactly this |
| Teaching a team prompt/parse discipline | Yes | Cheap, reversible, no procurement |
| Pre-merge smoke test in CI | Usually | Keep it non-blocking; quotas and cold starts will bite |
| Latency-sensitive user-facing path | No | No SLA, and queueing is invisible to you |
| Regulated or personal data | No | Assume logs you don't control |
| Guaranteed throughput | No | Measure first, then decide |
Limitations, honestly
Free allowances are a moving target, and "10M tokens" is a number published by the operator on a given day — record your own usage from the ledger rather than trusting a dashboard total. Free servers get shared machine characteristics you don't choose, so treat latency numbers as observations, not contracts. Hashing only proves determinism when you pin temperature: 0 and normalize output yourself; a model that wraps its answer in a sentence will break your diff while being perfectly correct.
And if you need a written SLA, per-request isolation, or a data-processing agreement, this workflow is not for you — go pay someone. That's not a knock on free tiers; it's just what they are.
Start with Stage 0 tonight. It takes five minutes and it costs nothing, and it's the stage that catches the most. If you want a second machine to run Stage 3 on without a credit card, the free server option is the cheapest way I know to find out whether "works on my laptop" was ever true.
Top comments (0)