Bump genai-prices to 0.1.10 and keep tests passing on rich 15, dbos 3 and e2b 2.55 (#10077)

Co-authored-by: Aditya Vardhan <[email protected]>
This commit is contained in:
David SF
2026-10-10 09:18:50 -04:00
committed by GitHub
co-authored by Aditya Vardhan
parent 38bf50ac55
commit 69ea1e5720
12 changed files with 45 additions and 31 deletions
+4 -4
View File
@@ -510,15 +510,15 @@ client = [
[[package]]
name = "genai-prices"
version = "0.1.9"
version = "0.1.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx2" },
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/f0/4b4c7f63de6d558714d2207808080dd0988d0317a54ebcde790e4f6c1b6e/genai_prices-0.1.9.tar.gz", hash = "sha256:20ec774d32ec5d345510877fac415f2560bbfeb081bc4cd4e7ebce57a5503e8d", size = 118447, upload-time = "2026-09-25T03:47:27.569Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/0b/159528e8580f9a5c985be78fcbea6230a1bcc1d46a9f8b681511d2c37e3f/genai_prices-0.1.10.tar.gz", hash = "sha256:889ba486497f8a85e9dd0e9228a27f2e135e31a850e96924b880f22c132092ac", size = 125604, upload-time = "2026-10-09T17:58:15.876Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/e0/57d07f653813b7ea9e3a82064a2b6a112f63004530ac665e0934829473c7/genai_prices-0.1.9-py3-none-any.whl", hash = "sha256:3da8faa0a126e4df7be36696b90abe2be1fea8826bfcf45d39d91bc397fa9d1d", size = 125549, upload-time = "2026-09-25T03:47:26.483Z" },
{ url = "https://files.pythonhosted.org/packages/11/0c/379fe71fccf01d829f1d72f805189453f12ba4b96e4b49a5cde8e1afd5ba/genai_prices-0.1.10-py3-none-any.whl", hash = "sha256:1f0eb9ff0b02f3a40b3aa590098fbc3bfc9e08ef124fa0bd132e9d899a6fec88", size = 132788, upload-time = "2026-10-09T17:58:14.463Z" },
]
[[package]]
@@ -1229,7 +1229,7 @@ requires-dist = [
{ name = "fastmcp-slim", extras = ["client"], marker = "extra == 'mcp'", specifier = ">=3.3.0,<5" },
{ name = "fastmcp-slim", extras = ["client"], marker = "extra == 'mcp-tasks'", specifier = ">=4.0.0b1,<5" },
{ name = "fastmcp-tasks", marker = "extra == 'mcp-tasks'", specifier = ">=4.0.0b1,<5" },
{ name = "genai-prices", specifier = ">=0.1.9" },
{ name = "genai-prices", specifier = ">=0.1.10" },
{ name = "google-genai", marker = "extra == 'google'", specifier = ">=2.25.0" },
{ name = "google-genai", marker = "extra == 'google-realtime'", specifier = ">=2.25.0" },
{ name = "griffelib", specifier = ">=2.0" },
+1 -1
View File
@@ -122,7 +122,7 @@ _(This example is complete, it can be run "as is")_
With Anthropic, the number of searches is reported as `web_search_requests` in [`RequestUsage.details`][pydantic_ai.usage.RequestUsage.details] and included in [`RunUsage.cost`][pydantic_ai.usage.RunUsage.cost].
With Google, the number of Google Search grounding queries is reported as `web_search_requests` in [`RequestUsage.details`][pydantic_ai.usage.RequestUsage.details], and the billed count as `web_searches` on [`RequestUsage`][pydantic_ai.usage.RequestUsage]: one per unique query on Gemini 3, and one per grounded request that returned a web source on Gemini 2.5 and older. Unlike Anthropic and OpenAI, this usage is not yet included in [`RunUsage.cost`][pydantic_ai.usage.RunUsage.cost].
With Google, the number of Google Search grounding queries is reported as `web_search_requests` in [`RequestUsage.details`][pydantic_ai.usage.RequestUsage.details], and the billed count as `web_searches` on [`RequestUsage`][pydantic_ai.usage.RequestUsage]: one per unique query on Gemini 3, and one per grounded request that returned a web source on Gemini 2.5 and older. The billed count is included in [`RunUsage.cost`][pydantic_ai.usage.RunUsage.cost], without deducting Google's free grounding allowance.
With OpenAI, you must use their Responses API to access the web search tool. The number of searches is reported as `web_search_requests` in [`RequestUsage.details`][pydantic_ai.usage.RequestUsage.details] and included in [`RunUsage.cost`][pydantic_ai.usage.RunUsage.cost]. Pages the model opens or searches within don't count as searches.
+1 -1
View File
@@ -65,7 +65,7 @@ dependencies = [
"pydantic-graph=={{ version }}",
"opentelemetry-api>=1.28.0",
"typing-inspection>=0.4.0",
"genai-prices>=0.1.9",
"genai-prices>=0.1.10",
]
[tool.hatch.metadata.hooks.uv-dynamic-versioning.optional-dependencies]
+3 -1
View File
@@ -198,7 +198,9 @@ async def test_close_prints_the_session_into_the_main_screen_once() -> None:
screen.surface.restore()
assert not screen.terminal.alternate
assert screen.lines()[:3] == ['$ clai2', 'banner', 'linked answer']
printed = Text.from_ansi(screen.terminal.getvalue().rsplit(LEAVE, 1)[1])
# The surface prints CRLF rows, and Rich 15.0.0's `from_ansi` blanks each one:
# https://github.com/Textualize/rich/issues/4090
printed = Text.from_ansi(screen.terminal.getvalue().rsplit(LEAVE, 1)[1].replace('\r\n', '\n'))
assert printed.get_style_at_offset(Console(), 0).link == 'https://example.com', 'scrollback keeps links'
screen.surface.restore()
assert screen.lines()[:4] == ['$ clai2', 'banner', 'linked answer', '']
+5 -2
View File
@@ -276,7 +276,8 @@ def test_printed_skips_output_already_in_scrollback_and_cleared_output() -> None
block.write('answer\n')
buffer.clear()
buffer.write('after clear\n')
assert Text.from_ansi(buffer.printed(width=40)).plain == 'after clear'
# Rich 15.0.0's `from_ansi` keeps a trailing newline (https://github.com/Textualize/rich/pull/4076).
assert [Text.from_ansi(line).plain for line in buffer.printed(width=40).splitlines()] == ['after clear']
assert buffer.printed(width=40) == ''
block.freeze()
assert plain(buffer) == ['after clear', '']
@@ -345,7 +346,9 @@ def test_pre_live_transcript_migrates_in_place_preserving_styles_and_partial_ans
transcript.write('m green\x1b[0m\n')
assert plain(transcript) == ['old output', 'pending green', '']
assert '\x1b[32m' in transcript.frame(width=80, height=24).rows[1]
assert Text.from_ansi(transcript.printed(width=80)).plain == 'pending green', 'old output was already emitted'
# Rich 15.0.0's `from_ansi` keeps a trailing newline (https://github.com/Textualize/rich/pull/4076).
printed = [Text.from_ansi(line).plain for line in transcript.printed(width=80).splitlines()]
assert printed == ['pending green'], 'old output was already emitted'
output = io.StringIO()
console = Console(file=output)
with transcript.capture(console):
+3 -1
View File
@@ -350,7 +350,9 @@ def test_set_menu_previews_each_style_in_the_choice_picker(tmp_path: Path, monke
monkeypatch.setattr('pydantic_clai2.ui.menus.field_menu.menu_key', lambda: next(keys))
result = menu.build_choices(row).run()
assert result.item is not None and result.item.value == 'grouped'
plain = Text.from_ansi(output.getvalue()).plain
# The menu paints CRLF rows, and Rich 15.0.0's `from_ansi` blanks each one:
# https://github.com/Textualize/rich/issues/4090
plain = Text.from_ansi(output.getvalue().replace('\r\n', '\n')).plain
assert '● shell git status' in plain
assert '● shell 3, read_file 2, shell 1' in plain
# The typed-value row has no sample to preview.
+5 -4
View File
@@ -4887,8 +4887,9 @@ async def test_dbos_default_ids_survive_recovery(dbos: DBOS) -> None:
assert [run_id for run_id, _ in seen] == [f'{workflow_id}:1', f'{workflow_id}:3', f'{workflow_id}:3:1']
assert len({*seen[0], *seen[1], *seen[2]}) == 6
# Recovery re-executes the workflow function: model requests replay from their recorded steps, while
# the function tool, which runs in the workflow, runs again and sees the same IDs.
handle = await asyncio.to_thread(DBOS._execute_workflow_id, workflow_id) # pyright: ignore[reportPrivateUsage]
assert sorted(tuple(ids) for ids in await asyncio.to_thread(handle.get_result)) == seen
# Resuming the pending workflow re-executes it under the same ID, the way recovery does: model requests
# replay from their recorded steps, while the function tool, which runs in the workflow, runs again and
# sees the same IDs.
handle = await DBOS.resume_workflow_async(workflow_id)
assert sorted(tuple(ids) for ids in await handle.get_result()) == seen
assert sorted(stable_ids_seen[3:]) == seen
+9 -3
View File
@@ -32,6 +32,7 @@ from typing import IO, TYPE_CHECKING, Literal, Protocol
import anyio
import anyio.lowlevel
import e2b
from e2b import CommandExitException, CommandResult, FileType, WriteInfo
from e2b.exceptions import (
AuthenticationException,
@@ -41,9 +42,13 @@ from e2b.exceptions import (
SandboxException,
SandboxNotFoundException,
TimeoutException,
format_sandbox_timeout_exception,
)
# What the SDK raises for an envd call on a gone sandbox. Looked up by its public name rather than
# through the SDK's formatting helper, which e2b renamed in 2.53.1: from then on it is the exported
# `SandboxNotRunningException` (e2b-dev/E2B#1949), before it `TimeoutException` itself.
_SandboxGoneException: type[TimeoutException] = getattr(e2b, 'SandboxNotRunningException', TimeoutException)
__all__ = (
'FakeCommandCall',
'FakeCreateCall',
@@ -651,10 +656,11 @@ class FakeSandbox:
E2B's proxy answers a request for a killed sandbox with a 502, which the SDK raises as
a `TimeoutException` blaming the sandbox timeout -- the same type it uses for a slow
request, so only the health probe tells the two apart.
request, so only the health probe tells the two apart. From e2b 2.53.1 it is the
`SandboxNotRunningException` subclass, which the backend still probes as a timeout.
"""
if self.killed:
raise format_sandbox_timeout_exception('The sandbox was not found')
raise _SandboxGoneException('The sandbox was not found: it was killed or reached its timeout.')
async def is_running(self, request_timeout: float | None = None) -> bool:
del request_timeout
+1 -1
View File
@@ -612,7 +612,7 @@ async def test_google_image_generation_with_web_search(allow_model_requests: Non
},
output_reasoning_tokens=529,
web_searches=1,
cost=Decimal('0.148734'),
cost=Decimal('0.162734'),
),
model_name='gemini-3-pro-image-preview',
timestamp=IsDatetime(),
@@ -381,7 +381,7 @@ async def test_native_output_with_builtin_tools_stream(allow_model_requests: Non
details={'thoughts_tokens': 78, 'text_prompt_tokens': 87, 'web_search_requests': 1},
output_reasoning_tokens=78,
web_searches=1,
cost=Decimal('0.0002775'),
cost=Decimal('0.0142775'),
),
model_name='gemini-3-flash-preview',
timestamp=IsDatetime(),
@@ -640,7 +640,7 @@ As for the weather in Tokyo, it is currently **cloudy** with a temperature of ap
details={'thoughts_tokens': 121, 'text_prompt_tokens': 132, 'web_search_requests': 1},
output_reasoning_tokens=121,
web_searches=1,
cost=Decimal('0.000615'),
cost=Decimal('0.014615'),
),
model_name='gemini-3-flash-preview',
timestamp=IsDatetime(),
@@ -790,7 +790,7 @@ async def test_native_output_with_function_and_builtin_tools(
output_reasoning_tokens=27,
input_tool_tokens=86,
web_searches=1,
cost=Decimal('0.000344'),
cost=Decimal('0.014344'),
),
model_name='gemini-3-flash-preview',
timestamp=IsDatetime(),
@@ -880,7 +880,7 @@ async def test_native_output_with_builtin_tools(allow_model_requests: None, goog
details={'thoughts_tokens': 71, 'text_prompt_tokens': 351, 'web_search_requests': 1},
output_reasoning_tokens=71,
web_searches=1,
cost=Decimal('0.0004215'),
cost=Decimal('0.0144215'),
),
model_name='gemini-3-flash-preview',
timestamp=IsDatetime(),
+5 -5
View File
@@ -1145,7 +1145,7 @@ Overall, today's weather in San Francisco is pleasant, with a mix of sun and clo
input_tool_tokens=119,
input_text_tool_tokens=119,
web_searches=1,
cost=Decimal('0.00431'),
cost=Decimal('0.03931'),
),
model_name='gemini-2.5-pro',
timestamp=IsDatetime(),
@@ -1234,7 +1234,7 @@ Tonight, the skies will remain cloudy with a continued chance of showers, and th
input_tool_tokens=286,
input_text_tool_tokens=286,
web_searches=1,
cost=Decimal('0.00398875'),
cost=Decimal('0.03898875'),
),
model_name='gemini-2.5-pro',
timestamp=IsDatetime(),
@@ -1390,7 +1390,7 @@ Hourly forecasts show temperatures remaining in the low 70s during the afternoon
input_tool_tokens=102,
input_text_tool_tokens=102,
web_searches=1,
cost=Decimal('0.00667875'),
cost=Decimal('0.04167875'),
),
model_name='gemini-2.5-pro',
timestamp=IsDatetime(),
@@ -1637,7 +1637,7 @@ There is a high chance of rain throughout the day, with some reports stating a 6
input_tool_tokens=319,
input_text_tool_tokens=319,
web_searches=1,
cost=Decimal('0.00612'),
cost=Decimal('0.04112'),
),
model_name='gemini-2.5-pro',
timestamp=IsDatetime(),
@@ -3728,7 +3728,7 @@ Based on your location in **San Francisco**, here is the weather forecast for to
output_tokens=706,
output_reasoning_tokens=456,
web_searches=1,
cost=Decimal('0.0021805'),
cost=Decimal('0.0161805'),
),
model_name='gemini-3-flash-preview',
timestamp=IsDatetime(),
Generated
+4 -4
View File
@@ -2346,15 +2346,15 @@ wheels = [
[[package]]
name = "genai-prices"
version = "0.1.9"
version = "0.1.10"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "httpx2" },
{ name = "pydantic" },
]
sdist = { url = "https://files.pythonhosted.org/packages/d6/f0/4b4c7f63de6d558714d2207808080dd0988d0317a54ebcde790e4f6c1b6e/genai_prices-0.1.9.tar.gz", hash = "sha256:20ec774d32ec5d345510877fac415f2560bbfeb081bc4cd4e7ebce57a5503e8d", size = 118447, upload-time = "2026-09-25T03:47:27.569Z" }
sdist = { url = "https://files.pythonhosted.org/packages/c4/0b/159528e8580f9a5c985be78fcbea6230a1bcc1d46a9f8b681511d2c37e3f/genai_prices-0.1.10.tar.gz", hash = "sha256:889ba486497f8a85e9dd0e9228a27f2e135e31a850e96924b880f22c132092ac", size = 125604, upload-time = "2026-10-09T17:58:15.876Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1b/e0/57d07f653813b7ea9e3a82064a2b6a112f63004530ac665e0934829473c7/genai_prices-0.1.9-py3-none-any.whl", hash = "sha256:3da8faa0a126e4df7be36696b90abe2be1fea8826bfcf45d39d91bc397fa9d1d", size = 125549, upload-time = "2026-09-25T03:47:26.483Z" },
{ url = "https://files.pythonhosted.org/packages/11/0c/379fe71fccf01d829f1d72f805189453f12ba4b96e4b49a5cde8e1afd5ba/genai_prices-0.1.10-py3-none-any.whl", hash = "sha256:1f0eb9ff0b02f3a40b3aa590098fbc3bfc9e08ef124fa0bd132e9d899a6fec88", size = 132788, upload-time = "2026-10-09T17:58:14.463Z" },
]
[[package]]
@@ -6545,7 +6545,7 @@ requires-dist = [
{ name = "fastmcp-slim", extras = ["client"], marker = "extra == 'mcp'", specifier = ">=3.3.0,<5" },
{ name = "fastmcp-slim", extras = ["client"], marker = "extra == 'mcp-tasks'", specifier = ">=4.0.0b1,<5" },
{ name = "fastmcp-tasks", marker = "extra == 'mcp-tasks'", specifier = ">=4.0.0b1,<5" },
{ name = "genai-prices", specifier = ">=0.1.9" },
{ name = "genai-prices", specifier = ">=0.1.10" },
{ name = "google-genai", marker = "extra == 'google'", specifier = ">=2.25.0" },
{ name = "google-genai", marker = "extra == 'google-realtime'", specifier = ">=2.25.0" },
{ name = "griffelib", specifier = ">=2.0" },