[release] add an observability agent reporter for failed release tests - #65906
[release] add an observability agent reporter for failed release tests#65906sai-miduthuri wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces the ObservabilityAgentReporter to query the Anyscale observability agent for failed release test jobs and log their analysis. It also adds corresponding unit tests and a temporary forced failure in hello_world.py for pipeline verification. The review feedback is highly actionable, pointing out the need to revert the temporary test failure before merging, recommending an early exit when ANYSCALE_CLI_TOKEN is missing to prevent noisy tracebacks, and suggesting safer dictionary lookups to avoid potential AttributeError crashes when handling null values in API responses.
I am having trouble creating individual review comments. Click here to see my feedback.
release/hello_world_tests/hello_world.py (6-10)
This temporary change forces a failure for testing. It must be reverted before merging this pull request to avoid breaking the hello_world release test.
release/ray_release/reporter/observability_agent.py (93-99)
Since this reporter is appended unconditionally to the reporters list, it will run in environments (such as OSS CI or local runs) where ANYSCALE_CLI_TOKEN is not set. Currently, a missing token causes _post to raise a RuntimeError, which is caught and logged as a full traceback via logger.exception. To avoid misleading and noisy tracebacks in the logs of these environments, consider adding an early exit check for the token in report_result.
job_id = result.job_id
if not job_id:
logger.info(
f"Skip triggering the observability agent for test "
f"{test.get_name()}; the test run has no Anyscale job id"
)
return
if not os.environ.get("ANYSCALE_CLI_TOKEN"):
logger.info(
f"Skip triggering the observability agent for test "
f"{test.get_name()}; ANYSCALE_CLI_TOKEN is not set"
)
returnrelease/ray_release/reporter/observability_agent.py (120-122)
If the response contains "result": null, response.get("result", {}) will return None instead of {} because the key exists. This will cause an AttributeError when calling .get() on query_result. Similarly, if analysis or metadata are null in the JSON response, calling .get() on them will also raise an AttributeError. Using or {} handles these cases safely.
query_result = response.get("result") or {}
summary = (query_result.get("analysis") or {}).get("summary")
slack_thread = (query_result.get("metadata") or {}).get("slack_thread")
release/ray_release/reporter/observability_agent.py (144)
If "result" is null in the response, response.get("result", {}) will return None, causing an AttributeError when calling .get("debug_session_id"). Using or {} prevents this potential crash.
debug_session_id = (response.get("result") or {}).get("debug_session_id")
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 006cdad. Configure here.
|
The Observability Agent trigger behavior is showcased in the buildkite/release CI test for commit 6f88ae3. The failing The failing |
272bd22 to
30ca343
Compare
Add ObservabilityAgentReporter, which creates an Anyscale observability agent debug session for the job of a failed release test and asks it why the job failed, logging the summary of its analysis at the end of the buildkite step along with a link to the slack thread holding the full report. It triggers on the RUNTIME_ERROR, ERROR and UNKNOWN result statuses and is a no-op otherwise, so that infra failures the agent cannot explain are left alone. Reporting failures are caught and logged, as the reporting loop in glue.py does not guard against them. Also add SKIP_COMMAND_FAILURES, off by default, which skips the failures raised by the test command itself. Note that enabling it leaves only the RUNTIME_ERROR and UNKNOWN statuses triggering, as a result with the ERROR status always carries one of those return codes. Test plan: - bazel test //release:test_observability_agent_reporter (12 tests) - bazel test //release:test_glue //release:test_run_script Signed-off-by: sai.miduthuri <sai.miduthuri@anyscale.com>
Temporarily raise an exception inside the hello_world ray task, so that a release test run on this PR fails the way a real test does and triggers the observability agent reporter added in the previous commit. Note that the agent fires on the retried attempt rather than the first one: run_release_test.sh sets BUILDKITE_MAX_RETRIES=1 and BUILDKITE_TIME_LIMIT_FOR_RETRY=10800, so _is_transient_error rewrites the first attempt's status to TRANSIENT_INFRA_ERROR, which is not a trigger status. Revert this commit before taking the PR out of draft. Signed-off-by: sai.miduthuri <sai.miduthuri@anyscale.com>
…agent" This reverts commit 6f88ae3. The forced failure has served its purpose: the release pipeline runs linked from this PR show the observability agent reporter triggering on a real test failure and skipping the infra failures. Restore hello_world so the PR carries only the reporter itself. Signed-off-by: sai.miduthuri <sai.miduthuri@anyscale.com>
…reporter
The agent sends explicit nulls, and `response.get("result", {})` returns
None when the key is present, so the following lookup raised an
AttributeError.
In report_result that parsing sits outside the try/except around the HTTP
calls, and glue.py runs the reporting loop unguarded, so a null response
would have failed the release test with an unrelated traceback. In
_create_debug_session the same lookup is inside the try, so the only cost
there was an AttributeError in place of the error the code means to raise
about the missing debug_session_id.
Both now use `or {}`, with a test for each: the query response covers null
result, analysis and metadata, and the create response asserts the failure
names the missing field.
Signed-off-by: sai.miduthuri <sai.miduthuri@anyscale.com>
30ca343 to
5f31cc1
Compare

