Skip to content

AI Gateway MLflow chat-completions omits finish_reason on natural stop (breaks inkling in Pi) #215

Description

@dgokeeffe

Summary

The AI Gateway MLflow chat-completions endpoint (/ai-gateway/mlflow/v1/chat/completions) omits the OpenAI-spec-required finish_reason field on the terminal chunk of a streaming response when a model completes naturally (i.e. not truncated by max_tokens). The stream ends with only data: [DONE].

This breaks any OpenAI-compatible streaming client that (correctly, per spec) requires a finish_reason before the stream terminates. Concretely it makes databricks-inkling unusable through the Pi coding agent, which errors Stream ended without finish_reason.

Affected model

  • databricks-inkling (a reasoning model — emits reasoning_content deltas, then content deltas)

Non-reasoning endpoints on the same route (e.g. databricks-glm-5-2) do send finish_reason and work correctly, so this appears specific to inkling's serving stack.

Reproduction (natural completion → NO finish_reason)

curl -sN "$WS/ai-gateway/mlflow/v1/chat/completions" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"model":"databricks-inkling","max_tokens":4000,"stream":true,
       "messages":[{"role":"user","content":"reply with exactly the two letters: ok"}]}'

Observed tail of the stream:

data: {"id":"chatcmpl-...","choices":[{"delta":{"role":"assistant","content":"ok"},"index":0,"logprobs":null}], ...}

data: [DONE]

grep -c finish_reason over the full stream returns 0 — no chunk carries it.

Contrast: truncated completion DOES send finish_reason

With a small budget the model is cut off and the gateway does emit finish_reason:"length":

curl -sN ... -d '{"model":"databricks-inkling","max_tokens":32,"stream":true, ...}'
...
data: {"...","choices":[{"delta":{"...","reasoning_content":" friendly"},"finish_reason":"length","index":0,...}], ...}
data: [DONE]

So the field is wired up — it's just missing on the natural stop path (should be finish_reason:"stop").

Expected

Per the OpenAI Chat Completions streaming spec, the final content chunk (or a final empty-delta chunk) before [DONE] should carry finish_reason:"stop" on normal completion.

Impact

  • databricks-inkling cannot be used by spec-compliant streaming clients (Pi, and likely others).
  • The corresponding ucode work exposing MLflow chat-completions-only models to Pi (closes ucode pi automatic model detection #204) leaves inkling selectable but non-functional until this is fixed. Once the gateway emits finish_reason, inkling works with no further ucode change.

Environment

  • Workspace: AI Gateway v2, /ai-gateway/mlflow/v1/chat/completions
  • Client: Pi 0.80.6 (@earendil-works/pi-ai openai-completions parser throws at the missing finish_reason)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions