All posts

5 min readparanine

Moving an OpenAI-compatible codebase to P/9 in one change

The gateway speaks the OpenAI chat completions API, streaming included. Change the base URL and the model string, scope a key, and read the cost of each request back from the response.

The whole point of an OpenAI-compatible gateway is that the migration is boring. This post is the boring version: what to change, what stays the same, and what to look at once the first request comes back.

1. Point the client at the gateway

Every SDK that speaks the OpenAI API takes a base URL. Set it to the P/9 gateway and pass a P/9 key as the API key. Nothing else in the client changes.

from openai import OpenAI

client = OpenAI(
    base_url="https://sovereign.intelligentinference.ai/v1",
    api_key=os.environ["P9_API_KEY"],
)

reply = client.chat.completions.create(
    model="paranine/gpt-oss-120b(Global)",
    messages=[{"role": "user", "content": "Hello"}],
    stream=True,
)
for chunk in reply:
    print(chunk.choices[0].delta.content or "", end="")

2. Use the route id, exactly as written

The model field takes a P/9 route id from the catalogue, punctuation and capitalisation included: paranine/gpt-oss-120b(Global), paranine/Kimi-K3(Global), and so on. The id is an alias the gateway keeps stable when the weights behind it move, so a model upgrade on our side is not a code change on yours. Keep it in configuration, not in code, and switching routes (or pools, as the PK NPU routes open) is a deploy, not a pull request.

3. Scope the key

Create the key on the dashboard with the caller's name, an expiry if it is not permanent, a credit limit in rupees, and the list of routes it is allowed to reach. The gateway enforces all three: 401 after expiry, 402 at the cap, 403 outside scope. A key that can only reach one route cannot quietly start billing another.

4. Read the cost back

The gateway reports what a request cost. Send the X-P9-Debug-Metrics header and the response carries the time to first token, the throughput and the exact rupee cost of that call, which is the same number the ledger debited. The playground on the dashboard shows the same figures for any prompt, so you can check a route's cost before writing a line.

curl https://sovereign.intelligentinference.ai/v1/chat/completions \
  -H "Authorization: Bearer $P9_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-P9-Debug-Metrics: 1" \
  -d '{
    "model": "paranine/Kimi-K3(Global)",
    "messages": [{ "role": "user", "content": "Summarise this in one line." }]
  }'

What stays the same, and what is not there

  • Streaming, message roles, temperature and max_tokens work as they do against OpenAI. Reasoning models spend completion budget on thinking before the visible reply, so give them room.
  • Embeddings are a separate route (the Qwen3 embedding model) and return vectors, not tokens; the catalogue prices them on input only.
  • There is no fine-tuning endpoint and no file upload. If your integration depends on either, it is not a one-line move yet.

That is the migration. One base URL, one model string, one scoped key, and a cost you can read off the response instead of waiting for an invoice.