Description
Adds
ObservabilityAgentReporter, a release test reporter that asks the Anyscaleobservability agent why a failed release test job failed, and logs the answer at the
end of the buildkite step.
When a release test run ends in an application-level failure, the reporter creates an
observability agent debug session for the test's Anyscale job, asks it "Why did this
job fail?", and logs the summary of the analysis together with a link to the slack
thread that holds the full report. The intent is that whoever triages a red release
test gets a first read on the failure without leaving the buildkite log.
It triggers only on the
RUNTIME_ERROR,ERRORandUNKNOWNresult statuses, and isa no-op for everything else, so that infra failures the agent cannot explain
(
INFRA_ERROR,INFRA_TIMEOUT,TRANSIENT_INFRA_ERROR) are left alone. It is also ano-op for failures that never got as far as creating an Anyscale job.
Related issues
None.
Additional information
Where the job id comes from.
AnyscaleJobManageralready captures the Anyscaleproduction job id when it submits the job,
AnyscaleJobRunner.job_id()exposes it, andglue.pystores it onresult.job_idbefore the reporting loop runs. The reporterreads it from there rather than making a second lookup.
Reporting never changes a test outcome.
run_release_test_anyscaleruns thereporting loop without guarding
reporter.report_result(...), so every failure in thisreporter is caught and logged. An observability agent outage cannot turn a passing test
red, or add noise to the traceback of a failing one.
Log volume. Only the summary and the slack thread link are logged at INFO. The full
response, which also carries the findings, issues and next steps, goes to
logger.debugso it stays available without cluttering the step output. A response with no
metadata.slack_threadis logged as an error, since every response is expected to carryone.
SKIP_COMMAND_FAILURESships off. The reporter carries a gated skip for failuresraised by the test command itself (
COMMAND_ERROR,COMMAND_ALERT,COMMAND_TIMEOUT,PREPARE_ERROR), disabled by default and pending a decision in review. Worth knowingbefore flipping it: a result with the
ERRORstatus always carries one of those returncodes, so enabling the skip leaves only
RUNTIME_ERRORandUNKNOWNtriggering theagent.
Sample output, from a run against a real failed staging job:
Not a duplicate
Searched the open PRs for work in this area before starting; there is no open PR adding
an observability agent reporter, or otherwise touching
release/ray_release/reporter/:Tests
The reporter was also exercised end-to-end against the staging observability agent with
a real failed job id, before the logging was narrowed to the summary: the debug session
was created, the query returned an analysis, and the no-op paths made no HTTP calls at
all. The current code is covered by the unit tests above.
AI assistance
AI assistance (Claude Code) was used to write this change.