Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR

### Corrigido

- A retomada pelo checkpoint deixa de recusar ou trocar valores já gravados (#196). No CSV e no XLSX, que gravam `""` e `None` na mesma célula vazia, o texto vazio de um campo de texto que não admite `None` volta `""`, em vez de a linha ser acusada como incompatível e a retomada pedir `reprocess_columns` para a coluna inteira; no campo que admite `None`, a célula vazia continua voltando `None`. No XLSX, o texto que começa com `=` é gravado como texto, e não como fórmula, que voltava vazia. Os campos `Sequence`, `AbstractSet`, `set`, `frozenset`, `Mapping` e as versões mutáveis, `TypedDict` e `NamedTuple` passam a contar como estrutura em `get_complex_fields`: o checkpoint os grava em JSON, o conjunto em ordem fixa, e a retomada os devolve ao tipo do campo, em vez de acusá-los como incompatíveis. O `"NA"` de um `Literal["NA", "deferido", None]` é relido como texto, e não como ausente. O aviso do reprocessamento interrompido diz que repetir a chamada com `reprocess_columns` refaz todas as linhas, inclusive as já reprocessadas.
- No provider `codex`, a thread daemon que pede o `thread/unsubscribe` da linha abortada pela morte do app-server deixa de ficar presa para sempre. O processo morto ainda aceita a escrita enquanto termina, e o SDK registra a espera da resposta depois de o leitor do stdout ter acordado os pedidos em voo; agora o fechamento do cliente acorda essa espera (#250).
- `read_df` com argumento de leitura que faz o pandas devolver outra coisa que um DataFrame, como `chunksize`, `iterator`, `sheet_name=None` ou lista e o `chunksize` do JSON em linhas, devolve o `TextFileReader`, o `JsonReader` ou o dict de abas como o pandas o devolve, sem normalizar, com ou sem modelo. Antes, sem modelo, ou com modelo que tem campo de lista, dict ou modelo aninhado, e com `normalize=True`, o padrão, a função levantava `AttributeError` ao procurar as colunas no objeto do leitor (#249).
- Com entrada polars, a saída deixa de trocar sem aviso o valor que o modelo devolveu quando o pyarrow tipa a coluna pelo primeiro valor e converte os demais. Sai `Object`, com o aviso das colunas que o polars não reúne, o campo cuja anotação junta `timedelta` a outro tipo, `float` a `bool` ou `bytes` a `str`, e a coluna cujos valores trazem essas combinações ou as de data e hora, também em `Any`, onde o escalar do numpy, como o `np.int64`, conta como o número do Python, nos itens de lista e no campo de modelo aninhado, além da coluna cujos dicts do mesmo campo têm chaves diferentes e da que traz `datetime` com e sem fuso, `time` com fuso ou `Decimal` NaN. O NaN de campo `float` sai NaN. Antes, em `Union[timedelta, int]` o 5 virava cinco microssegundos, em `Union[float, bool]` o `True` virava `1.0`, o `datetime` sem fuso ganhava o fuso do primeiro, o `time` perdia o fuso, o `Decimal` NaN e o NaN de campo `float` viravam nulo, `{}` ao lado de `{"a": 1}` virava `{"a": None}`, e em `Union[NewType sobre date, int]` o 5 virava `1970-01-06`. Com `resume=True`, a coluna do campo cuja anotação é `int`, um `Literal` com inteiro ou uma união com um deles é lida com os inteiros do polars, e o inteiro acima de 2**53 deixa de voltar arredondado em `Float64`. O `Decimal` NaN e o NaN de campo `float` de uma linha processada deixam de ser acusados como incompatíveis na retomada, também com entrada pandas, e o reprocessamento campo a campo os repassa às condições como valor; na coluna float64, como a do checkpoint relido, o NaN não se distingue do nulo e segue contando como ausente. O `NewType` e o apelido `type`, inclusive o apelido genérico usado com argumentos, como `Opcional[int]` de `type Opcional[T] = T | None`, passam a contar pelo tipo a que se referem na saída polars, na retomada e em `read_df`: o `NewType` sobre `dict` sai em texto JSON, como o campo `dict`, e é retomado, e o campo `NewType` sobre `str` ou sobre `int` é relido do CSV como texto cru ou como inteiro exato (#241).
Expand Down
4 changes: 2 additions & 2 deletions docs/en/guides/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ result = dataframeit(
)
```

The format is inferred from the file extension (`.csv`, `.xlsx`, `.parquet`). In every format, the model's list, dict and tuple fields are saved as JSON text, and `Enum` values by their value; `read_df` turns the structures back, and resuming returns structures and date, time, `datetime`, `Enum`, `int` and `bool` fields with their declared type, also with `strict=True`: a date saved as text, an integer read back as a float and a tuple read back as a list come back as `date`, `int` and `tuple`. XLSX stores numbers as doubles, and an integer above 2**53 comes back rounded; for ids that large, use CSV or parquet. None of the three formats keeps the NaN of a `float` field apart from null: CSV and XLSX write both as an empty cell, and parquet as null. When resuming, the NaN comes back missing, and a required field that received it is flagged for reprocessing. In parquet, an integer above 2**63 - 1 next to a pending row and an integer above 2**53 next to a float in the same column are not saved, and the checkpoint save fails with a warning. If execution is interrupted, run the same call again with the same input: with `resume=True`, the default, and the file present, execution continues from the checkpoint. On every save, `<checkpoint>.dataframeit.json` is written next to it with the run's signature (prompt, provider, model, `model_kwargs` except `timeout`, schema, search, and the text and status columns), a hash of each row's text, and a hash of the checkpoint itself. The checkpoint is resumed only when all three match; with another configuration, another input, or a file that is not the one the signature describes, the run warns, starts over, and overwrites it. In a row already concluded, successfully or with an error, the model fields come from the checkpoint, over whatever the input carried in them; in a successful row, that is what an uninterrupted run would give, because it writes the answer over them. To keep an output corrected by hand as it is, pass it with the status column, and the checkpoint is not read again. A complete output is returned without it, and the column comes back with `output["_dataframeit_status"] = "processed"` (or the name passed in `status_column`). With `resume=False`, the file is ignored and overwritten.
The format is inferred from the file extension (`.csv`, `.xlsx`, `.parquet`). In every format, the model's structure fields, that is, list, tuple, set, dict, `Sequence`, `AbstractSet`, `Mapping`, `TypedDict`, `NamedTuple` and nested model, are saved as JSON text, sets in a fixed order, and `Enum` values by their value; `read_df` turns the structures back, and resuming returns structures and date, time, `datetime`, `Enum`, `int` and `bool` fields with their declared type, also with `strict=True`: a date saved as text, an integer read back as a float and a tuple read back as a list come back as `date`, `int` and `tuple`. CSV and XLSX write an empty string and `None` as the same empty cell. When resuming, that cell comes back as `""` in a text field that does not accept `None`, such as `str` or `str = "default"`, and as `None` in a field that accepts it, such as `Optional[str]`; to keep the `""` of an optional field, use parquet. In XLSX, text that starts with `=` is saved as text, not as a formula. XLSX stores numbers as doubles, and an integer above 2**53 comes back rounded; for ids that large, use CSV or parquet. None of the three formats keeps the NaN of a `float` field apart from null: CSV and XLSX write both as an empty cell, and parquet as null. When resuming, the NaN comes back missing, and a required field that received it is flagged for reprocessing. In parquet, an integer above 2**63 - 1 next to a pending row and an integer above 2**53 next to a float in the same column are not saved, and the checkpoint save fails with a warning. If execution is interrupted, run the same call again with the same input: with `resume=True`, the default, and the file present, execution continues from the checkpoint. On every save, `<checkpoint>.dataframeit.json` is written next to it with the run's signature (prompt, provider, model, `model_kwargs` except `timeout`, schema, search, and the text and status columns), a hash of each row's text, and a hash of the checkpoint itself. The checkpoint is resumed only when all three match; with another configuration, another input, or a file that is not the one the signature describes, the run warns, starts over, and overwrites it. In a row already concluded, successfully or with an error, the model fields come from the checkpoint, over whatever the input carried in them; in a successful row, that is what an uninterrupted run would give, because it writes the answer over them. To keep an output corrected by hand as it is, pass it with the status column, and the checkpoint is not read again. A complete output is returned without it, and the column comes back with `output["_dataframeit_status"] = "processed"` (or the name passed in `status_column`). With `resume=False`, the file is ignored and overwritten.

To inspect what has already been processed, `read_df` loads the file with lists, dicts and text in the model's types. That DataFrame also works as input for resuming, and then the file is not read again:

Expand All @@ -119,7 +119,7 @@ result = dataframeit(
)
```

When the provider reports a failure that prevents any further row, such as an exhausted usage quota (`ProviderUsageLimitError`) or a terminated `codex` runtime (`ProviderAbortError`), execution stops dispatching rows, saves the checkpoint, and warns how many rows were left without status. The control columns stay in the output, and running again with `resume=True` processes only the pending rows. With `reprocess_columns`, rows the interruption left without reprocessing keep their previous values and are marked in `_error_details`.
When the provider reports a failure that prevents any further row, such as an exhausted usage quota (`ProviderUsageLimitError`) or a terminated `codex` runtime (`ProviderAbortError`), execution stops dispatching rows, saves the checkpoint, and warns how many rows were left without status. The control columns stay in the output, and running again with `resume=True` processes only the pending rows. With `reprocess_columns`, rows the interruption left without reprocessing keep their previous values and are marked in `_error_details`. Repeating the call with `reprocess_columns` redoes every row, including the ones already reprocessed.

## Token Tracking

Expand Down
4 changes: 2 additions & 2 deletions docs/en/reference/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def read_df(path: str, model=None, normalize: bool = True, **kwargs) -> pd.DataF
| `normalize` | bool | If `False`, no column is converted |
| `**kwargs` | | Arguments passed to pandas. With one that makes the reader return something other than a DataFrame, such as `chunksize`, `iterator` or `sheet_name=None` or a list, the function returns the pandas object, such as a `TextFileReader`, a `JsonReader`, a dict of sheets or the `Series` from `typ="series"`, without normalizing or rereading any column, even with `model` and `normalize=True` |

CSV and XLSX store `""` and missing values the same way. A required text field that was `""` comes back missing, and resuming flags it for reprocessing; use a `.parquet` checkpoint to keep that difference.
CSV and XLSX store `""` and missing values the same way, and `read_df` returns the empty cell as missing. Resuming through `checkpoint_path`, whose signature guarantees that the model is the same, gives `""` back to a text field that does not accept `None`. Passed as input, the file read back carries no such guarantee, and resuming flags the required field for reprocessing. In a field that accepts `None`, the empty cell comes back as `None`; use a `.parquet` checkpoint to keep that difference.

Called with `model` and no extra read arguments, the `**kwargs` passed to pandas, the function rereads from `.csv` and `.parquet` the column of an `int` field, a `Literal` of integers only, or one of them with `None`, that pandas does not return as an integer dtype: it comes back as `object`, with the exact `int` and `None` in empty cells, instead of float64 with `NaN`, which rounds integers above 2**53. A cell the file stores as a float, such as `7.5`, stays as read. A nullable `Int64` parquet column stays `Int64`, and a column with only empty cells stays as read: float in CSV and, in the null column the checkpoint writes to parquet, `object` with `None`. An extra read argument turns this reread off; `normalize` does not. `.xlsx`, which stores numbers as doubles, is left out.

Expand Down Expand Up @@ -246,7 +246,7 @@ Identifies the fields of a Pydantic model that hold a structure instead of a sim
def get_complex_fields(pydantic_model) -> set
```

Returns the set of names of the fields whose type is `list`, `dict`, `tuple` or a nested Pydantic model, including inside `Optional` and `Union`.
Returns the set of names of the fields whose type is a structure: `list`, `tuple`, `set`, `frozenset`, `dict`, `Sequence`, `AbstractSet` and `Mapping` and their mutable versions, `TypedDict`, `NamedTuple` or a nested Pydantic model, including inside `Optional` and `Union`.

```python
from dataframeit import get_complex_fields, normalize_complex_columns, read_df
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ resultado = dataframeit(
)
```

Formato inferido pela extensão do arquivo (`.csv`, `.xlsx`, `.parquet`). Em qualquer formato, os campos de lista, dict e tupla do modelo são gravados como texto JSON, e o `Enum`, pelo valor; `read_df` devolve as estruturas, e a retomada devolve ao tipo declarado as estruturas e os campos de data, hora, `datetime`, `Enum`, `int` e `bool`, inclusive com `strict=True`: a data gravada como texto, o inteiro relido como float e a tupla relida como lista voltam a `date`, `int` e `tuple`. O XLSX guarda número como double, e um inteiro acima de 2**53 volta arredondado dele; para ids desse tamanho, use CSV ou parquet. Nenhum dos três formatos guarda o NaN de um campo `float` à parte do nulo: o CSV e o XLSX gravam os dois como célula vazia, e o parquet, como nulo. Na retomada, o NaN volta ausente, e o campo obrigatório que o recebeu é acusado para reprocessar. No parquet, o inteiro acima de 2**63 - 1 ao lado de uma linha pendente e o inteiro acima de 2**53 ao lado de um float na mesma coluna não são gravados, e a gravação do checkpoint falha com aviso. Em caso de interrupção, rode a mesma chamada de novo, com a mesma entrada: com `resume=True`, o padrão, e o arquivo presente, a execução continua do checkpoint. A cada gravação, `<checkpoint>.dataframeit.json` é gravado ao lado dele com a assinatura da execução (prompt, provider, modelo, `model_kwargs` menos `timeout`, schema, busca e as colunas de texto e de status), o hash do texto de cada linha e o hash do próprio checkpoint. O checkpoint só é retomado quando os três batem; com outra configuração, outra entrada ou um arquivo que não é o que a assinatura descreve, a execução avisa, começa do zero e o sobrescreve. Numa linha já concluída, com sucesso ou com erro, os campos do modelo vêm do checkpoint, sobre o que a entrada trazia neles; na linha com sucesso, é o que uma execução sem interrupção daria, porque ela grava a resposta por cima. Para manter uma saída corrigida à mão como está, passe-a com a coluna de status, e o checkpoint não é relido. A saída completa sai sem ela, e a coluna volta com `saida["_dataframeit_status"] = "processed"` (ou o nome passado em `status_column`). Com `resume=False`, o arquivo é ignorado e sobrescrito.
Formato inferido pela extensão do arquivo (`.csv`, `.xlsx`, `.parquet`). Em qualquer formato, os campos de estrutura do modelo, isto é, lista, tupla, conjunto, dict, `Sequence`, `AbstractSet`, `Mapping`, `TypedDict`, `NamedTuple` e modelo aninhado, são gravados como texto JSON, o conjunto em ordem fixa, e o `Enum`, pelo valor; `read_df` devolve as estruturas, e a retomada devolve ao tipo declarado as estruturas e os campos de data, hora, `datetime`, `Enum`, `int` e `bool`, inclusive com `strict=True`: a data gravada como texto, o inteiro relido como float e a tupla relida como lista voltam a `date`, `int` e `tuple`. O CSV e o XLSX gravam o texto vazio e o `None` na mesma célula vazia. Na retomada, ela volta `""` no campo de texto que não admite `None`, como `str` ou `str = "padrão"`, e `None` no campo que o admite, como `Optional[str]`; para guardar o `""` de um campo opcional, use parquet. No XLSX, o texto que começa com `=` é gravado como texto, e não como fórmula. O XLSX guarda número como double, e um inteiro acima de 2**53 volta arredondado dele; para ids desse tamanho, use CSV ou parquet. Nenhum dos três formatos guarda o NaN de um campo `float` à parte do nulo: o CSV e o XLSX gravam os dois como célula vazia, e o parquet, como nulo. Na retomada, o NaN volta ausente, e o campo obrigatório que o recebeu é acusado para reprocessar. No parquet, o inteiro acima de 2**63 - 1 ao lado de uma linha pendente e o inteiro acima de 2**53 ao lado de um float na mesma coluna não são gravados, e a gravação do checkpoint falha com aviso. Em caso de interrupção, rode a mesma chamada de novo, com a mesma entrada: com `resume=True`, o padrão, e o arquivo presente, a execução continua do checkpoint. A cada gravação, `<checkpoint>.dataframeit.json` é gravado ao lado dele com a assinatura da execução (prompt, provider, modelo, `model_kwargs` menos `timeout`, schema, busca e as colunas de texto e de status), o hash do texto de cada linha e o hash do próprio checkpoint. O checkpoint só é retomado quando os três batem; com outra configuração, outra entrada ou um arquivo que não é o que a assinatura descreve, a execução avisa, começa do zero e o sobrescreve. Numa linha já concluída, com sucesso ou com erro, os campos do modelo vêm do checkpoint, sobre o que a entrada trazia neles; na linha com sucesso, é o que uma execução sem interrupção daria, porque ela grava a resposta por cima. Para manter uma saída corrigida à mão como está, passe-a com a coluna de status, e o checkpoint não é relido. A saída completa sai sem ela, e a coluna volta com `saida["_dataframeit_status"] = "processed"` (ou o nome passado em `status_column`). Com `resume=False`, o arquivo é ignorado e sobrescrito.

Para inspecionar o que já foi processado, `read_df` carrega o arquivo com listas, dicts e textos nos tipos do modelo. Esse DataFrame também serve de entrada para a retomada, e aí o arquivo não é lido de novo:

Expand All @@ -121,7 +121,7 @@ resultado = dataframeit(
)
```

Quando o provider informa uma falha que impede qualquer linha seguinte, como a cota de uso esgotada (`ProviderUsageLimitError`) ou o runtime do `codex` encerrado (`ProviderAbortError`), a execução para de despachar linhas, grava o checkpoint e avisa quantas ficaram sem status. As colunas de controle ficam na saída, e rodar de novo com `resume=True` processa só as pendentes. Com `reprocess_columns`, as linhas que a interrupção deixou sem reprocessar mantêm os valores anteriores e ficam marcadas em `_error_details`.
Quando o provider informa uma falha que impede qualquer linha seguinte, como a cota de uso esgotada (`ProviderUsageLimitError`) ou o runtime do `codex` encerrado (`ProviderAbortError`), a execução para de despachar linhas, grava o checkpoint e avisa quantas ficaram sem status. As colunas de controle ficam na saída, e rodar de novo com `resume=True` processa só as pendentes. Com `reprocess_columns`, as linhas que a interrupção deixou sem reprocessar mantêm os valores anteriores e ficam marcadas em `_error_details`. Repetir a chamada com `reprocess_columns` refaz todas as linhas, inclusive as que já tinham sido reprocessadas.

## Tracking de Tokens

Expand Down
Loading
Loading