Pull requests

Open Source

Bug fixes to Python ML and LLM libraries I use. Each one starts from an issue I filed and reproduced, and ships with a regression test that fails on main and passes with the change.

Issues filed
93
bugs found & reproduced
Pull requests
47
51% of issues
Merged
24
26% of issues · 51% of PRs

Across 23 repositories I don’t own · 20 PRs still open · from the GitHub API · updated 8 Nov 2026

By repository · sorted by stars

Featured

Hugging Face Transformers — correct JSON schemas for union-typed tool arguments

huggingface/transformers · PR #49136 · Merged Sep 2026

get_json_schema turns a Python function's type hints into a tool-calling schema. When a parameter was a union such as str | list[str], the schema builder flattened every member into a bare "type" list and silently dropped items, additionalProperties, prefixItems and enum, so the model never saw what the list held or which literals were allowed. The fix only flattens members that carry nothing but a type and routes everything else through the existing anyOf branch, with a regression test covering list, dict and Literal unions.

View pull request ↗
PythonTool CallingJSON Schema

Pydantic AI — TOML files accepted as inline model input

pydantic/pydantic-ai · PR #8848 · Merged Sep 2026

BinaryContent.from_path() correctly inferred application/toml for .toml files, but the text-like media-type check did not recognise it, so the OpenAI Chat and Mistral model mappings rejected TOML as unsupported binary content before any request was sent. TOML is UTF-8 text, so the fix classifies it alongside YAML and JSON and lets it be inlined, with tests for the classifier and the resulting request shape.

View pull request ↗
PythonLLM AgentsMultimodal Input

Hugging Face TRL — SFTTrainer appends EOS to any text column

huggingface/trl · PR #7446 · Merged Sep 2026

During dataset preparation, SFTTrainer only appended the EOS token to a column literally named "text", while tokenization read whatever column dataset_text_field pointed to. Setting dataset_text_field to any other name therefore produced training examples with no EOS at all, with no error or warning, so the model never learned to stop. The fix passes dataset_text_field into the EOS step so both stages agree on the column, with a parametrized regression test that also adds the first EOS assertion for the default "text" path.

View pull request ↗
PythonLLM Fine-tuningData Preprocessing

MLflow — order-independent schema inference for missing values

mlflow/mlflow · PR #26260 · Merged Sep 2026

When inferring a model signature, MLflow types a None value as AnyType and merges it with the concrete types seen elsewhere. The merge only worked when AnyType came first: [{"k": None}, {"k": 1}] inferred an optional long, but [{"k": 1}, {"k": None}] raised "Properties are incompatible". For inputs like chat histories, where a tool-call message's content is None, infer_signature then silently fell back to an untyped schema. The fix makes the property, array and map merges accept AnyType on either side, with tests covering both orders, nested values and unchanged required-field semantics.

View pull request ↗
PythonMLOpsSchema Inference

Docling — case-insensitive format detection for file paths

docling-project/docling · PR #4430 · Merged Oct 2026

Docling guesses an input's format from magic bytes and, for plain-text formats it cannot sniff, from the file extension. The DocumentStream branch already lower-cased the extension, but the Path branch did not, so converting notes.VTT from disk failed with "File format not allowed" while the same content passed as a stream worked. The fix lower-cases the suffix in the path branch to match, with a parametrized test asserting that both input kinds resolve to the same format.

View pull request ↗
PythonDocument ParsingInput Handling

Accelerate — fix wait_for_everyone() crash in multi-process CPU runs

huggingface/accelerate · PR #4354 · Merged Oct 2026

An earlier change passed device_ids=[local_rank] to every torch.distributed.barrier call to silence a CUDA warning. With device_ids set, PyTorch picks the barrier device from the machine's accelerator, so a multi-process CPU (gloo) run on a Mac crashed in accelerator.wait_for_everyone() with "c10d::barrier not implemented for MPS". The repository's MULTI_CPU tests were skipped on MPS machines, so the crash had gone unnoticed for several releases. The fix leaves out device_ids for MULTI_CPU, letting PyTorch choose the CPU device for the gloo group, and was verified with a two-process reproduction script.

View pull request ↗
PythonPyTorchDistributed Training

Hugging Face Datasets — multi-dimensional array columns round-trip through pandas

