PUNKthe adaptive runtime

//DOCS OpenAI Gateway

OpenAI-compatible AI gateway setup for routing existing OpenAI SDK traffic through Punk.

OpenAI-Compatible AI Gateway

Punk is an OpenAI-compatible AI gateway for production agents. If an app already calls client.chat.completions.create(...), the first integration is a base URL change: send the same request shape through Punk, add identity headers, and inspect the route explanation that comes back with every run.

Use this path when you want agent observability, exact caching, governance, trace-based learning, provider failover, and route explanations without changing the rest of the application.

Install

You do not need the Punk SDK for gateway-only traffic. Keep using the official OpenAI client:

npm install openai

Create a Punk tenant API key in the hosted dashboard, then point the OpenAI client at Punk:

export PUNK_BASE_URL=https://app.punktechnologies.com
export PUNK_API_KEY=pk_...

Provider keys and BYOK credentials live in Punk's hosted control plane, not in this app.

For production-like enterprise traffic, start with an observe-mode key and use Enterprise Pilot for the pilot sequence.

First-Run Checklist

  1. Set the OpenAI client base URL to $PUNK_BASE_URL/v1.
  2. Use the Punk tenant API key as the OpenAI client key or bearer token.
  3. Send X-Punk-App, X-Punk-Agent, and X-Punk-Subject.
  4. Send one deterministic request and record x-punk-run-id plus x-punk-route.
  5. Open the run explanation. Check readiness separately with an admin key when you are validating deployment posture.

Working curl smoke test:

curl -sS -D /tmp/punk-headers -o /tmp/punk-body \
  "$PUNK_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $PUNK_API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Punk-App: support-app" \
  -H "X-Punk-Agent: support-agent" \
  -H "X-Punk-Subject: user-123" \
  -d '{
    "model": "gpt-4o-mini",
    "temperature": 0,
    "messages": [
      { "role": "user", "content": "Classify this ticket: I was charged twice. Return JSON." }
    ]
  }'

RUN_ID=$(awk 'tolower($1)=="x-punk-run-id:" {print $2}' /tmp/punk-headers | tr -d '\r' | tail -n 1)
ROUTE=$(awk 'tolower($1)=="x-punk-route:" {print $2}' /tmp/punk-headers | tr -d '\r' | tail -n 1)

jq -r '.choices[0].message.content' /tmp/punk-body
printf 'route=%s runId=%s\n' "$ROUTE" "$RUN_ID"

curl -sS -H "Authorization: Bearer $PUNK_API_KEY" \
  "$PUNK_BASE_URL/api/v1/runs/$RUN_ID" \
  | jq '{route: .run.route, provider: .run.provider, explanation: .run.routeExplanation}'

Admin readiness check:

curl -sS -H "Authorization: Bearer ${PUNK_ADMIN_API_KEY:-$PUNK_API_KEY}" \
  "$PUNK_BASE_URL/api/v1/readiness" \
  | jq '{summary, attention: [.items[]? | select(.status=="attention") | {id, message, action}]}'

The first request normally routes live. Repeat the exact same safe request to test exact_cache. In observe mode, Punk returns the live provider response and records would-have optimization as ghost evidence in the run explanation. Artifact and model-substitution routes require repeated stable traffic, replay/shadow evidence, and promotion.

One-Line Integration

import OpenAI from "openai";

const punkBaseUrl = process.env.PUNK_BASE_URL ?? "https://app.punktechnologies.com";

const client = new OpenAI({
  baseURL: `${punkBaseUrl.replace(/\/$/, "")}/v1`,
  apiKey: process.env.PUNK_API_KEY,
  defaultHeaders: {
    "X-Punk-App": "support-app",
    "X-Punk-Agent": "support-agent",
    "X-Punk-Subject": "user-123"
  }
});

const result = await client.chat.completions.create({
  model: "gpt-4o-mini",
  temperature: 0,
  messages: [
    { role: "user", content: "Classify this ticket: I was charged twice." }
  ]
});

console.log(result.choices[0]?.message?.content);

Everything above the client constructor stays OpenAI-shaped. Streaming, tools, response_format, and normal model parameters continue to use the OpenAI client surface.

What Punk Adds

CapabilityWhat happens after the base URL change
TraceEvery model request becomes a run with cost, latency, input, output, identity, and route evidence.
CacheRepeated safe work can route through exact cache before touching the provider again.
GovernTenant policy can block, require approval, redact, or allow requests based on identity and side-effect posture.
LearnStable repeated patterns can become candidate deterministic artifacts after replay and shadow evidence.
ExplainResponses include x-punk-run-id and x-punk-route; run detail explains why that route won.

Read The Route

The OpenAI SDK exposes response headers through .withResponse():

const { data, response } = await client.chat.completions
  .create({
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: "Say hello." }]
  })
  .withResponse();

console.log(data.choices[0]?.message?.content);
console.log(response.headers.get("x-punk-route"));
console.log(response.headers.get("x-punk-run-id"));

Then inspect the run:

curl -sS -H "Authorization: Bearer $PUNK_API_KEY" \
  "$PUNK_BASE_URL/api/v1/runs/<runId>" \
  | jq .run.routeExplanation

First-Run Troubleshooting

SymptomCheckFix
No runs appearResponse lacks x-punk-run-id, or /api/v1/runs?limit=5 is empty.Confirm the client base URL ends in /v1, the request goes to Punk, and the key is a Punk key.
Missing identityRun detail shows default-app, anonymous, or an unexpected app.Send all three X-Punk-* headers. If the API key is app-pinned, its app overrides X-Punk-App.
Auth failure401 means missing/invalid bearer token; /api/v1/readiness can return 403.Use Authorization: Bearer <Punk tenant key>. Use an admin key for readiness.
Mock vs live confusionRun provider is mock, or readiness says only mock is configured.Add a live provider/BYOK key in Punk. Local PUNK_PROVIDER=mock deliberately forces deterministic simulation.
No optimization yetRoute is live; alternatives say cache miss, no pattern, or gate not satisfied.Repeat stable requests. Observe-mode keys record ghost savings; artifacts serve only after replay, shadow, and promotion evidence.

When To Add The SDK

The gateway sees model traffic. Add @punktechnologies/sdk when you also want Punk to see application context the model request does not include:

NeedSDK feature
Trace toolstraceTool(...) records tool calls and side-effect levels.
Cache read-only toolstraceTool({ sideEffectLevel: 1, ttlSeconds: 300, ... }) participates in the tool-result cache.
Send feedbackfeedback(runId, rating, correction) closes the learning loop.
Read evidencerunDetail, receipt, and evidencePacket return route and audit material.
Fetch web contextfetchSom(url) returns compact semantic page context instead of raw HTML.

Read next: SDK, Agent Observability & Tool Caching, API.

For OpenRouter provider/model slugs through the same OpenAI-compatible wire, see OpenRouter.