ape-linux 0.5.2__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ape_linux-0.5.2 → ape_linux-0.5.4}/CLAUDE.md +18 -1
- {ape_linux-0.5.2 → ape_linux-0.5.4}/PKG-INFO +1 -1
- {ape_linux-0.5.2 → ape_linux-0.5.4}/ape_linux.py +13 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/pyproject.toml +1 -1
- {ape_linux-0.5.2 → ape_linux-0.5.4}/tests/test_app.py +15 -0
- ape_linux-0.5.4/uv.lock +2279 -0
- ape_linux-0.5.2/uv.lock +0 -1851
- {ape_linux-0.5.2 → ape_linux-0.5.4}/.github/workflows/publish.yaml +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/.github/workflows/tests.yaml +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/.gitignore +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/.python-version +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/LICENSE +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/README.md +0 -0
- {ape_linux-0.5.2 → ape_linux-0.5.4}/justfile +0 -0
|
@@ -110,7 +110,19 @@ The flow in `ape_linux.py` is intentionally minimal:
|
|
|
110
110
|
yields `None` so **no** `model_settings` are sent — deliberately, because a
|
|
111
111
|
hard-coded temperature crashes models that reject sampling settings (some reasoning
|
|
112
112
|
models, Claude Opus 4.7/4.8) — and an unparseable value exits `1` before any LLM
|
|
113
|
-
call.
|
|
113
|
+
call. `call_llm()` also sets `pydantic_ai.BANNER_ENABLED = False` before building
|
|
114
|
+
the agent: recent Pydantic AI versions print a first-run banner (logo plus an
|
|
115
|
+
"observability: off" block) to **stderr** on the first agent run in a terminal,
|
|
116
|
+
which would sit next to Ape's one-line command output. That attribute is Pydantic
|
|
117
|
+
AI's documented switch for it. This is the **one** `ty: ignore` in the codebase
|
|
118
|
+
(`invalid-assignment`), and it is deliberate: Pydantic AI declares the flag as a
|
|
119
|
+
bare `BANNER_ENABLED = True` with no annotation, so ty infers `Literal[True]` and
|
|
120
|
+
rejects assigning `False` — the very thing the attribute exists for. No
|
|
121
|
+
`pydantic-ai-slim` floor bump is needed: the assignment is a harmless no-op on
|
|
122
|
+
versions that predate the banner. Note the banner is invisible to the test suite
|
|
123
|
+
regardless — Pydantic AI suppresses it whenever `PYTEST_VERSION`, `CI` or
|
|
124
|
+
`PYDANTIC_AI_NO_BANNER` is in the environment — so a regression here would only
|
|
125
|
+
show up when a user runs `ape` in a terminal.
|
|
114
126
|
4. Errors are flattened to one-line stderr messages, raising `SystemExit(1)` —
|
|
115
127
|
`ModelHTTPError` reports status/message; any other exception (bad credentials,
|
|
116
128
|
unknown provider) prints `str(error)`. There is no CLI framework swallowing
|
|
@@ -133,6 +145,11 @@ stderr, exit `2`). The `mockenv` fixture sets a dummy `APE_API_KEY` and `APE_MOD
|
|
|
133
145
|
blank/whitespace** `APE_MODEL` or `APE_API_KEY` is each checked to exit `1` before
|
|
134
146
|
`call_llm` is ever reached.
|
|
135
147
|
|
|
148
|
+
`test_call_llm_disables_the_pydantic_ai_banner` runs the real `call_llm` (with
|
|
149
|
+
`TestModel` swapped in for `infer_model`) and asserts the banner flag ends up `False`.
|
|
150
|
+
It sets the flag back to `True` via `monkeypatch.setattr(..., raising=False)` first, so
|
|
151
|
+
the check is meaningful and monkeypatch restores the global afterwards.
|
|
152
|
+
|
|
136
153
|
`build_provider()` is covered directly (not through `main()`): tests construct the
|
|
137
154
|
`openai` and `anthropic` providers with the standard credential vars deleted and assert
|
|
138
155
|
the injected key lands on `provider.client.api_key` (proving the standard var is neither
|
|
@@ -7,6 +7,7 @@ import sys
|
|
|
7
7
|
from functools import partial
|
|
8
8
|
from typing import Any, Protocol, cast
|
|
9
9
|
|
|
10
|
+
import pydantic_ai
|
|
10
11
|
from pydantic import BaseModel
|
|
11
12
|
from pydantic_ai import Agent, ModelSettings
|
|
12
13
|
from pydantic_ai.exceptions import ModelHTTPError
|
|
@@ -91,6 +92,18 @@ def call_llm(
|
|
|
91
92
|
user_prompt: str,
|
|
92
93
|
model_settings: ModelSettings | None,
|
|
93
94
|
) -> Command | CannotHelp:
|
|
95
|
+
# Pydantic AI shows a first-run banner (its logo plus an "observability: off" block)
|
|
96
|
+
# on stderr the first time an agent runs in a terminal. Ape prints a single command
|
|
97
|
+
# meant to be read or piped, so it owns its own output: turn the banner off before
|
|
98
|
+
# any run. This is Pydantic AI's documented switch for exactly that. Pydantic AI
|
|
99
|
+
# declares it as a bare `BANNER_ENABLED = True` with no annotation, so ty infers
|
|
100
|
+
# the narrowest type, `Literal[True]`, and reads any assignment of `False` as a
|
|
101
|
+
# type error; the assignment is precisely what the attribute is documented for, so
|
|
102
|
+
# the diagnostic is suppressed rather than worked around. Assigning it is also a
|
|
103
|
+
# harmless no-op on versions that predate the banner, so the pydantic-ai-slim floor
|
|
104
|
+
# stays where it is.
|
|
105
|
+
pydantic_ai.BANNER_ENABLED = False # ty: ignore[invalid-assignment]
|
|
106
|
+
|
|
94
107
|
# Two spellings here are for the type checker, and neither changes behavior: the
|
|
95
108
|
# generic parameters are explicit (`object` is the default deps type, as Ape uses
|
|
96
109
|
# no deps), and `output_type` uses Pydantic AI's sequence form instead of
|
|
@@ -3,6 +3,7 @@ import os
|
|
|
3
3
|
import platform
|
|
4
4
|
import socket
|
|
5
5
|
|
|
6
|
+
import pydantic_ai
|
|
6
7
|
import pytest
|
|
7
8
|
from pydantic_ai.exceptions import ModelHTTPError
|
|
8
9
|
from pydantic_ai.models.test import TestModel
|
|
@@ -258,6 +259,20 @@ def test_call_llm_returns_structured_output(monkeypatch):
|
|
|
258
259
|
assert isinstance(result.command, str)
|
|
259
260
|
|
|
260
261
|
|
|
262
|
+
def test_call_llm_disables_the_pydantic_ai_banner(monkeypatch):
|
|
263
|
+
# Pydantic AI shows a first-run banner on stderr, which would land next to ape's
|
|
264
|
+
# one-line output. call_llm turns it off before the agent runs. Setting the flag
|
|
265
|
+
# back to True first makes the check meaningful, and hands monkeypatch the global
|
|
266
|
+
# to restore afterwards; raising=False because versions predating the banner have
|
|
267
|
+
# no such attribute.
|
|
268
|
+
monkeypatch.setattr(pydantic_ai, "BANNER_ENABLED", True, raising=False)
|
|
269
|
+
monkeypatch.setattr(
|
|
270
|
+
"ape_linux.infer_model", lambda model, provider_factory: TestModel()
|
|
271
|
+
)
|
|
272
|
+
ape_linux.call_llm("openai:gpt-4.1", "key", "system", "user", None)
|
|
273
|
+
assert pydantic_ai.BANNER_ENABLED is False
|
|
274
|
+
|
|
275
|
+
|
|
261
276
|
def test_build_provider_injects_key_without_standard_env_var(monkeypatch):
|
|
262
277
|
# build_provider constructs the inferred provider with our key injected directly,
|
|
263
278
|
# so the provider's standard credential variable is neither needed nor read.
|