huggingface/datasets · PR #8701 · Merged Oct 2026

Array2D…Array5D columns become a custom pandas ExtensionArray in to_pandas(), but that class only implemented the Arrow → pandas direction (__from_arrow__), not pandas → Arrow (__arrow_array__). As a result Dataset.from_pandas(ds.to_pandas()) and a batched map under with_format("pandas") both raised ArrowTypeError, for fixed and variable first-dimension shapes alike. Found with a round-trip property test over twenty-odd feature types. The fix adds __arrow_array__, reusing the writer's existing nested-list conversion and wrapping it in the matching extension type so the feature type survives the round trip; covered by a regression test for both directions.

View pull request ↗
PythonApache Arrowpandas

TransformerLens — consistent pos_slice across the two activation-caching APIs

TransformerLensOrg/TransformerLens · PR #1859 · Merged Oct 2026

run_with_cache and get_caching_hooks both accept pos_slice (keep some sequence positions) and remove_batch_dim. The hook-based path dropped the batch axis before slicing, so the hard-coded "position is dim 1" lookup landed on d_model or the heads and silently returned a slice of the wrong axis, while run_with_cache gave a different tensor for the same inputs. Review showed the hard-coded axis was itself wrong for head-major hooks (Gemma-3 / Cohere post-reshape norms, Mamba's conv input, MLA projections), so the final change lets each HookPoint declare its position axis, tags those components, and resolves the axis in both APIs. Verified by sweeping twelve architectures on random-init models; tests use no downloaded weights and assert the two APIs agree for int, range and list slices, including backward hooks.

View pull request ↗
PythonPyTorchMechanistic Interpretability

Sentence Transformers — load model2vec token mapping and weights in StaticEmbedding

huggingface/sentence-transformers · PR #4108 · Merged Oct 2026

StaticEmbedding.load, from_model2vec and from_distillation read only the embeddings tensor from a model2vec checkpoint and ignored the optional token → row mapping and per-token weights that newer model2vec releases add for vocabulary quantization. The official potion-code-16M therefore encoded silently wrong (cosine 0.64 against model2vec's own output), and quantized-vocabulary models crashed with an index error. Found by auditing the safetensors keys of every published model2vec checkpoint and comparing outputs tensor by tensor. The fix folds mapping and weights into one row per token at load time, which is mathematically equivalent to the weighted mean and leaves the forward pass and save format untouched; the maintainer added an FP32 promotion for half-precision weights before merging.

View pull request ↗
PythonPyTorchEmbeddings

Logfire — keep large negative integers in OTLP span attributes

pydantic/logfire · PR #2502 · Merged Oct 2026

OTLP integers are signed 64-bit, and Logfire already turned ints above the maximum into strings with a warning. The lower bound was never checked, so an int below -2**63 was handed to OpenTelemetry as is, failed protobuf encoding with "Value out of range", and the attribute was silently dropped from the exported span with nothing at the call site. The fix checks both bounds, rewords the warning to "outside the range of OTLP integers", and adds a test that -2**63 - 1 becomes a string while the boundary value -2**63 stays an int.

View pull request ↗
PythonOpenTelemetryObservability

Darts — NaN-safe thresholds in QuantileDetector and IQRDetector

unit8co/darts · PR #3251 · Merged Oct 2026

Both anomaly detectors learn their thresholds with np.quantile, so a single missing value in the fit series made the threshold NaN. With two bounds this surfaced as a misleading "low > high" error; with one bound it was silent and detect() flagged nothing at all. Switching to np.nanquantile fixes both detectors. Regression tests cover the low, high and two-sided cases plus the IQR variant, and the maintainer merged it as my first contribution to the project.

View pull request ↗
PythonNumPyTime Series

Awkward Array — reducers on boolean typetracer arrays

scikit-hep/awkward · PR #4433 · Merged Oct 2026

A recent change gave ak.mean and ak.var a float64 accumulator for boolean input, but called .astype on the intermediate result, which the shape-only TypeTracerArray backend does not have. Since dask-awkward builds its task graph on typetracers, common code like ak.mean(mask) crashed with an AttributeError from v2.12.0 on, across mean, var, std, corr and covar. The fix uses the backend-agnostic nplike.astype, and the test checks that typetracer and concrete arrays produce the same output form for all six functions, with known and unknown lengths.

View pull request ↗
PythonArray ComputingDask

Narwhals — Unicode-aware title case on SQL backends and older Polars

narwhals-dev/narwhals · PR #4085 · Merged Oct 2026

On DuckDB and Spark-like backends, str.to_titlecase split words with the ASCII-only regex [a-z]*[^a-z]*, so every accented letter started a new word and münchen straße silently became MüNchen StraßE. The fix switches to the Unicode letter classes \p{L}*\P{L}*, matching Python's str.title and native Polars. In review, the maintainer pointed out that the fallback for Polars < 1.35 rebuilt title case with the same regex, so it got the same fix, tested on Polars 0.20, 1.20 and 2.0.

View pull request ↗
PythonDataFramesSQL

sbi — KDE cross-validation that refines around the right bandwidth

sbi-dev/sbi · PR #2061 · Merged Oct 2026

With get_kde(bandwidth="cv"), when the best bandwidth landed on the edge of the search grid, the refinement step centered the next grid on the grid index (best_index_) instead of the bandwidth value, with the step sign flipped. The new grid could contain negative bandwidths, and on bimodal data it picked 0.08 where a fine-grid reference gives about 0.46. The fix re-centers on the selected bandwidth and keeps the grid positive; I checked it against a fine-grid reference. In review, the maintainer built on it directly: the lower edge now searches the next order of magnitude, CV respects sample weights, and the first grid is scaled by each dimension's standard deviation.

View pull request ↗
PythonSimulation-Based InferenceStatistics

inspect_ai — dataclass tool parameters that keep their types

UKGovernmentBEIS/inspect_ai · PR #5746 · Merged Oct 2026

When a @tool took a dataclass parameter defined in a module with from __future__ import annotations, the schema builder read Field.type directly, which is an unevaluated string like "int". The model saw a tool definition with every field untyped, and calling the tool crashed in issubclass("int", BaseModel). The fix resolves dataclass fields with get_type_hints(), the same way the TypedDict branch already did, and falls back to the raw annotation when resolution fails. In review, the maintainer found that a self-referential dataclass now recursed forever, so the schema builder tracks the classes it is expanding and stops at a cycle.

View pull request ↗
PythonLLM EvaluationType Hints

sqlglot — DAYOFWEEK transpiled to valid BigQuery

tobymao/sqlglot · PR #8778 · Merged Oct 2026

Transpiling DAYOFWEEK(x) from MySQL or Spark to BigQuery produced DAY_OF_WEEK(x), a function BigQuery does not have, so the generated query failed as soon as it ran. The BigQuery generator mapped DayOfMonth, DayOfYear and WeekOfYear to EXTRACT but had no entry for DayOfWeek, so it fell back to the default function name. After checking that all three dialects number days Sunday = 1 through Saturday = 7, the fix maps it to EXTRACT(DAYOFWEEK FROM x) with a regression test.

View pull request ↗
PythonSQLCompilers

hist — Ellipsis-aware wildcard indexing

scikit-hep/hist · PR #744 · Merged Oct 2026

String category indexes support wildcards, but when they came after an Ellipsis, as in h[..., "a*"], the pattern was resolved against the wrong axis. Depending on the histogram this silently selected the wrong bins, raised "No matches found", or failed with an IndexError on 1D histograms. The index transform used the position inside the index tuple as the axis number, which stops being true once an Ellipsis expands to several axes. The fix shifts positions after the Ellipsis by ndim - len(index), with tests for each case.

View pull request ↗
PythonScientific ComputingIndexing

DoWhy — Dagitty node names kept intact

py-why/dowhy · PR #1842 · Merged Nov 2026

DoWhy can load causal graphs exported from dagitty.net, but the converter stripped Dagitty markers such as exposure and outcome with a regex over the whole string. Any node whose name contained one of those words was silently renamed or emptied, which broke the resulting DOT graph for common epidemiology models. The fix only parses attributes inside each [...] block, splitting on commas outside quotes so values like pos="0.1,0.2" survive, and leaves node names and edges untouched. Two follow-up edge cases from Copilot review (brackets and escaped quotes inside quoted names) were fixed in the same PR.

View pull request ↗
PythonCausal InferenceParsing

All pull requests