Commit Graph
6 Commits
Author SHA1 Message Date
ryan-crabbe-berriandGitHub f047b1571e fix(otel): capture 401 error details in management endpoint spans (#29535)
Auth failures on management endpoints such as team/list and
organization/list (invalid or expired keys) were raised as
ProxyException, whose __str__ returned an empty string, so the OTEL
SERVER span recorded an error with no message. ProxyException now
stringifies to its message, get_error_information prefers the explicit
.message attribute, and the proxy exception handlers stamp a consistent
error.type, error.code and error.message on the span

Resolves LIT-3515
2026-06-02 16:40:30 -07:00
ryan-crabbe-berriandGitHub c754c560dd fix(proxy): link passthrough success spans to the SERVER root OTEL span (#29315)
* fix(proxy): link passthrough success spans to the SERVER root OTEL span

Passthrough requests never wired user_api_key_dict.parent_otel_span into the
logging metadata, so on success the litellm_request span orphaned into its own
trace and the "Received Proxy Server Request" root span was never ended. Setting
it once in _init_kwargs_for_pass_through_endpoint fixes both the non-streaming
and streaming paths, since update_environment_variables copies that metadata onto
the logging object's model_call_details, which is what the OTEL handler reads.

Resolves LIT-3443

* fix(proxy): set passthrough parent span after client metadata merge

Greptile flagged that litellm_parent_otel_span was assigned before the
_metadata.update() calls that merge request-body metadata, so a client body
mirroring the internal key could overwrite the real span with a JSON scalar
and null the fix for that request. Move the assignment after the merge and
add a regression test that fails on the old ordering.

* fix(proxy): also set user_api_key after client metadata merge

Per Greptile, user_api_key had the same clobber window as the parent span: a
passthrough request body mirroring the key could overwrite the authenticated
value in the logged metadata. Move it into the same post-merge block and add a
deterministic contract test asserting both internal keys resist client-supplied
metadata.
2026-05-29 21:30:45 -07:00
30551de371 fix(otel): export SERVER span on management-endpoint success without http_request (#28794)
Co-authored-by: Yassin Kortam <yassinkortam@Yassins-MacBook-Pro.local>
2026-05-25 12:13:17 -07:00
ryan-crabbe-berriandGitHub 886e91b85e fix(otel): stamp http.response.status_code on all error responses (#28405)
* fix(otel): stamp http.response.status_code on all error responses

httpx.HTTPStatusError exposes status under .response.status_code, not as a
top-level attr, so unified-endpoint 5xx failures left the SERVER span without
a status. The admin hooks only wrote a child span and never stamped or ended
the parent at all, so admin 4xx/5xx (and success) responses were invisible
to dashboards. Adds a fallback to .response.status_code in get_error_information,
and ends the parent SERVER span in async_management_endpoint_{success,failure}_hook
with the same _record_exception_on_span helper the unified path uses.

Resolves LIT-3193

* test(otel): exercise httpx.HTTPStatusError through admin path

Pins the contract that get_error_information's response.status_code fallback
is reachable from any entry point — without this, a future refactor that
bypasses _record_exception_on_span in the admin hooks could regress for
httpx-wrapped exceptions while the unified suite still passes.

* chore(otel): trim verbose comments in LIT-3193 changes

Tighten docstrings and remove redundant section dividers/inline narration.
Behavior is unchanged.

* fix(otel): set span.status on management hook parent SERVER span

Mirror the unified failure path: stamp StatusCode.ERROR on the parent
SERVER span before recording the exception, and StatusCode.OK before
ending it on success. Without this, OTEL backends filtering on span
status (the idiomatic primitive) miss admin-endpoint failures even
though the http.response.status_code attribute is correct.

Extend assert_server_span_attrs to assert span.status.status_code
matches the expected outcome so the gap can't regress.

* fix(otel): close SERVER span on body-validation and unhandled errors

Stash the SERVER span on request.state in auth so FastAPI exception
handlers can finish it for failures that occur after auth but before
the route handler (e.g. /model/new TypeError, /key/generate
RequestValidationError). Without this, those requests left dangling
spans missing http.response.status_code.

Resolves LIT-3193

* fix(otel): generic 500 body, log exception details server-side

Don't leak str(exc) and type(exc).__name__ to clients on uncaught
exceptions. The full traceback is logged via verbose_proxy_logger and
the SERVER span still gets http.response.status_code=500.

Resolves LIT-3193

* fix(otel): stamp http.response.status_code on every SERVER span path

Closes three remaining gaps where the proxy SERVER span ended without
the http.response.status_code attribute:

1. ProxyException raised from _read_request_body (e.g. invalid JSON
   body) bubbled out of user_api_key_auth before the SERVER span was
   created, so the FastAPI handler had nothing to close and the trace
   never reached the backend. Hoist the span creation to a new
   idempotent _ensure_parent_otel_span_on_request_state helper called
   at the top of user_api_key_auth; wire openai_exception_handler to
   close the dangling span. Covers /v1/chat/completions, /v1/messages,
   /v1/responses (shared handler).

2. /v1/responses success — _handle_success ends the proxy span before
   async_post_call_success_hook fires on this path, so the hook's
   set_response_status_code_attribute(200) silently no-op'd against an
   ended span. Stamp 200 + set OK status at the close site in
   _handle_success / _end_proxy_span_from_kwargs via a shared
   _close_proxy_span_ok helper, so the attribute lands regardless of
   which success hook runs first.

3. Failure path for exceptions without code/status_code (e.g. a bare
   TypeError surfacing through _handle_llm_api_exception) — empty
   error_information.error_code → _record_exception_on_span skips the
   stamp → the hook ends the span. Default to 500 in
   async_post_call_failure_hook so the attribute is always set.

Resolves LIT-3193
2026-05-23 19:21:32 +00:00
Ryan Crabbe dc46467235 fix(tests): replace deprecated Bedrock Claude 3.7 Sonnet model ID
AWS Bedrock has reached end-of-life for `claude-3-7-sonnet-20250219-v1:0`,
returning 404s with "This model version has reached the end of its life."
Update test references to `claude-sonnet-4-5-20250929-v1:0` (same capability
surface: thinking, tools, prompt caching, PDF input, vision, computer use).

The bedrock/invoke pass-through tests stay on Sonnet 3.5 since Sonnet 4.5
is converse-only on Bedrock.
2026-04-28 14:24:19 -07:00
Keith Decker 5dcdbb35da add metrics and logs (events) with semconv attributes 2025-09-02 09:20:25 -06:00