AI & ML / Inference & serving / 04_the_chat_api.md

The chat API surface

Updated 5 interview angles 5 min read source
On this page6
  1. One request, one shape
  2. The API is stateless
  3. Streaming changes the felt latency, not the total
  4. Picking a model, and the size question
  5. Related
  6. Interview angle

The chat API surface

What you actually send and receive when you call a model. Everyone writes this code; fewer can explain why it has the shape it does, and the shape is what the follow-up questions are about.

One request, one shape

The Chat Completions API — a list of messages in, one message out — is the de-facto standard. OpenAI defined it, and enough providers copied it that “OpenAI-compatible” is now a feature bullet: Groq, Together, Fireworks, vLLM, Ollama and most gateways accept the same request body.

python
messages = [
    {"role": "system", "content": "Be brief."},
    {"role": "user", "content": "What is a KV cache?"},
]
resp = client.chat.completions.create(
    model="gpt-5.6", messages=messages,
)
print(resp.choices[0].message.content)

That compatibility is a portability story, not a capability one. The moment you want prompt caching, extended thinking or provider-specific tool semantics you are on native SDKs, because the shared shape only covers the common subset.

The four roles

Role Who is speaking Notes
system the operator standing rules, persona, tools
user the human the actual request
assistant the model its previous replies
tool your code results you hand back

The system prompt is the operator’s instructions, not the user’s. That distinction is the whole basis of prompt-injection defence: content arriving in a user or tool message is data, however imperative it sounds. See Prompt injection.

The assistant role appearing in your input is what makes multi-turn work — you are replaying the conversation, not continuing a session.

The API is stateless

The provider remembers nothing between calls. Every request carries the entire history, and you are billed for all of it, every turn.

text
turn 1   send: [sys, u1]                    -> a1
turn 2   send: [sys, u1, a1, u2]            -> a2
turn 3   send: [sys, u1, a1, u2, a2, u3]    -> a3

Two consequences worth stating out loud in an interview:

  1. Cost grows quadratically with conversation length if you resend everything. This is the entire reason memory strategies exist — see Memory Strategies for LLM Conversations and Agents.
  2. Conversation state is your problem. Session id, transcript storage and what to replay are application concerns.

Stateful server-side conversation APIs do exist, and prompt caching softens the cost curve, but the default mental model is: stateless, resend everything.

Prompt tokens and completion tokens are priced apart

Kind What it is Relative price
Prompt (input) everything you sent cheaper
Completion (output) what the model wrote typically 3-5x
Cached input a prefix served from cache much cheaper

usage on the response reports the real counts — use them rather than estimating, and only fall back to a tokenizer when a provider omits them.

Because output costs multiples of input, “make it cheaper” usually means cap the output, not trim the prompt. A verbose format like JSON with long key names is a recurring, invisible cost.

Streaming changes the felt latency, not the total

Streaming returns tokens as they are generated, so the user sees text at time to first token rather than after the whole completion. Total time is unchanged, sometimes slightly worse.

python
stream = client.chat.completions.create(
    model="gpt-5.6", messages=messages, stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content
    if delta:
        print(delta, end="", flush=True)

Gotcha: you cannot validate a structured response you have already shown the user. If output must be schema-valid, either do not stream it or stream to a buffer and render only after it parses. See LLM JSON Validation — Common Interview Questions and Answers.

Streaming also complicates error handling: a failure mid-stream arrives after a 200 OK, so the transport succeeded and the response did not.

Picking a model, and the size question

The model string is the biggest single lever on cost, latency and quality. Parameter counts (8B, 70B, 405B) are a rough proxy: bigger models follow multi-step instructions and choose tools more reliably, and cost more per token.

The 2026 production norm is routing — a small model for classification, extraction and simple replies, a frontier model for reasoning and tool use. Quoting a fixed model for everything reads as not having measured.

Note: as of 2026-08, name a model class rather than a specific version when the point is capability. Version numbers move faster than notes do; see Stack baseline — 2026-2027 for the current table.

Interview angle 5

  • “Walk me through a chat completion request.” - a list of role-tagged messages in, one assistant message out. system carries operator instructions, user the request, assistant the prior replies you are replaying, tool the results you hand back. The response reports token usage, which is what you bill and log against.
  • “What does OpenAI-compatible actually get you?” - portability of the common subset: swap a base URL and keep the request body. It does not get you prompt caching, extended thinking or provider-specific tool semantics, so treat it as a migration convenience rather than an abstraction you can build on.
  • “The API is stateless - what follows from that?” - you resend the whole history each turn, so cost grows quadratically with conversation length and session state is the application’s job. That is the reason summarisation, sliding windows and retrieval-based memory exist at all.
  • “Why is output more expensive than input?” - input is processed in one parallel prefill pass; output is generated one token at a time, each pass reading the whole KV cache. Practically it means capping max_tokens and choosing a terse response format saves more than trimming the prompt.
  • “Does streaming make it faster?” - no, it makes it feel faster by cutting time to first token; total latency is unchanged. The costs are that you cannot validate what you have already displayed, and errors can arrive after a 200.