A *skyl.Response carries the assistant's turn, why generation stopped, what it
cost, and — always — the provider's untouched body. This chapter covers each,
including the parts where providers disagree and skyl has to normalise.
In this chapter
- Every field on
Response, and which ones are always populated - How to get text, tool calls, or both
- What each stop reason means and which are provider-specific
- Why cached tokens are a breakdown of your input, not an addition to it
- Why the model that answered may not be the one you asked for
- How to reach anything skyl does not model
The response object#
resp, err := client.Complete(ctx, req)
resp.ID // the provider's identifier, when it gives one
resp.Provider // "anthropic"
resp.Model // the model that ACTUALLY answered
resp.Message // the assistant's turn, ready to append
resp.StopReason // why generation ended
resp.Usage // token consumption
resp.Raw // the untouched provider body — ALWAYS populatedresp, err := client.Complete(ctx, req)
resp.ID // the provider's identifier, when it gives one
resp.Provider // "anthropic"
resp.Model // the model that ACTUALLY answered
resp.Message // the assistant's turn, ready to append
resp.StopReason // why generation ended
resp.Usage // token consumption
resp.Raw // the untouched provider body — ALWAYS populatedRead The Response Object for every field in detail, including which are best-effort.
Text and tool calls#
Read Text and Tool Calls for the difference between Text() and Message.Parts, and why reasoning content is not in either.
Stop reasons#
| Constant | Value | Meaning | Provider values |
|---|---|---|---|
StopEndTurn | end_turn | The model finished naturally. | end_turn, stop, STOP |
StopMaxTokens | max_tokens | The output hit Request.MaxTokens. The response is truncated — treat it as incomplete. | max_tokens, length, MAX_TOKENS, model_context_window_exceeded |
StopToolUse | tool_use | The model wants a tool run. Execute the calls and send the results back. | tool_use, tool_calls, function_call |
StopStopSequence | stop_sequence | A sequence from Request.Stop was produced. Only ever comes from Anthropic — OpenAI reports a stop-sequence hit as plain "stop", so it arrives as StopEndTurn. | stop_sequence (Anthropic only) |
StopRefusal | refusal | The model or its safety classifiers declined. Content may be empty or partial; do not retry the same request. | refusal, content_filter, SAFETY, RECITATION, BLOCKLIST, PROHIBITED_CONTENT, SPII |
StopUnknown | unknown | The provider reported something skyl does not model. Read Response.Raw. | anything else, including pause_turn, OTHER, MALFORMED_FUNCTION_CALL |
Read Stop Reasons for the two that are effectively provider-specific, and how to handle a truncated answer.
Token usage#
Read Token Usage and Caching — this is the page that stops your cost report being wrong by the size of your cache.
Which model answered#
Read Which Model Actually Answered for why resp.Model is read from the response rather than echoed.
Raw provider JSON#
Read Reading Raw Provider JSON for the escape hatch that means nothing a provider sends is ever lost.
What’s next?
Start with The Response Object. If you are chasing a cost discrepancy, go straight to Token Usage and Caching — it is almost certainly the inclusion semantics.