Conversation
There was a problem hiding this comment.
Code Review
This pull request modernizes the narratives agent toolchain by migrating to uv, targeting Python 3.14, and restructuring the codebase into a proper narratives_agent package. It also introduces strict mypy type-checking, adds pytest unit tests, and updates the Dockerfile, CI pipeline, and documentation. However, three critical syntax errors were introduced in narratives/agent/src/narratives_agent/mcp/data_utils.py where multiple exceptions are caught using comma separation instead of a tuple, which will cause SyntaxErrors at runtime in Python 3.
beets
left a comment
There was a problem hiding this comment.
LGTM - thanks for the refactor!
| # This module is `agent/src/narratives_agent/config.py`, so `agent/` is three | ||
| # levels up. Anything resolving a path against the agent directory should read | ||
| # this rather than counting parents of its own `__file__`. | ||
| AGENT_ROOT = Path(__file__).resolve().parents[2] |
There was a problem hiding this comment.
Counting Path(__file__).resolve().parents[2] is brittle now that narratives_agent is an installable package — it only resolves to /app in the container because uv sync defaults to an editable install (/app/src/narratives_agent/config.py). If --no-editable or a wheel install is ever used, __file__ moves into .venv/lib/python3.14/site-packages/ and parents[2] silently resolves to /app/.venv/lib instead of /app.
Consider honoring an AGENT_ROOT environment variable (and setting ENV AGENT_ROOT=/app in Dockerfile) with parents[2] only as the local-dev fallback:
| AGENT_ROOT = Path(__file__).resolve().parents[2] | |
| AGENT_ROOT = Path( | |
| os.environ.get("AGENT_ROOT") or Path(__file__).resolve().parents[2] | |
| ) |
| When `DATA_PLANE_AUTH=none` is set in the environment, `attach_auth` exits | ||
| early without attaching any headers. Clearing `DATA_PLANE_AUTH` ensures |
There was a problem hiding this comment.
Nit: attach_auth in gcp_auth.py:114 checks os.environ.get("DATA_PLANE_AUTH", "auto").lower() == "off" rather than "none".
| When `DATA_PLANE_AUTH=none` is set in the environment, `attach_auth` exits | |
| early without attaching any headers. Clearing `DATA_PLANE_AUTH` ensures | |
| When `DATA_PLANE_AUTH=off` is set in the environment, `attach_auth` exits | |
| early without attaching any headers. Clearing `DATA_PLANE_AUTH` ensures |
Consider also adding a quick unit test to pin the DATA_PLANE_AUTH=off opt-out path:
def test_data_plane_auth_off_attaches_no_credential(
monkeypatch: pytest.MonkeyPatch,
) -> None:
monkeypatch.setenv("DATA_PLANE_AUTH", "off")
headers: dict[str, str] = {}
gcp_auth.attach_auth(headers, "https://api.datacommons.org/mcp")
assert headers == {}| name: python:3.14-slim | ||
| entrypoint: bash | ||
| args: | ||
| - '-c' | ||
| - | | ||
| set -e | ||
| cd agent | ||
| pip install --quiet -r requirements.txt | ||
| python tests/test_mcp_session.py | ||
| python tests/test_backend_generations.py | ||
| python tests/test_auth_selection.py | ||
| python tests/test_prompt_rendering.py | ||
| pip install --quiet uv==0.10.2 |
There was a problem hiding this comment.
Optional nit: Using the official pre-baked uv + Python 3.14 image avoids running pip install --quiet uv==0.10.2 on every Cloud Build validation run:
| name: python:3.14-slim | |
| entrypoint: bash | |
| args: | |
| - '-c' | |
| - | | |
| set -e | |
| cd agent | |
| pip install --quiet -r requirements.txt | |
| python tests/test_mcp_session.py | |
| python tests/test_backend_generations.py | |
| python tests/test_auth_selection.py | |
| python tests/test_prompt_rendering.py | |
| pip install --quiet uv==0.10.2 | |
| name: ghcr.io/astral-sh/uv:0.10.2-python3.14-bookworm-slim | |
| entrypoint: bash | |
| args: | |
| - '-c' | |
| - | | |
| set -e | |
| cd agent |
Overview
The Narratives agent had only an unpinned
requirements.txt(no lockfile), no configured formatter, linter, or type checker, and contained standalone test scripts but no collectable test suite. This PR replaces the Python toolchain withuv,ruff,mypy, andpytestwhile leaving the application's runtime behavior untouched.The quality tooling closes the gaps on the Python side of the application:
ruff(80 columns, formatting + linting) replaces.pylintrcand.style.yapf.mypy --strictbrings static type checking to the Python side alongside the UI's existingtsc --noEmitgate.pytestreplaces the five standalone scripts inagent/tests/with six colocated*_test.pymodules underagent/src/narratives_agent/.Approach taken on
mypy: The backend architecture is being migrated and most existing modules are rewritten, deleted, or updated in the changes that follow this one. These modules are temporarily exempt from strict typing (to prevent duplicate work).Package rename: The agent's Python package was previously named
src, soimport src.configresolved only when the working directory happened to beagent/. Renaming the package tonarratives_agent(agent/src/narratives_agent/) makes it installable and importable from any working directory.Related Issues
None. Implements stage 1 (toolchain baseline) leading up to the FastAPI migration. See follow-ups.
Changes Made
agent/pyproject.toml,agent/uv.lock,agent/Dockerfile): Replacesrequirements.txtwithpyproject.tomlanduv.lock(pinned to public PyPI), upgrades the container base image topython:3.14-slimwithuv sync --frozen, and adds.dockerignore/.gcloudignoreto keep.venvand local artifacts out of build uploads.agent/src/narratives_agent/): Movesagent/src/*into an installablenarratives_agentpackage, updates imports and root path resolution, and renamesget_current_datetime_isttoget_current_datetime.agent/pyproject.toml): Configuresruff(80 columns) andmypy --strict(with temporary overrides for legacy modules), formats the package, fixes remaining lint findings, and deletes.pylintrcand.style.yapf.agent/src/narratives_agent/**/*_test.py): Migrates the five standalone test scripts inagent/tests/to 44pytestcases across six colocated*_test.pyfiles usingmonkeypatchfixtures.cloudbuild/pr-validate.yaml,deploy.sh,AGENTS.md,CODING_GUIDELINES.md): Updatespr-validate.yamlanddeploy.shpreflight checks for Python 3.14 anduv, and documents the toolchain andmypystandards inAGENTS.mdandCODING_GUIDELINES.md.Testing Done
From
narratives/agent/:uv sync --frozenuv run ruff format --check .(19 files formatted)uv run ruff check .(All checks passed)uv run mypy(Success: no issues found in 8 source files)uv run pytest(44 passed)From
narratives/ui/:npx tsc --noEmitnpx vitest run(4 test files, 29 tests passed)Verification:
Risk & Rollback
None — runtime behavior is unchanged. Rollback is a clean revert of the PR commits and redeploying the previous agent image tag.
Follow-ups
The
mypyexemptions inagent/pyproject.tomlare retired in the subsequent PRs:mypyoverride entries.Checklist
AGENTS.mdand followedCODING_GUIDELINES.md, plusFRONTEND.mdfor UI changes.Note: Only Maintainers can approve and merge PRs. Expected initial review time: 3 business days.