Give a Free AI Tier a Cold-Start Job Before It Touches Real User Requests

# ai# python# architecture# tutorial
Give a Free AI Tier a Cold-Start Job Before It Touches Real User Requestskongkong

The current AI news cycle keeps telling teams to try the newest model. The less popular fix, and the...

The current AI news cycle keeps telling teams to try the newest model. The less popular fix, and the one that usually saves a failed launch, is deciding which layer is allowed to fail first. A free model tier is a good cold-start layer for a read-only feature—if you give it a narrow job, an explicit fallback, and a quota signal. Otherwise it quietly becomes a production backbone that breaks the moment the free allowance changes.

MonkeyCode is a practical candidate for this experiment. Its operator advertises free model access with an allowance in the tens of millions of tokens, around 30M, plus a free server option; I have not independently verified those limits. Disclosure: This article was prepared as part of MonkeyCode's product outreach.

The failure is not usually model quality. It is the handoff: the endpoint returns 429 at 02:00, skips a field in its JSON response, or times out exactly when the feature started getting real traffic. If your only strategy is to call the model and trust the response, every one of those failures becomes a user-facing error.

Put the free tier on a side path

The pattern below treats the model as a best-effort summarizer. The primary path is the HTTP route. Inside that route, the model call is guarded by three things:

  1. A provider seam so the model call can be replaced in tests and migrations.
  2. A cache keyed by input hash so repeated requests do not burn the free allowance.
  3. A deterministic fallback that returns something useful instead of an exception.

A model-only path would be the wrong shape for this experiment. A cold-start path tells you whether the feature is used at all, where it fails, and how often the fallback fires—without making the free tier load-bearing.

The code

Install the dependencies and start the service:

python -m pip install fastapi uvicorn httpx pydantic pytest
uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

Create main.py:

from __future__ import annotations

import hashlib
import json
import os
from typing import Literal, Protocol

import httpx
from fastapi import Depends, FastAPI
from pydantic import BaseModel

app = FastAPI()


class SummarizeRequest(BaseModel):
    text: str


class SummarizeResponse(BaseModel):
    summary: str
    source: Literal["model", "cache", "extractive"]
    fallback_reason: str | None = None


class CompletionClient(Protocol):
    def summarize(self, text: str) -> str:
        ...


class MonkeyCodeClient(CompletionClient):
    def __init__(self, base_url: str, api_key: str) -> None:
        self.base_url = base_url.rstrip("/")
        self.headers = {"Authorization": f"Bearer {api_key}"}

    def summarize(self, text: str) -> str:
        # Replace this request and response shape with the provider's current API.
        with httpx.Client(timeout=10.0) as client:
            response = client.post(
                f"{self.base_url}/completions",
                headers=self.headers,
                json={"input": text, "task": "summarize"},
            )
            response.raise_for_status()
            data = response.json()

        output = data.get("output", "")
        if not isinstance(output, str) or not output.strip():
            raise ValueError("empty model output")
        return output.strip()


_cache: dict[str, SummarizeResponse] = {}


def cache_key(text: str) -> str:
    return hashlib.sha256(text.encode("utf-8")).hexdigest()


def extractive_fallback(text: str) -> str:
    sentences = [s.strip() for s in text.split(".") if s.strip()]
    return sentences[0] + "." if sentences else text[:120]


def log_fallback(key: str, reason: str) -> None:
    # In production, send this to structured logs instead of stdout.
    print(json.dumps({"event": "fallback", "cache_key": key, "reason": reason}))


def get_client() -> CompletionClient:
    return MonkeyCodeClient(
        base_url=os.environ["MONKEYCODE_BASE_URL"],
        api_key=os.environ["MONKEYCODE_API_KEY"],
    )


@app.post("/summarize", response_model=SummarizeResponse)
def summarize(
    req: SummarizeRequest,
    client: CompletionClient = Depends(get_client),
) -> SummarizeResponse:
    key = cache_key(req.text)
    cached = _cache.get(key)
    if cached is not None:
        return cached

    try:
        summary = client.summarize(req.text)
        result = SummarizeResponse(summary=summary, source="model")
    except Exception as exc:
        result = SummarizeResponse(
            summary=extractive_fallback(req.text),
            source="extractive",
            fallback_reason=type(exc).__name__,
        )
        log_fallback(key, result.fallback_reason or "unknown")

    _cache[key] = result
    return result
Enter fullscreen mode Exit fullscreen mode

The important boundary is CompletionClient. The route does not import a model SDK and does not know which provider is behind the call. That makes the free tier replaceable instead of structural.

Prove the fallback works

The fastest way to trust a free tier is to test the failure before it happens in production. With FastAPI's TestClient, you can make the client raise a quota error and assert the route still returns a useful response.

Create test_app.py:

from fastapi.testclient import TestClient

from main import app


def test_quota_falls_back_without_crashing(monkeypatch):
    class QuotaClient:
        def summarize(self, text: str) -> str:
            raise RuntimeError("429 quota exceeded")

    monkeypatch.setattr("main.get_client", lambda: QuotaClient())

    response = TestClient(app).post(
        "/summarize",
        json={"text": "Free tier is useful. It falls back cleanly. Do not depend on it."},
    )

    assert response.status_code == 200
    data = response.json()
    assert data["source"] == "extractive"
    assert data["fallback_reason"] == "RuntimeError"
    assert data["summary"].startswith("Free tier is useful.")
Enter fullscreen mode Exit fullscreen mode

Run it with pytest -q. A passing test means the system has a named failure mode instead of an unhandled traceback.

What to watch before you pay for uptime

The fallback path is useful, but it can also hide problems. Log the fallback reason and measure it as a rate, not as an afterthought. If RuntimeError becomes frequent, the quota or request shape changed. If ValueError becomes frequent, the model response contract changed. Both are signals to update the adapter, not to keep serving extractive summaries forever.

The free server option is also useful here as a disposable environment. Deploy the service, set MONKEYCODE_BASE_URL and MONKEYCODE_API_KEY, and let it run against scratch data. Do not couple the server to persistent state that matters; treat the slot as something you can recreate from environment variables.

When this design fits

Use the cold-start path when Avoid it when
The feature is read-only: summaries, tags, titles, triage notes The output drives writes, payments, auth, or compliance decisions
You can degrade to a deterministic extractive fallback The answer must always be model-generated
You need a real usage signal before paying for a production model You are sending PII or sensitive business data
You want to test rate-limit and malformed-response behavior cheaply You need a strict sub-100ms latency budget with no cache hit available

A free tier is a good place to learn failure behavior. It is a bad place to promise availability. Keep the model behind a seam, return cached or deterministic output when the provider cannot answer, and let the fallback rate decide whether the feature deserves a paid production dependency.

If you are evaluating MonkeyCode's free server option, the service above is small enough to run there while you collect a day of real fallback signals. Change one provider call later, not every call site in the application.