Skip to content

feat(api): update API spec from langfuse/langfuse 5ed4f29#1711

Closed
langfuse-bot wants to merge 1 commit into
mainfrom
api-spec-bot-5ed4f29
Closed

feat(api): update API spec from langfuse/langfuse 5ed4f29#1711
langfuse-bot wants to merge 1 commit into
mainfrom
api-spec-bot-5ed4f29

Conversation

@langfuse-bot

@langfuse-bot langfuse-bot commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Greptile Summary

This auto-generated PR (via Fern from langfuse/langfuse spec commit 5ed4f29) adds a delete evaluator endpoint to the unstable API surface, including sync and async client methods, raw HTTP client implementations, and the new DeleteEvaluatorResponse model.

  • Adds EvaluatorsClient.delete / AsyncEvaluatorsClient.delete thin wrappers delegating to the raw client, with full docstrings describing the 409/403 guard conditions.
  • Introduces DeleteEvaluatorResponse (a frozen Pydantic model with a single message field) and exports it through the full module hierarchy (typesevaluatorsunstable).
  • Both RawEvaluatorsClient.delete and AsyncRawEvaluatorsClient.delete contain a duplicate, unreachable block of status-code handlers (400, 401, 403, 404, 405) that mirrors a pre-existing pattern in other methods — a Fern codegen artifact.

Confidence Score: 4/5

The change is safe to merge — it adds a new delete endpoint that did not exist before, so no existing callers are affected.

The duplicate status-code handlers in both the sync and async delete implementations are permanently dead code: once the first matching if block raises, the second set of 400/401/403/404/405 handlers using commons_errors_* types can never execute. This means callers can never observe a commons_errors_unauthorized_error_UnauthorizedError (or similar) from this endpoint, even if the generator intended them as a fallback. The pattern is consistent with the pre-existing get method in the same file (also a Fern artifact), but it still represents unreachable logic in the newly added code.

langfuse/api/unstable/evaluators/raw_client.py — both the sync and async delete methods have duplicate unreachable status-code handlers.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant EvaluatorsClient
    participant RawEvaluatorsClient
    participant LangfuseAPI

    Caller->>EvaluatorsClient: delete(evaluator_id)
    EvaluatorsClient->>RawEvaluatorsClient: delete(evaluator_id)
    RawEvaluatorsClient->>LangfuseAPI: "DELETE /api/public/unstable/evaluators/{evaluatorId}"
    alt 2xx
        LangfuseAPI-->>RawEvaluatorsClient: DeleteEvaluatorResponse (message)
        RawEvaluatorsClient-->>EvaluatorsClient: HttpResponse[DeleteEvaluatorResponse]
        EvaluatorsClient-->>Caller: DeleteEvaluatorResponse
    else 400 Bad Request
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: BadRequestError
    else 401 Unauthorized
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: UnauthorizedError
    else 403 Forbidden (managed evaluator)
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: AccessDeniedError
    else 404 Not Found
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: NotFoundError
    else 409 Conflict (rules still reference evaluator)
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: ConflictError
    else 429 Too Many Requests
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: TooManyRequestsError
    else 500 Internal Server Error
        LangfuseAPI-->>RawEvaluatorsClient: PublicApiError
        RawEvaluatorsClient-->>Caller: InternalServerError
    end
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
langfuse/api/unstable/evaluators/raw_client.py:740-800
**Dead code: duplicate status-code handlers are never reached**

The `delete` method (both sync and async) first raises an exception for status codes 400, 401, 403, 404, and 405 using the `unstable_errors_*` / `PublicApiError` variants. Immediately after handling 500, a second identical block of `if _response.status_code == 400/401/403/404/405` handlers using `commons_errors_*` with `typing.Any` bodies follows. Because Python evaluates `if` statements in order and the first matching branch raises, the second block is permanently unreachable — callers can never receive a `commons_errors_*` exception from this endpoint. The same dead-code pattern appears in the async `delete` method. This looks like a Fern codegen artifact that was already present in the existing `get` method, but it still means the `Error`, `commons_errors_unauthorized_error_UnauthorizedError`, and related types are silently dead here.

Reviews (1): Last reviewed commit: "feat(api): update API spec from langfuse..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@langfuse-bot langfuse-bot requested a review from wochinge June 15, 2026 16:48
@github-actions

Copy link
Copy Markdown

@claude review

