Skip to content

Mensagem de erro promete rate limit que a execução não trata como rate limit #243

Open

Description

@bdcdo

Desde o #203, is_rate_limit_error decide pela classe quando o erro é um ProviderError do pacote: só o ProviderOverloadedError conta como rate limit, e um "429" no texto não reduz os workers. get_friendly_error_message ainda decide pelo texto. Um ProviderError com "429" na mensagem recebe a caixa "LIMITE DE REQUISIÇÕES ATINGIDO", que manda reduzir parallel_requests e aumentar rate_limit_delay, enquanto a execução segue com os mesmos workers. Isso afeta quem usa codex ou claude_code: no primeiro, o turno que falha sem código estruturado e com "429 Too Many Requests" só no texto; no segundo, o result de erro do SDK, que traz texto do modelo e pode citar, por exemplo, o art. 429 do CPC. Nos dois, o usuário lê um diagnóstico que não é o da execução.

import warnings
from unittest.mock import patch

import pandas as pd
from pydantic import BaseModel

from dataframeit import dataframeit
from dataframeit.errors import (
    ProviderError,
    ProviderTransientError,
    get_friendly_error_message,
    is_rate_limit_error,
)


def titulo(mensagem):
    """A primeira linha com texto da caixa da mensagem amigável."""
    return next(t for linha in mensagem.splitlines() if (t := linha.strip("║╔╗╚╝╠╣═ ")))


# Mensagens iguais às que `_raise_turn_error` (codex) e `_raise_for_result_error`
# (claude_code) montam nesses casos.
erros = {
    "codex": ProviderError("Turno Codex falhou: 429 Too Many Requests"),
    "claude_code": ProviderTransientError(
        "Claude Code SDK retornou erro (error_during_execution, status None): "
        "Não foi possível concluir: o art. 429 do CPC exige..."
    ),
}
for provider, erro in erros.items():
    print(provider, is_rate_limit_error(erro), titulo(get_friendly_error_message(erro, provider)))
# codex False LIMITE DE REQUISIÇÕES ATINGIDO
# claude_code False LIMITE DE REQUISIÇÕES ATINGIDO


class Campo(BaseModel):
    campo: str


with patch("dataframeit.core.call_langchain", side_effect=erros["codex"]), patch(
    "dataframeit.core.validate_provider_dependencies"
), warnings.catch_warnings(record=True) as avisos:
    warnings.simplefilter("always")
    dataframeit(pd.DataFrame({"texto": ["a", "b"]}), Campo, "{texto}",
                parallel_requests=2, max_retries=1, track_tokens=False)
for aviso in avisos:
    linhas = str(aviso.message).splitlines()
    print(linhas[0], titulo("\n".join(linhas[1:])))
# Falha ao processar linha 0. LIMITE DE REQUISIÇÕES ATINGIDO
# Falha ao processar linha 1. LIMITE DE REQUISIÇÕES ATINGIDO
# (nenhum aviso "Rate limit detectado! Reduzindo workers")

Medido na main em 97a5100. Conferi também os dois erros produzidos pelos caminhos reais, _raise_turn_error com um TurnError(message="429 Too Many Requests", codexErrorInfo=None) e _raise_for_result_error com um ResultMessage falso de is_error=True, e o resultado é o mesmo.

A divergência também corre no sentido oposto: um ProviderOverloadedError sem "429" no texto reduz os workers, mas recebe a caixa genérica.

from dataframeit.errors import ProviderOverloadedError, get_friendly_error_message, is_rate_limit_error

erro = ProviderOverloadedError("Turno Codex falhou: server overloaded")
print(is_rate_limit_error(erro), get_friendly_error_message(erro, "codex").splitlines()[1].strip("║ "))
# True ERRO NO PROCESSAMENTO

Causa

Em errors.py, is_rate_limit_error devolve False para todo ProviderError e ProviderOutputError que não seja ProviderOverloadedError, antes de olhar o texto. get_friendly_error_message só trata pela classe o ProviderRejectedOutputError e o ProviderOutputError; os demais ProviderError caem em is_category, que, sem status HTTP estruturado, procura "429" no texto da exceção e escolhe a caixa de rate limit. As outras categorias decididas pelo texto (credencial, permissão, timeout) têm o mesmo risco para o ProviderError cuja mensagem traz texto do modelo, mas aqui a divergência é direta com uma decisão que o pacote já tomou pela classe.

Proposta

Fazer a mensagem decidir o rate limit pela mesma regra: para ProviderError, a caixa de rate limit sai só quando is_rate_limit_error devolve True, isto é, para o ProviderOverloadedError, e o restante cai na mensagem genérica, que mostra o tipo e o começo do texto do erro. Um teste parametrizado com os dois erros acima, que exija que a caixa de rate limit e a redução de workers apareçam juntas ou não apareçam, impede que as duas regras se separem de novo.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions