An llm_call holding only tokens and cost answers what a call spent and
nothing about why. This adds the rest of what RubyLLM already puts on the
event, enough to retrace a call and reproduce it.
finish_reason matters most: max_tokens means the answer was cut off, and
without it a truncated document extraction reads exactly like a complete
one. attachments and attachment_types show that a call carried two images
and a PDF. params holds the settings that produced the answer, tools names
what the model could reach, and provider_request_id joins the record to the
provider's own, read from the response headers.
provider_options is filtered twice before storage: through the app's
parameter filter, and recursively against the credential-name matcher that
catches X-Api-Key on a header. The default filter is password-shaped, so a
per-call api_key went through untouched -- including ones nested in
extra_headers or a fallbacks array.
Retries needed nothing new: RubyLLM retries through Faraday, so each
physical attempt is already an outgoing_request.
🤖 Generated with [Claude Code](https://claude.com/claude-code)