Skip to content

Bump Python 3.11 → 3.14, make ML deps optional#2

Merged
franchb merged 3 commits intomainfrom
versions-bump
Feb 23, 2026
Merged

Bump Python 3.11 → 3.14, make ML deps optional#2
franchb merged 3 commits intomainfrom
versions-bump

Conversation

@franchb
Copy link
Owner

@franchb franchb commented Feb 22, 2026

Summary

  • Bump Python runtime target from 3.11 to 3.14 across .python-version, CI workflows, Dockerfiles, and ruff/ty configs
  • Move sentence-transformers, torch, transformers from core deps to an optional local-models extra — eliminates ~3GB of nvidia/CUDA downloads for API-only usage
  • Replace fragile sed-based dep removal in Dockerfile with uv sync --extra local-models conditional
  • Upgrade transitive deps for cp314 wheel availability: onnxruntime 1.20.1→1.24.2, markitdown 0.1.5b1→0.1.5, streamlit 1.51.0→1.54.0, pyarrow 21.0.0→23.0.1

requires-python = ">=3.11" lower bound intentionally unchanged to preserve compatibility for downstream consumers.

Test plan

  • ./scripts/hooks/lint.sh — ruff, ty, eslint, prettier all pass
  • Unit tests — 88 passed, 6 skipped (integration tests needing API keys)
  • uv build — package builds successfully
  • CI runs green on this PR
  • Docker build validates in CI (build-docker-slim job)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Optional "local models" support can be enabled for API deployments.
    • Embeddings now support specifying an encoding format (e.g., "float").
  • Chores

    • Upgraded Python runtime target to 3.14 across CI, tooling, and deployment images.
    • Docker builds and dependency sync now respect the optional local-models setting.
  • Documentation

    • Added fork context and development/deployment guidance.

- Update .python-version, CI workflow, Dockerfiles, and ruff/ty configs
  to target Python 3.14
- Move sentence-transformers, torch, transformers to optional
  `local-models` extra — eliminates ~3GB nvidia/CUDA downloads for
  API-only usage
- Replace fragile sed-based dep removal in Dockerfile with
  `uv sync --extra local-models` conditional
- Upgrade transitive deps for cp314 wheel availability:
  onnxruntime 1.20.1→1.24.2, markitdown 0.1.5b1→0.1.5,
  streamlit 1.51.0→1.54.0, pyarrow 21.0.0→23.0.1
- Add ty unresolved-import ignore for optional ML imports guarded
  by try/except
- Add fork context documentation to CLAUDE.md

requires-python lower bound (>=3.11) intentionally unchanged to
preserve compatibility for downstream consumers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@franchb franchb self-assigned this Feb 22, 2026
@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Switches Python target from 3.11 to 3.14 across CI, Docker, and tooling; moves heavy ML deps into a new optional local-models group; Dockerfile conditionally syncs those optional deps via uv sync; adds encoding_format="float" to LiteLLM embedding requests; adds fork context docs.

Changes

Cohort / File(s) Summary
CI & repo baseline
/.github/workflows/ci.yml, /.python-version
Update Python runtime/spec from 3.11 → 3.14 for CI jobs and repo baseline.
Docker build / dependency sync
docker/standalone/Dockerfile
Bump base images to python:3.14-slim; replace pyproject sed removal with conditional dependency sync: uv sync --extra local-models when INCLUDE_LOCAL_MODELS=true, otherwise uv sync.
API optional ML deps & tooling
hindsight-api/pyproject.toml
Remove heavy ML deps from main deps and add [project.optional-dependencies].local-models with sentence-transformers, transformers, torch; update [tool.ruff] target-version → py314; update Ty python-version → 3.14 and add unresolved-import/unresolved-attribute ignore rules.
Dev/embed tooling
hindsight-dev/pyproject.toml, hindsight-embed/pyproject.toml
Update tooling/typecheck targets from py311/3.11 → py314/3.14 and adjust Ty rules as noted.
Embeddings API changes
hindsight-api/hindsight_api/engine/embeddings.py, hindsight-api/tests/test_litellm_sdk_embeddings.py
Add encoding_format="float" parameter to LiteLLM embedding request payloads and tests; propagate encoding_format to Litellm SDK calls and SDK-backed embedding initialization/use.
Documentation
CLAUDE.md
Add detailed fork context and contributor/development guidance (documentation only).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped from three-eleven to three-point-one-four,
Optional models tucked in a soft, secret drawer,
Docker listens when INCLUDE_LOCAL_MODELS calls,
Embeddings now whisper "float" as they scuttle the halls.
A tiny hop, a joyful twitch — the codebase springs once more.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main objectives: upgrading Python from 3.11 to 3.14 and making ML dependencies optional, which are the primary changes across all modified files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch versions-bump

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
docker/standalone/Dockerfile (1)