Comment on lines +740 to +800
),
),
)
if _response.status_code == 409:
raise ConflictError(
headers=dict(_response.headers),
body=typing.cast(
PublicApiError,
parse_obj_as(
type_=PublicApiError, # type: ignore
object_=_response.json(),
),
),
)
if _response.status_code == 429:
raise TooManyRequestsError(
headers=dict(_response.headers),
body=typing.cast(
PublicApiError,
parse_obj_as(
type_=PublicApiError, # type: ignore
object_=_response.json(),
),
),
)
if _response.status_code == 500:
raise InternalServerError(
headers=dict(_response.headers),
body=typing.cast(
PublicApiError,
parse_obj_as(
type_=PublicApiError, # type: ignore
object_=_response.json(),
),
),
)
if _response.status_code == 400:
raise Error(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
parse_obj_as(
type_=typing.Any, # type: ignore
object_=_response.json(),
),
),
)
if _response.status_code == 401:
raise commons_errors_unauthorized_error_UnauthorizedError(
headers=dict(_response.headers),
body=typing.cast(
typing.Any,
parse_obj_as(
type_=typing.Any, # type: ignore
object_=_response.json(),
),
),
)
if _response.status_code == 403:
raise commons_errors_access_denied_error_AccessDeniedError(
headers=dict(_response.headers),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Dead code: duplicate status-code handlers are never reached

The delete method (both sync and async) first raises an exception for status codes 400, 401, 403, 404, and 405 using the unstable_errors_* / PublicApiError variants. Immediately after handling 500, a second identical block of if _response.status_code == 400/401/403/404/405 handlers using commons_errors_* with typing.Any bodies follows. Because Python evaluates if statements in order and the first matching branch raises, the second block is permanently unreachable — callers can never receive a commons_errors_* exception from this endpoint. The same dead-code pattern appears in the async delete method. This looks like a Fern codegen artifact that was already present in the existing get method, but it still means the Error, commons_errors_unauthorized_error_UnauthorizedError, and related types are silently dead here.

Prompt To Fix With AI
This is a comment left during a code review.
Path: langfuse/api/unstable/evaluators/raw_client.py
Line: 740-800

Comment:
**Dead code: duplicate status-code handlers are never reached**

The `delete` method (both sync and async) first raises an exception for status codes 400, 401, 403, 404, and 405 using the `unstable_errors_*` / `PublicApiError` variants. Immediately after handling 500, a second identical block of `if _response.status_code == 400/401/403/404/405` handlers using `commons_errors_*` with `typing.Any` bodies follows. Because Python evaluates `if` statements in order and the first matching branch raises, the second block is permanently unreachable — callers can never receive a `commons_errors_*` exception from this endpoint. The same dead-code pattern appears in the async `delete` method. This looks like a Fern codegen artifact that was already present in the existing `get` method, but it still means the `Error`, `commons_errors_unauthorized_error_UnauthorizedError`, and related types are silently dead here.

How can I resolve this? If you propose a fix, please make it concise.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, auto-generated Fern API spec update adding a delete evaluator endpoint to the unstable namespace.

Extended reasoning...

Overview

This PR is an auto-generated Fern SDK update (# This file was auto-generated by Fern from our API Definition.) that adds a new delete method to the unstable EvaluatorsClient (sync + async, regular + raw). It also adds the corresponding DeleteEvaluatorResponse Pydantic model and the necessary re-exports in the relevant __init__.py files. All changes are confined to langfuse/api/unstable/evaluators/ and the unstable namespace's top-level __init__.py.

Security risks

None. This is generated client code for a new DELETE /api/public/unstable/evaluators/{id} endpoint. Authentication, authorization, and the actual delete semantics live on the server; the SDK only constructs the HTTP request and parses the response. No new auth, crypto, or permission logic is introduced.

Level of scrutiny

Low. Generated SDK code following the exact same shape as the existing get/list/create methods in the same file (DELETE method, same error-handling ladder, same jsonable_encoder URL interpolation). The endpoint is in the unstable namespace, which is explicitly versioned as subject to change. The duplicate 400/401/403/404/405 error handlers below the unreachable else block are a known Fern generator quirk — they're unreachable due to the earlier identical status checks, present in every other method in this file, and not a regression introduced by this PR.

Other factors

The new DeleteEvaluatorResponse model is a single-field Pydantic model with extra="allow" matching the convention used throughout this generated module. Re-exports in the four __init__.py files are alphabetically inserted following the same lazy-import pattern as all sibling types. No bugs were flagged by the bug hunting system, and the diff is mechanical enough that human review would not add signal.

@wochinge wochinge enabled auto-merge (squash) June 15, 2026 16:58
auto-merge was automatically disabled June 15, 2026 20:25

Pull request was closed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants