diff --git a/CHANGELOG.md b/CHANGELOG.md index f0257bcd..8d6b09ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,7 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR - Nos providers do LangChain, a tentativa seguinte a uma resposta recusada pela validação do modelo Pydantic, inclusive por validadores próprios, leva ao modelo a resposta e os erros por campo, com pedido de correção; antes, repetia o mesmo prompt. A recusa levanta `ProviderRejectedOutputError`, transitória por classe, e deixa de ser lida como erro HTTP quando o texto analisado tem números como 404. Os tokens das tentativas recusadas são somados ao uso da linha quando a resposta os traz, o que inclui a OpenAI, pela resposta HTTP anexada ao erro do SDK (#144). - A docstring de `dataframeit()` e a mensagem de rate limit descrevem `rate_limit_delay` como a pausa de cada worker depois de cada linha processada com sucesso, com teto de `parallel_requests * 60 / rate_limit_delay` linhas por minuto. - No provider `codex`, chaves do schema que não restringem o valor, como anotações próprias em `json_schema_extra` e os metadados `examples` e `deprecated`, são descartadas com aviso em vez de recusadas; keywords de validação fora do subconjunto do Structured Outputs continuam recusadas. -- No provider `codex`, o preflight recusa `format` fora da lista do Structured Outputs, como os de `bytes`, `Path`, `AnyUrl` e `UUID4`, e `pattern` com lookahead, lookbehind ou referência a grupo, como o de `Decimal`. Antes, o schema passava, e cada linha falhava no turno: a API recusa esse `format` e o lookaround com HTTP 400 `invalid_json_schema`, e com referência a grupo o turno gera texto até `max_output_tokens` (#158). +- No provider `codex`, o preflight recusa `format` fora da lista do Structured Outputs, como os de `bytes`, `Path`, `AnyUrl` e `UUID4`, e `pattern` com lookahead, lookbehind ou referência a grupo, como o que o pydantic anterior à 2.14 gera para `Decimal`. Antes, o schema passava, e cada linha falhava no turno: a API recusa esse `format` e o lookaround com HTTP 400 `invalid_json_schema`, e com referência a grupo o turno gera texto até `max_output_tokens` (#158). - Com `resume=True` e `checkpoint_path` existente, rodar a mesma chamada de novo continua do checkpoint, sem carregá-lo à mão; antes, a execução recomeçava e o sobrescrevia. `.dataframeit.json`, gravado a cada gravação do checkpoint, guarda a assinatura da execução, o hash do texto de cada linha e o do próprio arquivo, e o checkpoint só é retomado quando os três batem; com outra configuração ou entrada, a execução avisa e recomeça como antes. Das linhas concluídas, com sucesso ou com erro, vêm também os campos do modelo, sobre o que a entrada trazia neles. - No provider `codex`, o app-server roda com `HOME` no diretório isolado da execução, e as skills do usuário, como as de `~/.agents/skills`, deixam de ser carregadas. - Com `search_per_field=True`, o modo por grupo também devolve `search_provider` no uso, e o modo por campo devolve os campos na ordem do modelo, como o modo por grupo. O campo pulado por condição é registrado uma vez no log, e não duas. diff --git a/docs/en/guides/providers.md b/docs/en/guides/providers.md index 958a0988..34a25f84 100644 --- a/docs/en/guides/providers.md +++ b/docs/en/guides/providers.md @@ -107,7 +107,7 @@ result = dataframeit( ) ``` -For this provider, `model_kwargs` accepts only `effort` and `timeout`. `effort` takes one of the values the SDK declares: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`; each model accepts only some of them. `timeout` is the deadline, in seconds, for each attempt, from opening the thread to the end of the turn, with a default of 600: a turn that does not finish in time is interrupted, the attempt fails with `ProviderTransientError`, and the row is retried according to `max_retries`. If the deadline runs out before the app-server answers the request that opens the thread or the turn, the app-server is shut down and the run stops with `ProviderAbortError`; rows without a status are left for `resume=True`. With `timeout=None`, there is no deadline. Changing `timeout` does not prevent resuming a checkpoint. `use_search=True` is not supported. The Pydantic model must have fields at the root and use the [JSON Schema subset accepted by Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs#supported-schemas); `RootModel`, `Any`, dynamic-key `dict` fields, validation keywords outside the subset, such as those of fixed tuples, sets, and `min_length`/`max_length` on strings, `format` values outside the supported list, such as those of `bytes`, `Path`, `AnyUrl`, and `UUID4`, and `pattern` values with lookahead, lookbehind, or backreferences, such as the one of `Decimal`, are rejected during preflight. Keys that do not constrain the value, such as custom annotations in `json_schema_extra` and the `examples` and `deprecated` metadata, are dropped from the schema sent, with a warning listing them; the response is still validated by the Pydantic model. Authentication configured during installation comes from `auth.json`, so do not pass `api_key` to `dataframeit()`. +For this provider, `model_kwargs` accepts only `effort` and `timeout`. `effort` takes one of the values the SDK declares: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max`, and `ultra`; each model accepts only some of them. `timeout` is the deadline, in seconds, for each attempt, from opening the thread to the end of the turn, with a default of 600: a turn that does not finish in time is interrupted, the attempt fails with `ProviderTransientError`, and the row is retried according to `max_retries`. If the deadline runs out before the app-server answers the request that opens the thread or the turn, the app-server is shut down and the run stops with `ProviderAbortError`; rows without a status are left for `resume=True`. With `timeout=None`, there is no deadline. Changing `timeout` does not prevent resuming a checkpoint. `use_search=True` is not supported. The Pydantic model must have fields at the root and use the [JSON Schema subset accepted by Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs#supported-schemas); `RootModel`, `Any`, dynamic-key `dict` fields, validation keywords outside the subset, such as those of fixed tuples, sets, and `min_length`/`max_length` on strings, `format` values outside the supported list, such as those of `bytes`, `Path`, `AnyUrl`, and `UUID4`, and `pattern` values with lookahead, lookbehind, or backreferences, such as the one pydantic before 2.14 generates for `Decimal`, are rejected during preflight. Keys that do not constrain the value, such as custom annotations in `json_schema_extra` and the `examples` and `deprecated` metadata, are dropped from the schema sent, with a warning listing them; the response is still validated by the Pydantic model. Authentication configured during installation comes from `auth.json`, so do not pass `api_key` to `dataframeit()`. With `model`, the library checks at startup, through a thread with no turn, that the runtime resolved the requested model, and fails before the first row if it did not; without `model`, a warning says which model will be used. A turn that the runtime reroutes to another model is interrupted, and the row fails. `effort` is sent with each turn and does not come back in the runtime's response, so it is not checked. diff --git a/docs/guides/providers.md b/docs/guides/providers.md index 6e3393a9..5dea3fc0 100644 --- a/docs/guides/providers.md +++ b/docs/guides/providers.md @@ -107,7 +107,7 @@ resultado = dataframeit( ) ``` -Para esse provider, `model_kwargs` aceita somente `effort` e `timeout`. O `effort` recebe um dos valores que o SDK declara: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` e `ultra`; cada modelo aceita só parte deles. O `timeout` é o prazo, em segundos, de cada tentativa, da abertura da thread ao fim do turno, com padrão de 600: o turno que não termina no prazo é interrompido, a tentativa falha com `ProviderTransientError` e a linha é repetida conforme `max_retries`. Se o prazo acaba antes de o app-server responder à abertura da thread ou do turno, o app-server é encerrado e a execução para com `ProviderAbortError`; as linhas sem status ficam para `resume=True`. Com `timeout=None`, não há prazo. Trocar o `timeout` não impede retomar um checkpoint. `use_search=True` não é suportado. O modelo Pydantic deve ter campos no nível raiz e usar o [subconjunto de JSON Schema aceito por Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs#supported-schemas); `RootModel`, `Any`, campos `dict` com chaves dinâmicas, keywords de validação fora do subconjunto, como as de tuplas fixas, `set` e `min_length`/`max_length` em texto, `format` fora da lista suportada, como os de `bytes`, `Path`, `AnyUrl` e `UUID4`, e `pattern` com lookahead, lookbehind ou referência a grupo, como o de `Decimal`, são rejeitados no preflight. Chaves que não restringem o valor, como as anotações próprias em `json_schema_extra` e os metadados `examples` e `deprecated`, são descartadas do schema enviado, com um aviso que as lista; a resposta continua validada pelo modelo Pydantic. A autenticação configurada durante a instalação vem de `auth.json`, portanto não passe `api_key` ao `dataframeit()`. +Para esse provider, `model_kwargs` aceita somente `effort` e `timeout`. O `effort` recebe um dos valores que o SDK declara: `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` e `ultra`; cada modelo aceita só parte deles. O `timeout` é o prazo, em segundos, de cada tentativa, da abertura da thread ao fim do turno, com padrão de 600: o turno que não termina no prazo é interrompido, a tentativa falha com `ProviderTransientError` e a linha é repetida conforme `max_retries`. Se o prazo acaba antes de o app-server responder à abertura da thread ou do turno, o app-server é encerrado e a execução para com `ProviderAbortError`; as linhas sem status ficam para `resume=True`. Com `timeout=None`, não há prazo. Trocar o `timeout` não impede retomar um checkpoint. `use_search=True` não é suportado. O modelo Pydantic deve ter campos no nível raiz e usar o [subconjunto de JSON Schema aceito por Structured Outputs](https://developers.openai.com/api/docs/guides/structured-outputs#supported-schemas); `RootModel`, `Any`, campos `dict` com chaves dinâmicas, keywords de validação fora do subconjunto, como as de tuplas fixas, `set` e `min_length`/`max_length` em texto, `format` fora da lista suportada, como os de `bytes`, `Path`, `AnyUrl` e `UUID4`, e `pattern` com lookahead, lookbehind ou referência a grupo, como o que o pydantic anterior à 2.14 gera para `Decimal`, são rejeitados no preflight. Chaves que não restringem o valor, como as anotações próprias em `json_schema_extra` e os metadados `examples` e `deprecated`, são descartadas do schema enviado, com um aviso que as lista; a resposta continua validada pelo modelo Pydantic. A autenticação configurada durante a instalação vem de `auth.json`, portanto não passe `api_key` ao `dataframeit()`. Com `model`, a biblioteca confere na abertura, por uma thread sem turno, que o runtime resolveu o modelo pedido, e falha antes da primeira linha se não resolveu; sem `model`, um aviso diz qual modelo será usado. Um turno que o runtime reroteia para outro modelo é interrompido, e a linha falha. O `effort` vai em cada turno e não volta na resposta do runtime, então não é conferido. diff --git a/tests/test_codex.py b/tests/test_codex.py index a9b0b395..6ab25bcd 100644 --- a/tests/test_codex.py +++ b/tests/test_codex.py @@ -12,7 +12,6 @@ from collections.abc import Callable from datetime import date, datetime, timedelta from datetime import time as time_type -from decimal import Decimal from ipaddress import IPv4Address, IPv6Address from pathlib import Path from typing import Annotated, Any, Literal @@ -552,11 +551,22 @@ def test_format_que_nao_e_texto_e_recusado(self, formato): _to_strict_json_schema(schema) def test_pattern_do_decimal_e_recusado_pelo_lookahead(self): - class Modelo(BaseModel): - valor: Decimal + # O pattern que o pydantic emite para Decimal até a 2.13, escrito aqui + # porque da 2.14 em diante o schema do tipo não traz pattern. + schema = { + "type": "object", + "properties": { + "valor": { + "anyOf": [ + {"type": "number"}, + {"type": "string", "pattern": r"^(?!^[-+.]*$)[+-]?0*\d*\.?\d*$"}, + ] + } + }, + } with pytest.raises(ProviderConfigurationError, match="lookahead"): - _to_strict_json_schema(Modelo.model_json_schema()) + _to_strict_json_schema(schema) @pytest.mark.parametrize( ("pattern", "construcao"),