44-46: ⚠️ Potential issue | 🟠 Major

Non-reproducible builds: uv.lock is not copied into the Docker build context.

uv sync without --frozen will generate a new lock file on every build, potentially installing different package versions each time. This undermines the specific transitive-dependency version pins the PR explicitly upgrades (onnxruntime 1.24.2, markitdown 0.1.5, etc.). Adding COPY hindsight-api/uv.lock ./api/ and passing --frozen (shown in the fix above) guarantees bit-for-bit reproducibility.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/standalone/Dockerfile` around lines 44 - 46, The Dockerfile currently
copies pyproject.toml and README.md but omits the uv.lock file, causing
non-reproducible builds; update the Dockerfile to COPY hindsight-api/uv.lock
into ./api/ alongside pyproject.toml and README.md and ensure the uv sync
invocation used during the image build (the RUN step that executes "uv sync") is
run with the --frozen flag so the lockfile is strictly honored; look for the
COPY lines and the RUN that calls uv sync in the Dockerfile and add the uv.lock
COPY and the --frozen option to uv sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@CLAUDE.md`:
- Line 22: Update the heading "### CI workflows (`.github/workflows/`)" to use
the correct capitalization "GitHub"; then add a blank line before and after both
CI tables (the two markdown tables after that heading) to satisfy MD058; finally
add an explicit language tag (e.g., ```yaml) to the fenced code block around
line 41 to satisfy MD040. Make these edits in CLAUDE.md, targeting the "### CI
workflows (`.github/workflows/`)" section and its subsequent tables and fenced
code block.
- Line 27: Update the CI table row for the `lint` job in CLAUDE.md to reflect
the new Python version: change the description text that currently reads "Python
3.11 + Node 20" to "Python 3.14 + Node 20" so the `lint` row accurately matches
the PR changes.

In `@docker/standalone/Dockerfile`:
- Around line 50-56: The RUN block invoking uv sync will try to install the
project (hindsight-api) because pyproject.toml declares a build-system, but the
source isn't copied yet; change the sequence to first run uv sync with
--no-install-project (and --frozen if you want deterministic installs) when
copying only pyproject.toml and uv.lock, then copy the hindsight_api/ source and
run uv sync again (or uv pip install -e .) to install the project; update the
conditional using the INCLUDE_LOCAL_MODELS variable so both the local-models
extra and the no-install-project flag are respected (e.g., use "uv sync
--no-install-project" in the first step and a later "uv sync" after source
copy).

In `@hindsight-dev/pyproject.toml`:
- Line 48: The pyproject setting target-version = "py314" conflicts with
requires-python = ">=3.11"; update the configuration to be consistent by either
(A) if you intend to drop 3.11–3.13 support, change requires-python to ">=3.14"
or (B) if you must keep 3.11 compatibility, revert target-version to "py311";
apply the same change consistently in the other two pyproject.toml files
(hindsight-embed and hindsight-api) so target-version and requires-python match
your intended supported Python range.

---

Outside diff comments:
In `@docker/standalone/Dockerfile`:
- Around line 44-46: The Dockerfile currently copies pyproject.toml and
README.md but omits the uv.lock file, causing non-reproducible builds; update
the Dockerfile to COPY hindsight-api/uv.lock into ./api/ alongside
pyproject.toml and README.md and ensure the uv sync invocation used during the
image build (the RUN step that executes "uv sync") is run with the --frozen flag
so the lockfile is strictly honored; look for the COPY lines and the RUN that
calls uv sync in the Dockerfile and add the uv.lock COPY and the --frozen option
to uv sync.

---

Duplicate comments:
In `@hindsight-api/pyproject.toml`:
- Line 126: The pyproject.toml has a mismatch between target-version = "py314"
and requires-python = ">=3.11"; update one of these so they match—either set
target-version = "py311" to match requires-python or raise requires-python to
">=3.14" so it matches target-version; edit the keys target-version and
requires-python in hindsight-api/pyproject.toml to keep them consistent.

In `@hindsight-embed/pyproject.toml`:
- Line 31: The pyproject.toml has a mismatch between target-version = "py314"
and the package Python requirement (requires-python = ">=3.11"); update one to
match the other consistently—either set target-version to "py311" to align with
requires-python or bump requires-python to ">=3.14" so it matches
target-version; locate and change the target-version and/or requires-python
entries in this file (and mirror the same change in hindsight-dev/pyproject.toml
if needed) so both settings refer to the same Python major/minor target.

- **Docker images**: Only slim variants are built (`INCLUDE_LOCAL_MODELS=false`), targeting `linux/amd64` only. Images are published to `ghcr.io/franchb/` (not upstream's registry).
- **No PyPI/npm publishing**: This fork does not publish packages. Only Docker images are released.

### CI workflows (`.github/workflows/`)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Capitalise "GitHub".

The platform name should be "GitHub" (capital H). Also, the two CI tables (lines 25–30 and 33–35) and the fenced code block at line 41 lack a blank line before/after the tables (MD058) and a language tag (MD040) respectively, as flagged by markdownlint-cli2.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~22-~22: The official name of this software platform is spelled with a capital “H”.
Context: ...images are released. ### CI workflows (.github/workflows/) ci.yml -- runs on p...

(GITHUB)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` at line 22, Update the heading "### CI workflows
(`.github/workflows/`)" to use the correct capitalization "GitHub"; then add a
blank line before and after both CI tables (the two markdown tables after that
heading) to satisfy MD058; finally add an explicit language tag (e.g., ```yaml)
to the fenced code block around line 41 to satisfy MD040. Make these edits in
CLAUDE.md, targeting the "### CI workflows (`.github/workflows/`)" section and
its subsequent tables and fenced code block.

**`ci.yml`** -- runs on push to `main` and PRs targeting `main`:
| Job | What it does |
|-----|-------------|
| `lint` | Python 3.11 + Node 20. Runs `./scripts/hooks/lint.sh` (Ruff, ty, ESLint, Prettier) |
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stale Python version in the CI table.

The lint job description says "Python 3.11 + Node 20" but this PR changes it to Python 3.14. Should read "Python 3.14 + Node 20".

📝 Proposed fix
-| `lint` | Python 3.11 + Node 20. Runs `./scripts/hooks/lint.sh` (Ruff, ty, ESLint, Prettier) |
+| `lint` | Python 3.14 + Node 20. Runs `./scripts/hooks/lint.sh` (Ruff, ty, ESLint, Prettier) |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| `lint` | Python 3.11 + Node 20. Runs `./scripts/hooks/lint.sh` (Ruff, ty, ESLint, Prettier) |
| `lint` | Python 3.14 + Node 20. Runs `./scripts/hooks/lint.sh` (Ruff, ty, ESLint, Prettier) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` at line 27, Update the CI table row for the `lint` job in
CLAUDE.md to reflect the new Python version: change the description text that
currently reads "Python 3.11 + Node 20" to "Python 3.14 + Node 20" so the `lint`
row accurately matches the PR changes.

Comment on lines +50 to 56
# Sync dependencies
# When INCLUDE_LOCAL_MODELS=true, install the local-models extra (sentence-transformers, torch, transformers)
RUN if [ "$INCLUDE_LOCAL_MODELS" = "true" ]; then \
uv sync --extra local-models; \
else \
uv sync; \
fi
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

uv sync will fail: missing --no-install-project when source is not yet present.

hindsight-api/pyproject.toml defines a [build-system] (hatchling). uv uses the presence of a build system to determine if a project contains a package that should be installed in the project virtual environment. If a build system is not defined, uv will not attempt to build or install the project itself, just its dependencies. Since a build-system is defined here, uv sync without --no-install-project will attempt to build and install hindsight-api — but hindsight_api/ source isn't copied until line 59. The build will fail.

The comment on line 61 ("uv sync only installed dependencies, not the package itself") reflects the intent but not the actual behaviour.

The canonical uv + Docker pattern is: copy uv.lock + pyproject.tomluv sync --frozen --no-install-project → copy source → uv sync --frozen (or uv pip install -e .). See the uv Docker guide. uv can install only the dependencies with --no-install-project. It only needs the lock file and pyproject.toml for this. Installing the dependencies separately ensures better caching since you probably don't change dependencies as much as the code.

🐛 Proposed fix
 COPY hindsight-api/pyproject.toml ./api/
 COPY hindsight-api/README.md ./api/
+COPY hindsight-api/uv.lock ./api/

 WORKDIR /app/api

 # Sync dependencies
 # When INCLUDE_LOCAL_MODELS=true, install the local-models extra (sentence-transformers, torch, transformers)
 RUN if [ "$INCLUDE_LOCAL_MODELS" = "true" ]; then \
-        uv sync --extra local-models; \
+        uv sync --frozen --no-install-project --extra local-models; \
     else \
-        uv sync; \
+        uv sync --frozen --no-install-project; \
     fi

 # Copy source code (alembic migrations are inside hindsight_api/)
 COPY hindsight-api/hindsight_api ./hindsight_api

 # Install the local package (uv sync only installed dependencies, not the package itself)
 RUN uv pip install -e .
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Sync dependencies
# When INCLUDE_LOCAL_MODELS=true, install the local-models extra (sentence-transformers, torch, transformers)
RUN if [ "$INCLUDE_LOCAL_MODELS" = "true" ]; then \
uv sync --extra local-models; \
else \
uv sync; \
fi
COPY hindsight-api/pyproject.toml ./api/
COPY hindsight-api/README.md ./api/
COPY hindsight-api/uv.lock ./api/
WORKDIR /app/api
# Sync dependencies
# When INCLUDE_LOCAL_MODELS=true, install the local-models extra (sentence-transformers, torch, transformers)
RUN if [ "$INCLUDE_LOCAL_MODELS" = "true" ]; then \
uv sync --frozen --no-install-project --extra local-models; \
else \
uv sync --frozen --no-install-project; \
fi
# Copy source code (alembic migrations are inside hindsight_api/)
COPY hindsight-api/hindsight_api ./hindsight_api
# Install the local package (uv sync only installed dependencies, not the package itself)
RUN uv pip install -e .
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker/standalone/Dockerfile` around lines 50 - 56, The RUN block invoking uv
sync will try to install the project (hindsight-api) because pyproject.toml
declares a build-system, but the source isn't copied yet; change the sequence to
first run uv sync with --no-install-project (and --frozen if you want
deterministic installs) when copying only pyproject.toml and uv.lock, then copy
the hindsight_api/ source and run uv sync again (or uv pip install -e .) to
install the project; update the conditional using the INCLUDE_LOCAL_MODELS
variable so both the local-models extra and the no-install-project flag are
respected (e.g., use "uv sync --no-install-project" in the first step and a
later "uv sync" after source copy).

[tool.ruff]
line-length = 120
target-version = "py311"
target-version = "py314"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

target-version = "py314" conflicts with requires-python = ">=3.11".

Ruff's target-version controls version-specific lint rules and formatter behaviour. This setting is used by the linter to enforce any enabled version-specific lint rules, as well as by the formatter for any version-specific formatting options. Starting with Ruff v0.15.0, parentheses around tuples of exceptions in except clauses will now be removed on Python 3.14 and later. Code formatted with py314 (e.g. except A, B, C: instead of except (A, B, C):) is a syntax error on Python 3.11–3.13, which requires-python = ">=3.11" still advertises as supported.

The same issue applies to hindsight-embed/pyproject.toml and hindsight-api/pyproject.toml.

If Python 3.11–3.13 compatibility is intentionally dropped by this PR, consider updating requires-python = ">=3.14". If 3.11 compatibility must be preserved, revert target-version to "py311" (matching requires-python).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-dev/pyproject.toml` at line 48, The pyproject setting
target-version = "py314" conflicts with requires-python = ">=3.11"; update the
configuration to be consistent by either (A) if you intend to drop 3.11–3.13
support, change requires-python to ">=3.14" or (B) if you must keep 3.11
compatibility, revert target-version to "py311"; apply the same change
consistently in the other two pyproject.toml files (hindsight-embed and
hindsight-api) so target-version and requires-python match your intended
supported Python range.

Batch upgrades across hindsight-api dependencies:

Core infrastructure:
- asyncpg 0.30→0.31, sqlalchemy 2.0.44→2.0.46, alembic 1.17→1.18
- pgvector 0.4.1→0.4.2, greenlet 3.2→3.3, pg0-embedded 0.11→0.12

Web framework:
- fastapi 0.121→0.131, uvicorn 0.38→0.41, fastmcp 2.14→3.0

AI/LLM SDKs:
- openai 2.20→2.21, anthropic 0.75→0.83, google-genai 1.53→1.64
- litellm 1.81.10→1.81.14, claude-agent-sdk 0.1.31→0.1.39
- langchain-core 1.2.7→1.2.14, langchain-text-splitters 1.0→1.1

Other:
- pydantic 2.12.4→2.12.5, rich 14.2→14.3, PyJWT 2.10→2.11
- dateparser 1.2→1.3, google-auth 2.43→2.48, cohere 5.20.1→5.20.6
- typer 0.20→0.24, obstore 0.8→0.9, authlib 1.6.6→1.6.8
- filelock 3.20→3.24

Dev deps:
- pytest 9.0.0→9.0.2, ty 0.0.8→0.0.18

Postponed: ruff 0.15.2 has a regression that reformats
except (ExcA, ExcB): to except ExcA, ExcB: (Python 2 syntax,
SyntaxError in Python 3). Staying on ruff 0.14.9.

Also adds unresolved-attribute = ignore to ty rules in
hindsight-api and hindsight-dev to suppress false positives on
Optional types accessed after truthy checks (ty 0.0.18 is stricter).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
hindsight-api/pyproject.toml (1)

184-185: Global unresolved-attribute = "ignore" is an overly broad ty suppression.

unresolved-import = "ignore" is fine for optional deps behind try/except. However, unresolved-attribute = "ignore" silences all attribute-resolution diagnostics project-wide, including real typos and API mismatches unrelated to optional ML imports.

Consider scoping attribute suppression to the files that actually access optional types, using # type: ignore[attribute] inline at each call site, or ty's per-file ignore rules, rather than a blanket project-wide rule.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-api/pyproject.toml` around lines 184 - 185, Remove the global
unresolved-attribute = "ignore" entry from pyproject.toml and instead apply
attribute-resolution suppressions only where needed: either add per-file ty
ignore rules (e.g., a per-file my_module.py: disable attribute checks via the
file's metadata settings) or place inline annotations at specific call sites
using # type: ignore[attribute]; keep unresolved-import = "ignore" as-is for
optional deps. Update any locations that previously relied on the global rule by
replacing broad suppression with targeted # type: ignore[attribute] comments on
the exact attribute access or by adding a file-scoped ignore, and run the type
checker to ensure no other unresolved-attribute issues remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@hindsight-api/pyproject.toml`:
- Around line 60-64: Tighten the dependency constraints in the local-models
list: change the sentence-transformers and transformers entries so they forbid
major v5 upgrades (e.g., set sentence-transformers to a 3.x-compatible range
like "sentence-transformers>=3.3.0,<4.0" and transformers to
"transformers>=4.53.0,<5.0"), then run dependency resolution/update
(poetry/pip-compile) and test codepaths that use sentence-transformers encode_*
APIs (refer to the local-models list and the sentence-transformers /
transformers package names) to ensure no breaking API changes were pulled in.

---

Duplicate comments:
In `@hindsight-api/pyproject.toml`:
- Line 126: The pyproject mismatch: Ruff's target-version = "py314" conflicts
with requires-python = ">=3.11" so Ruff will apply Python 3.14 transforms
despite advertised 3.11 support; update one of them so they match—either set
target-version to "py311" to match requires-python or raise requires-python to
">=3.14" (or remove target-version if you want Ruff to follow requires-python),
and ensure the keys target-version and requires-python in pyproject.toml are
consistent.

In `@hindsight-dev/pyproject.toml`:
- Line 94: The project-level setting unresolved-attribute = "ignore" in
pyproject.toml is too broad; remove that global entry and replace it with
targeted suppressions: remove the unresolved-attribute key from the global
config and instead add per-file or inline suppressions where needed (for example
use file-level tool-specific config or inline ignore comments for the exact
attribute accesses) so only optional-type-after-truthy-check cases are silenced;
look for the unresolved-attribute entry in pyproject.toml to remove and add
targeted ignores in the specific modules/functions that trigger the warning.
- Line 48: The pyproject.toml currently sets Ruff's target-version = "py314"
which conflicts with the declared project.requires-python = ">=3.11"; change
this so Ruff targets the same minimum Python declared in the project (either
remove or update target-version to match project.requires-python, e.g., use
"py311" or omit target-version per Ruff docs) and ensure project.requires-python
remains ">=3.11" so Ruff won't apply py3.14-specific transformations to a 3.11+
package; update the target-version key (or remove it) rather than leaving
target-version = "py314".

---

Nitpick comments:
In `@hindsight-api/pyproject.toml`:
- Around line 184-185: Remove the global unresolved-attribute = "ignore" entry
from pyproject.toml and instead apply attribute-resolution suppressions only
where needed: either add per-file ty ignore rules (e.g., a per-file
my_module.py: disable attribute checks via the file's metadata settings) or
place inline annotations at specific call sites using # type: ignore[attribute];
keep unresolved-import = "ignore" as-is for optional deps. Update any locations
that previously relied on the global rule by replacing broad suppression with
targeted # type: ignore[attribute] comments on the exact attribute access or by
adding a file-scoped ignore, and run the type checker to ensure no other
unresolved-attribute issues remain.

Comment on lines +60 to +64
local-models = [
"sentence-transformers>=3.3.0",
"transformers>=4.53.0",
"torch>=2.6.0",
]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how sentence-transformers and transformers are actually used in the codebase
rg -n --type py -C2 'sentence[_-]transformers|SentenceTransformer|from sentence_transformers|from transformers|import transformers'

Repository: franchb/hindsight

Length of output: 5673


🏁 Script executed:

# Also check if there are any version pins or constraints mentioned elsewhere in the project
fd -e "txt" -e "lock" -e "in" . | xargs rg -l "sentence-transformers|transformers" 2>/dev/null | head -20

Repository: franchb/hindsight

Length of output: 68


🌐 Web query:

sentence-transformers 5.0 release breaking changes API

💡 Result:

Sentence-Transformers v5.0.0 (released July 1, 2025) introduced several API/migration changes you’ll likely feel as “breaking” when upgrading from v4.x. [3]

Key breaking / migration-relevant API changes in v5.0

  • New retrieval-specific encode entry points: use model.encode_query() and model.encode_document() (recommended for IR) instead of manually managing prompts / routing via encode(). These methods automatically select the “query”/“document” prompt (if present) and set the task for routing (if a Router exists). [1]
  • encode_multi_process() is deprecated: multi-process encoding is now done via model.encode(..., device=[...], chunk_size=..., pool=...) (i.e., pass a list of devices to device). [1]
  • models.Asymmodels.Router rename/update: asymmetric routing is now done with the Router module (more consistent API); encode_query()/encode_document() integrate with Router automatically, and for other routes you use task=... on encode(). [1]
  • Asymmetric training data plumbing changed: instead of encoding dict-wrapped inputs (e.g. {"query": "..."}" inside samples), v5 adds router_mapping=... in SentenceTransformerTrainingArguments to map dataset columns to Router routes. [1]
  • Custom modules: prefer Sentence-Transformers’ Module / InputModule base classes (instead of plain torch.nn.Module) to get the library’s standardized save/load/config helpers (load_*, save_*, get_config_dict, etc.). [1]

Additional context on the v5.0 direction/features (Sparse encoders, encode improvements, Router) is summarized in the release announcement. [2]

Sources: Migration guide / v4→v5 notes [1], v5.0 announcement [2], PyPI release history (v5.0.0 date) [3].


🌐 Web query:

transformers 5.0 release status 2026

💡 Result:

As of Feb 22, 2026, Hugging Face Transformers 5.0 is fully released (not just a release candidate):

  • v5.0.0 (final) was released on Jan 26, 2026. [1][2]
  • The v5.0.0 RC cycle started with v5.0.0rc0 on Dec 1, 2025. [2][3]
  • The latest stable 5.x on PyPI is v5.2.0 (Feb 16, 2026) (with v5.1.0 on Feb 5, 2026). [1][2]

Sources: PyPI release history and files for transformers, GitHub releases, and the official HF blog post announcing the v5 RC. [1][2][3]


sentence-transformers>=3.3.0 resolves to v5.x today — verify API compatibility and tighten constraints.

The current stable sentence-transformers is v5.2.3 (released July 1, 2025), a major version jump from the v3.3.0 lower bound. The constraint will resolve to v5.x, which introduced breaking API changes including new encode_query()/encode_document() entry points, deprecation of encode_multi_process(), and Router-based asymmetric encoding. Additionally, transformers v5.0.0 is already released (Jan 26, 2026) and >=4.53.0 currently resolves to v5.x, which is a major version jump that may introduce incompatibilities.

Tighten both constraints to prevent unexpected major version upgrades:

📦 Suggested version pinning
 local-models = [
-    "sentence-transformers>=3.3.0",
-    "transformers>=4.53.0",
+    "sentence-transformers>=5.0.0,<6.0",
+    "transformers>=5.0.0,<6.0",
     "torch>=2.6.0",
 ]

Alternatively, to remain on v4 of transformers, use transformers>=4.53.0,<5.0.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-api/pyproject.toml` around lines 60 - 64, Tighten the dependency
constraints in the local-models list: change the sentence-transformers and
transformers entries so they forbid major v5 upgrades (e.g., set
sentence-transformers to a 3.x-compatible range like
"sentence-transformers>=3.3.0,<4.0" and transformers to
"transformers>=4.53.0,<5.0"), then run dependency resolution/update
(poetry/pip-compile) and test codepaths that use sentence-transformers encode_*
APIs (refer to the local-models list and the sentence-transformers /
transformers package names) to ensure no breaking API changes were pulled in.

DeepInfra rejects requests when encoding_format is null. LiteLLM sets
it to None by default, so we explicitly pass "float" — the only format
compatible with our list[list[float]] return type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
hindsight-api/hindsight_api/engine/embeddings.py (1)

793-798: ⚠️ Potential issue | 🟡 Minor

encoding_format="float" added to SDK path — add to proxy path for consistency.

The encoding_format parameter is valid and well-supported by litellm for both SDK and gateway /embeddings routes. However, there's an inconsistency: LiteLLMSDKEmbeddings (lines 793–798, 840–850) now explicitly includes "encoding_format": "float", while LiteLLMEmbeddings (proxy variant, lines 678, 713) omits it from its JSON payloads.

Since "float" is the default in both the OpenAI API and litellm, both paths will return float embeddings unless explicitly overridden. Still, add "encoding_format": "float" to the JSON bodies in LiteLLMEmbeddings.initialize() and LiteLLMEmbeddings.encode() for defensive consistency and to prevent issues if a gateway or provider changes defaults.

Proposed fixes for LiteLLMEmbeddings
             response = self._client.post(
                 f"{self.api_base}/embeddings",
-                json={"model": self.model, "input": ["test"]},
+                json={"model": self.model, "input": ["test"], "encoding_format": "float"},
             )
             response = self._client.post(
                 f"{self.api_base}/embeddings",
-                json={"model": self.model, "input": batch},
+                json={"model": self.model, "input": batch, "encoding_format": "float"},
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-api/hindsight_api/engine/embeddings.py` around lines 793 - 798, The
proxy path is missing the explicit "encoding_format": "float" key—update the
JSON payloads sent by LiteLLMEmbeddings by adding "encoding_format": "float" to
the request bodies in LiteLLMEmbeddings.initialize() and
LiteLLMEmbeddings.encode() so they match LiteLLMSDKEmbeddings (which already
sets it), ensuring both initialization and encode calls include that field in
their JSON.
🧹 Nitpick comments (2)
hindsight-dev/pyproject.toml (1)

75-94: Blanket unresolved-attribute = "ignore" disables a useful safety net.

This rule suppresses all unresolved attribute errors across the entire project, not just for the optional ML imports. Consider using inline # ty: ignore[unresolved-attribute] comments on the specific lines where optional-import attributes are accessed, so the rule continues to catch real typos and missing attributes elsewhere.

That said, for this hindsight-dev sub-package (as opposed to the API package), the blast radius is smaller, so this is a low-priority suggestion.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-dev/pyproject.toml` around lines 75 - 94, Remove the blanket
unresolved-attribute = "ignore" entry from the [tool.ty.rules] block in
pyproject.toml and instead add targeted inline suppressions (# ty:
ignore[unresolved-attribute]) on the specific lines that access attributes of
optional ML/dynamic imports; keep unresolved-attribute enabled globally so ty
still catches real typos and only silence it where necessary in the codebase
(search for optional import sites and attribute accesses to add the inline
comments).
hindsight-api/pyproject.toml (1)

184-185: Replace broad unresolved-attribute suppression with targeted inline ignore comments.

The project-wide unresolved-attribute = "ignore" rule suppresses typos and missing-member errors across 134 Python files in the production API package. While unresolved-import = "ignore" is justified for optional dependencies guarded by try/except, there are currently no inline # ty: ignore[unresolved-attribute] comments in the codebase. Instead of globally hiding attribute resolution errors, identify the specific lines that legitimately need the suppression (e.g., accessing attributes on optional types after truthy checks) and apply targeted comments to only those locations. This prevents silent failures from genuine typos like response.dta instead of response.data.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hindsight-api/pyproject.toml` around lines 184 - 185, Remove the broad
project-wide unresolved-attribute = "ignore" setting from pyproject.toml and
instead add targeted inline suppressions where truly necessary by placing "#
type: ignore[unresolved-attribute]" on the specific lines that access attributes
of optional/third-party objects after proper guards; search the production API
package for legitimate patterns (e.g., attribute access after truthy checks) and
add the inline comment only at those call sites while leaving other files to
surface real unresolved-attribute errors so typos like response.dta will be
caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@hindsight-api/hindsight_api/engine/embeddings.py`:
- Around line 793-798: The proxy path is missing the explicit "encoding_format":
"float" key—update the JSON payloads sent by LiteLLMEmbeddings by adding
"encoding_format": "float" to the request bodies in
LiteLLMEmbeddings.initialize() and LiteLLMEmbeddings.encode() so they match
LiteLLMSDKEmbeddings (which already sets it), ensuring both initialization and
encode calls include that field in their JSON.

---

Duplicate comments:
In `@hindsight-api/pyproject.toml`:
- Line 126: The pyproject mismatch: update the mypy/formatters target setting so
it matches the package Python requirement—change target-version = "py314" to
target-version = "py311" (or else raise requires-python to >=3.14) so that the
target-version and requires-python are consistent; locate the target-version
entry in pyproject.toml and make the change where target-version = "py314" is
defined.
- Around line 60-64: The dependency constraints in the local-models group
(sentence-transformers, transformers, torch) are too loose and may pull breaking
major releases (e.g., transformers v5.x); update the local-models specification
to pin or bound major versions (for example use
sentence-transformers>=3.3.0,<4.0.0 and transformers>=4.53.0,<5.0.0 and a
compatible torch range) in pyproject.toml, verify compatibility in any
initialization code that imports these packages (search for references to
local-models or imports of sentence_transformers / transformers / torch) and run
tests to confirm no regressions.

In `@hindsight-dev/pyproject.toml`:
- Line 48: The pyproject.toml has a mismatch: target-version = "py314" conflicts
with requires-python = ">=3.11"; make these consistent by either changing
target-version to a value compatible with the declared minimum (e.g., "py311" or
removing target-version) or by raising requires-python to ">=3.14" (or a
matching range) so both keys align; update the target-version and/or
requires-python entries accordingly (refer to the target-version and
requires-python keys in pyproject.toml).

---

Nitpick comments:
In `@hindsight-api/pyproject.toml`:
- Around line 184-185: Remove the broad project-wide unresolved-attribute =
"ignore" setting from pyproject.toml and instead add targeted inline
suppressions where truly necessary by placing "# type:
ignore[unresolved-attribute]" on the specific lines that access attributes of
optional/third-party objects after proper guards; search the production API
package for legitimate patterns (e.g., attribute access after truthy checks) and
add the inline comment only at those call sites while leaving other files to
surface real unresolved-attribute errors so typos like response.dta will be
caught.

In `@hindsight-dev/pyproject.toml`:
- Around line 75-94: Remove the blanket unresolved-attribute = "ignore" entry
from the [tool.ty.rules] block in pyproject.toml and instead add targeted inline
suppressions (# ty: ignore[unresolved-attribute]) on the specific lines that
access attributes of optional ML/dynamic imports; keep unresolved-attribute
enabled globally so ty still catches real typos and only silence it where
necessary in the codebase (search for optional import sites and attribute
accesses to add the inline comments).

@franchb franchb merged commit ee058e4 into main Feb 23, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant