Record why the assistant failed on the message - #5070
Conversation
|
Based on my analysis, let me determine which checks apply and verify them. Changed files:
Scoping analysis:
Other checks: |
ed7e3fd to
f742165
Compare
3683332 to
5a28e60
Compare
f742165 to
4cff9e9
Compare
1813938 to
d16eb79
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## timeout-oban-lifecycle #5070 +/- ##
========================================================
Coverage ? 90.5%
========================================================
Files ? 421
Lines ? 20024
Branches ? 0
========================================================
Hits ? 18131
Misses ? 1893
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
4cff9e9 to
fe286cd
Compare
d16eb79 to
50ae521
Compare
fe286cd to
0797f22
Compare
50ae521 to
d340e01
Compare
Every failure looks the same to a user: the message goes red and offers a retry. Apollo being down, Apollo being slow, and a deploy interrupting the run are indistinguishable, so people retry a hard-down service forever and support cannot triage from a screenshot. The reasons already exist. handle_error_response builds strings written for a person to read, and they were logged and then dropped - the channel only ever sent a message id and the word error. They now live on the message as a category and a sentence. Columns rather than a broadcast, because the failure that matters most is a deploy interrupting a run, and that is exactly when the browser reconnects to a different node with the broadcast long gone. It is also frequently written by a different process than the one that failed. Two columns rather than one blob: the category is what the frontend switches on and what you group by when someone asks how many timeouts there were last week. The sentence is user-facing prose only. Raw error terms and upstream response bodies stay in the log, where they cannot leak an internal hostname or a stack trace to whoever is reading the chat panel. A raise on our side is the sharpest case of that, since the exception text carries module names and inspected payloads, and it now has a test that fails if any of it reaches the row. It is bounded, too. It is written through Ecto.Changeset.change/2, which applies no validation, and read back and re-sent on every channel join, so an unbounded string would be paid for on each one. The internal-failure branch no longer logs a second time. Whoever raised already logged the exception, and every extra Logger.error is another Sentry event for one failure. A returned reason is now a named type. The specs said String.t() | Ecto.Changeset.t(), which this change makes untrue - the internal tuple is neither - so they say stream_error() instead, and dialyzer can hold the callers to it.
0797f22 to
078cb15
Compare
d340e01 to
e815b6a
Compare
Description
Every AI chat failure looks the same, to the user and to us. A hung Apollo, a dropped connection and a deploy interrupting a run all end as one generic error, so people retry a hard-down service forever and support cannot tell from a screenshot which happened.
The reasons already exist as sentences meant for a person to read, and were logged and thrown away: the channel only ever sent a message id and the word error. They now live on the message, as a category to group by and a sentence to show.
Columns rather than a broadcast, because the failure that matters most is a deploy interrupting a run, and that is exactly when the browser reconnects to a different node with the broadcast long gone.
This is the plumbing, not the visible change. Nothing in the frontend reads either field yet. What it buys is that the reason is recorded, survives a reconnect, and reaches the client.
The sentence is user-facing prose only; raw error terms stay in the log, where they cannot leak an internal hostname or a stack trace into the chat panel. It is bounded, since it is written through
change/2, which validates nothing, and re-sent on every channel join.Validation steps
APOLLO_ENDPOINTat a port nothing is listening on), then send a message from the assistant panel.failure_message.Additional notes for the reviewer
Nothing in the frontend reads the two new columns yet, so no user sees anything different once this merges. The reason has to survive a reconnect before anything can show it.
AI Usage
Please disclose whether you've used AI anywhere in this PR (it's cool, we just want to know!):
You can read more details in our Responsible AI Policy
Pre-submission checklist
/reviewwith Claude Code):owner,:admin,:editor,:viewer)