Repository navigation
Exemplos da documentação rodam como estão escritos, e as páginas descrevem o que o código faz - #264
Conversation
…cessado O dataframeit grava os resultados e o status no próprio DataFrame pandas que recebe. No notebook 04, as células 11 e 13 chamavam dataframeit três vezes com o mesmo df, e só a primeira chamava o modelo; a comparação entre prompts mostrava os resultados da primeira. No 08, a célula 20 reusava o df da célula 13 e não fazia nenhuma chamada. O 04 passa df.copy() a cada chamada, e a célula 17 passa uma cópia de df_comentarios, para que o erro da célula 19 liste as colunas do DataFrame como ele foi criado. O 08 processa na célula 20 um DataFrame novo com os mesmos textos. test_exemplo_nao_reusa_dataframe_ja_processado reprova o notebook ou script que passa a dataframeit() um nome já processado, ou uma cópia dele, sem atribuí-lo de novo no meio.
…no lugar A assinatura em api.md omitia perguntas, o 4º parâmetro de dataframeit(). Quem passava resume por posição caía em perguntas, que é ignorado: dataframeit(saida, M, "p", False) chamava o modelo que resume=False evitaria. A assinatura passa a trazê-lo, e test_assinatura_da_referencia_segue_a_ordem_do_codigo compara a ordem documentada com a de inspect.signature nas duas línguas. O retorno deixa de dizer que a saída volta no formato da entrada: é DataFrame do polars para entrada polars e do pandas para as demais. A seção nova "Entrada modificada no lugar" diz que a entrada pd.DataFrame recebe as colunas do modelo e as de controle, inclusive o status que a saída remove, e o que acontece numa segunda chamada sobre ela. A página de exceções lista ProviderAbortError e ProviderUsageLimitError, que estão em __all__, e a frase "uma falha numa linha não interrompe a execução" ganha a exceção delas, que param o despacho das linhas seguintes. A linha do ImportError mostra o que a mensagem pede de fato. test_pagina_de_excecoes_lista_todas_as_exportadas reprova a exceção exportada que a página não cita.
Rodados em sequência, os exemplos de basic-usage.md reusavam o df já processado com Sentimento, e a chamada com Categoria levantava ValueError por campos incompatíveis; os três exemplos de providers não chamavam o modelo. Cada chamada passa df.copy(), e a página diz por quê. O exemplo de trace completo em web-search.md chamava MedicamentoInfo, que usa prompt e prompt_append em json_schema_extra, sem search_per_field=True, e levantava ValueError. Ele lê _trace, a coluna do agente único, e passa a usar EmpresaInfo, o modelo de Uso Básico, que não tem configuração por campo. O trace por campo segue no exemplo seguinte.
…o atual - FAQ: a mensagem de dependência sugere o pacote LangChain do provider ou o extra all, e não dataframeit[anthropic]; o claude_code e o codex pedem o extra deles. Só o codex recusa api_key; o claude_code a ignora. - Página inicial: as métricas de throughput saem no resumo final e só com parallel_requests > 1, e não em tempo real; a saída é sempre DataFrame, e não "no mesmo formato da entrada". Performance diz o mesmo sobre a seção de throughput. - READMEs e página inicial: o comentário do extra all deixa de se ler como se ele excluísse busca web, Polars e Excel. - READMEs e início rápido: a saída do exemplo também traz as colunas de tokens, que o padrão track_tokens=True grava.
EvidênciaBase: Caso na base (
|
| Caso | Esperado | Obtido na base (b48bd034) |
Obtido no ramo (2bfdb7eb) |
|---|---|---|---|
basic-usage.md em sequência, pt e en |
cada exemplo chama o modelo | blocos 1-3: ValueError antes de qualquer chamada; os três do bloco 7: 0 chamadas |
3 chamadas em cada bloco de 1 a 3 e em cada uma das três do bloco 7 |
web-search.md, "Exemplo: Trace Completo", pt e en |
roda e imprime o trace | ValueError antes da primeira linha |
3 chamadas; imprime queries, duração e modelo |
| Notebook 04, segunda chamada da célula 11 e célula 13 | cada prompt chama o modelo | 0 e 0 | 3 e 3 |
| Notebook 04, célula 19 | o erro lista as colunas do df_comentarios original |
lista também as 7 colunas gravadas pela célula 17 | ['id', 'autor', 'comentario'] |
| Notebook 08, célula 20 | processa os 10 textos | 0 chamadas | 10 chamadas |
Assinatura de api.md na ordem de inspect.signature, pt e en |
passa | falha: o 4º parâmetro documentado é resume, o do código é perguntas |
passa |
exceptions.md cita toda exceção de __all__, pt e en |
passa | falha: faltam ProviderAbortError e ProviderUsageLimitError |
passa |
Nenhum dos 9 notebooks e scripts de example/ passa a dataframeit() um DataFrame já processado |
passa nos 9 | falha no 04 (['df', 'df', 'df_comentarios']) e no 08 (['df']) |
passa nos 9 |
A checagem de reuso acusa o caso certo, em 9 casos sintéticos: mesmo df, cópia de df já processado, cópia nas duas chamadas, df reatribuído no meio, coluna gravada não libera, argumento por nome, saída no mesmo nome, outro DataFrame, entrada sem nome |
passa | passa | passa |
mkdocs build pt e en, como o job docs (uv run --python 3.13 --extra docs mkdocs build) |
saída 0, sem aviso novo | saída 0, 2 avisos (seletor de língua do i18n com navigation.instant) |
saída 0, os mesmos 2 avisos; as 12 âncoras dos links novos existem no site gerado |
As linhas de teste da tabela foram rodadas assim: na árvore da base, git checkout 2bfdb7eb -- tests/test_exemplos.py tests/test_api_publica.py e uv run $E pytest -W error tests/test_exemplos.py tests/test_api_publica.py -q, com saída 6 failed, 47 passed; no ramo, o mesmo pytest dá 53 passed.
As afirmações corrigidas descrevem o comportamento do código, que é o mesmo nas duas árvores. Outro script fora do repositório, mede_afirmacoes.py, mede cada uma com call_langchain trocado por dublê (uv run $E python mede_afirmacoes.py), e a saída sai idêntica na base e no ramo.
| Afirmação corrigida e página no ramo | Esperado, pelo código | Obtido |
|---|---|---|
A assinatura tem perguntas na 4ª posição; docs/reference/api.md:14 |
src/dataframeit/core.py:1044 |
parâmetros 1 a 5: data, questions, prompt, perguntas, resume; dataframeit(saida, M, "p", False) faz 3 chamadas, resume=False faz 0 |
A saída é DataFrame, do polars para entrada polars e do pandas para as demais; docs/reference/api.md:106, docs/index.md:90 |
to_pandas em src/dataframeit/utils.py:135; from_pandas em :292, com o ramo polars em :333 |
list, dict, pd.Series e pd.DataFrame dão pandas.DataFrame |
A entrada pandas é modificada no lugar; docs/reference/api.md:140 |
src/dataframeit/utils.py:163-164 devolve o próprio DataFrame; src/dataframeit/core.py:1465-1484 devolve sem chamar o modelo quando toda linha tem status |
depois da chamada o df tem _dataframeit_status, _error_details e as quatro colunas de tokens; a segunda chamada sobre ele faz 0 chamadas; a Series sai inalterada |
ProviderAbortError e ProviderUsageLimitError param a execução; docs/reference/exceptions.md:26, :42-43, :57-58, :62-64 |
classes em src/dataframeit/errors.py:67 e :76; sem retry em errors.py:813-817; capturadas em src/dataframeit/core.py:2552 (sequencial) e :2742 (paralelo); checkpoint gravado em core.py:2586-2587 e :2848-2849; aviso em core.py:1636; levantadas pelo codex em src/dataframeit/codex.py:1071 e :1132 |
as duas classes, com parallel_requests 1 e 2: 2 de 6 chamadas, status ['processed', None, None, None, None, None], aviso "Execução interrompida: ... 5 linha(s) ficaram sem status" |
Métricas de throughput só no resumo final e só com parallel_requests > 1; docs/index.md:74, docs/guides/performance.md:160 |
elapsed_seconds só é gravado em src/dataframeit/core.py:2852, dentro de _process_rows_parallel, e a seção sai em core.py:1804-1810 |
seção presente com parallel_requests=2, ausente com 1 |
A mensagem de dependência sugere o pacote langchain ou dataframeit[all], e claude_code e codex sugerem o extra; docs/guides/faq.md:10-12 |
src/dataframeit/errors.py:329-353; extras em errors.py:175 e :183 |
anthropic sem o pacote: pip install langchain-anthropic e pip install dataframeit[all]; claude_code: pip install dataframeit[claude-code] |
codex recusa api_key e claude_code a ignora; docs/guides/faq.md:21 |
src/dataframeit/codex.py:412-413; src/dataframeit/claude_code.py não lê api_key (nenhuma ocorrência no arquivo) |
codex: ProviderConfigurationError; claude_code: 3 linhas processadas, sem erro |
O extra all cobre os providers menos o Codex, mais busca web, Polars e Excel; README.md:25, README.pt-BR.md:25, README.es.md:25, docs/index.md:101 |
pyproject.toml:107-118 |
leitura do pyproject.toml |
A saída traz as quatro colunas de tokens; README.md:73, README.pt-BR.md:73, README.es.md:73, docs/getting-started/quickstart.md:87 |
TOKEN_COLUMNS em src/dataframeit/utils.py:58 |
colunas da saída: texto, sentimento, _input_tokens, _cached_input_tokens, _output_tokens, _reasoning_tokens |
Testes
| Execução | Base (b48bd034) |
Ramo (2bfdb7eb) |
|---|---|---|
Job unit do CI: uv run --exact --extra dev --extra all --extra search-all pytest --cov=dataframeit --cov-branch --cov-report= --cov-fail-under=0 |
1599 passados, 129 pulados | 1621 passados, 129 pulados |
Job codex do CI: uv run --exact --extra dev --extra codex pytest com as mesmas opções |
1393 passados, 367 pulados | 1415 passados, 367 pulados |
Cobertura combinada dos dois jobs: coverage combine e coverage report |
100% | 100% |
Todos os extras num ambiente só: uv run --exact $E pytest --cov=dataframeit --cov-branch |
1760 passados, 0 pulados, 99,96% | 1782 passados, 0 pulados, 99,96% |
Testes novos com -W error |
6 falhas e 47 passados (testes do ramo sobre a documentação e os exemplos da base) | 53 passados |
Os 22 testes a mais são os novos: 2 da assinatura, 2 da página de exceções, 9 da checagem de reuso nos arquivos de example/ e 9 casos sintéticos dessa checagem. Os 99,96% do ambiente único vêm de src/dataframeit/codex.py:700-701, que só o job codex cobre, e se repetem na base. Gates estáticos no ramo, como no job quality (uv run --python 3.13 $E ...), todos com saída 0: ruff format --check src tests, ruff check src tests, ty check, vulture, deptry src. O CI do PR em 2bfdb7eb está verde em todos os jobs.
Risco: o PR muda documentação, dois notebooks, o CHANGELOG.md e testes, sem tocar src/. Como mexe em docs/, o merge na main republica o site pelo docs.yml, e uma página errada vai ao ar até o próximo deploy. O teste de reuso reprova um exemplo futuro que passe a dataframeit() um DataFrame já processado sem cópia nem nova atribuição; um exemplo que precise desse reuso de propósito, como uma demonstração de resume, passa se reler o DataFrame antes da chamada, como faz o 03_resume com read_df. O teste da assinatura reprova quem mudar a assinatura de dataframeit() sem atualizar api.md nas duas línguas, e o da página de exceções reprova uma exceção nova em __all__ sem entrada em exceptions.md, que é o objetivo dos dois. Desfazer: git revert do commit de merge, que republica o site com as páginas anteriores.
SHA revisado: 2bfdb7e
…pção A seção sobre a entrada modificada no lugar dizia que uma segunda chamada com outro modelo levanta ValueError. Isso só vale quando algum campo novo não admite None; com campos Optional, a chamada não manda nenhuma linha ao modelo, não avisa e devolve None em todas as linhas. O texto agora descreve os dois casos e indica um DataFrame novo ou uma cópia feita antes da primeira chamada. A seção sobre ProviderAbortError dizia que resume=True processa só as pendentes. Isso vale para a entrada pandas e para a saída devolvida; a entrada polars repetida refaz todas as linhas, a lista só retoma com checkpoint_path, e com reprocess_columns as linhas não reprocessadas ficam processed com os valores anteriores, que resume=True sozinho não refaz. Refs #200
O guia dizia que rodar de novo com resume=True depois de uma interrupção processa só as pendentes, o que não vale para toda entrada nem com reprocess_columns. A frase dá lugar a um link para a seção de exceptions.md que descreve cada caso. Refs #200
O guia de busca web reusava o df já processado pelo bloco de Uso Básico. Na sequência da página, o trace completo não fazia chamada e json.loads recebia None, e os exemplos com MedicamentoInfo e MedicamentoRegulatorio levantavam ValueError de campos incompatíveis. Cada chamada sobre o df passa df.copy(), e a página remete à seção de entrada modificada no lugar, nas duas línguas.
A frase dizia que a linha do erro e todas as seguintes ficam sem status. No modo paralelo, as linhas já em andamento quando o erro chega terminam e ficam com status 'processed'; sem status ficam só a linha do erro e as ainda não despachadas. Corrigido nas duas línguas.
O bloco Usando Diferentes Providers chamava Model, que a página não define, e levantava NameError quando a página roda em sequência. Passa a usar Sentimento (Sentiment na versão em inglês), definido no início.
Revisão independente (rodada 2)Subagente revisor do Claude, sem ler o corpo nem os comentários do PR, sobre o SHA Corrigido em
Corrigido em
Nit, descartado: o helper Preexistente, vira issue: Preexistente, já corrigido na Verificado: gates verdes; unit 1621 passed (pandas 3.0.6) e codex 1415 passed; cobertura combinada 100%; prova do vermelho com docs e exemplos revertidos (6 testes novos reprovam); 6 de 7 mutações mortas, a sobrevivente equivalente; Eixos não executados: LLM real; notebooks além do 04 e do 08; abort do SHA revisado: 50f3dc5 |
# Conflicts: # CHANGELOG.md
Problema
Três exemplos da documentação quebram ou mostram um resultado que não aconteceu, e várias páginas afirmam o que o código não faz. Rodados com o LLM trocado por dublê na
mainde agora:docs/guides/basic-usage.mdem sequência: a chamada comCategorialevantaValueError: ... campos incompatíveis ['categoria', 'subcategoria'], porque odfjá saiu processado comSentimento; o mesmo acontece comEntidadeseResumo, e os três exemplos de providers fazem 0 chamadas ao modelo.df, e só a primeira chama o modelo; as três saídas da comparação entre prompts mostram o resultado do primeiro. Notebook 08: a célula 20 reusa odfda 13 e faz 0 chamadas.docs/guides/web-search.md, "Exemplo: Trace Completo":MedicamentoInfotemprompteprompt_appendemjson_schema_extra, e a chamada semsearch_per_field=TruelevantaValueErrorantes de qualquer linha.A causa dos dois primeiros é que o DataFrame pandas de entrada é modificado no lugar, o que nenhuma página dizia. Entre as afirmações, a mais cara é a assinatura de
api.md, que omitiaperguntasna 4ª posição:dataframeit(saida, M, "p", False)cai emperguntas, que é ignorado, e chama o modelo queresume=Falseevitaria.O que muda
Em
docs/edocs/en/, e nos três READMEs onde o trecho está neles:search_per_field=True_trace, a coluna do agente único, e passa a usarEmpresaInfo/CompanyInfo, o modelo de "Uso Básico", que não tem configuração por campo. Comsearch_per_field=Truea coluna seria_trace_{campo}, que é o exemplo "Trace por Campo", logo abaixodf.copy()df.copy()não bastaria, porque a célula 13 já gravou o status nodfbasic-usage.mdem sequênciadfpassamdf.copy(), inclusive as três de providers, e a página diz por quêapi.mdnão diz que a entrada é modificada no lugarapi.mdperguntas=Nonena 4ª posição, conferida contra amainde agoraindex.md,api.md)exceptions.mdsemProviderAbortErroreProviderUsageLimitErrorindex.md: saem no resumo final, só comparallel_requests > 1.performance.mdpassa a dizer o mesmo, porque o exemplo de lá mostra a seção numa chamada semparallel_requestspip install langchain-anthropicoudataframeit[all]; noclaude_codee nocodex, o extra. A linha doImportErroremexceptions.md, que dizia o mesmo que a FAQ, tambémapi_keynocodexe noclaude_codecodexrecusa comProviderConfigurationError, oclaude_codeignoraallnos READMEsindex.md, que dizia "todos os providers", tambémFica fora a afirmação de que a resposta fora do modelo volta ao LLM com o erro para correção (
index.md, card "Saída Estruturada"), que é da #193.Decisões
df_comentariosna célula 17. Sem ela, o erro que a célula 19 mostra lista as colunas que a 17 gravou (sentimento,_dataframeit_status, as de tokens), e o texto da célula fala do "mesmo DataFrame".dfem vários blocos (providers, performance, error-handling, web-search, llm-reference, os exemplos deapi.md) ficam como estão: ali os blocos são alternativas sobre umdfgenérico, muitos comModelde marcador, e não uma sequência para rodar. A seção nova deapi.mdcobre o caso.tests/test_exemplos.py::test_exemplo_nao_reusa_dataframe_ja_processadoreprova o notebook ou script deexample/que passa adataframeit()um DataFrame que uma chamada anterior já processou, ou uma cópia dele, sem atribuir o nome de novo no meio. O03_resume, que relê o checkpoint antes de cada chamada, passa.tests/test_api_publica.pyconfere que a assinatura deapi.md, nas duas línguas, tem a ordem deinspect.signature(dataframeit), e queexceptions.mdcita toda exceção de__all__.Closes #200