Skip to content

Commit 1183392

Browse files
committed
livekit: release py 0.8.0, run tests in CI, drop redundant owner state
- release: bump pyproject + __version__ 0.7.0 -> 0.8.0 (new adapter adds src + packaging metadata, so version-guard requires a bump); move the changelog entry from Unreleased to a dated py 0.8.0 section. - ci: install the [livekit] extra in the test job so the adapter tests actually run instead of importorskip-ing. livekit-agents supports Python >=3.10, so it goes in the base install for every matrix cell (no conditional like pipecat). - livekit: drop _TurnSlot.owner and the parallel self._reservation_owner — the exception-path ownership check reads the same signal from self._active is slot (the slot is already in closure scope). Removes dead state; behavior unchanged (overlap + delayed-metrics tests still pass).
1 parent dfa78c6 commit 1183392

5 files changed

Lines changed: 13 additions & 14 deletions

File tree

.github/workflows/ci.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,10 @@ jobs:
6565
# (instead of skipping). langchain-core covers the LangChain handler
6666
# tests; litellm covers the callback-swallowing regression tests (the
6767
# CrewAI adapter tests stub crewai itself, so the heavy extra stays out);
68-
# langgraph covers the fan-out reserve/settle and advisory-channel tests.
69-
- run: pip install -e ".[dev,langchain,litellm,langgraph]"
68+
# langgraph covers the fan-out reserve/settle and advisory-channel tests;
69+
# livekit covers the LiveKit voice-agent reserve/settle/release tests
70+
# (livekit-agents supports Python >=3.10, so it goes in the base install).
71+
- run: pip install -e ".[dev,langchain,litellm,langgraph,livekit]"
7072
# pipecat-ai requires Python >=3.11, so add it (and exercise the Pipecat
7173
# adapter tests) on every matrix cell except 3.10 — there the tests
7274
# importorskip cleanly.

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ both packages adhere to [Semantic Versioning](https://semver.org/).
1010

1111
## Unreleased
1212

13+
## py 0.8.0 — 2026-07-23
14+
1315
### Added (py)
1416

1517
- **LiveKit Agents adapter** (`floe-guard[livekit]`, issue #39):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "floe-guard"
7-
version = "0.7.0"
7+
version = "0.8.0"
88
description = "Local budget guardrail for AI agents — hard-stops a runaway loop before its next LLM call crosses a spend ceiling. No account, no network."
99
readme = "README.md"
1010
requires-python = ">=3.10"

src/floe_guard/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from .pricing import ManualPrice, PricedModel, price_tokens, resolve_price
2929
from .stream import StreamGuard, guard_stream
3030

31-
__version__ = "0.7.0" # keep in lockstep with pyproject.toml
31+
__version__ = "0.8.0" # keep in lockstep with pyproject.toml
3232

3333
__all__ = [
3434
"BudgetGuard",

src/floe_guard/integrations/livekit.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class _TurnSlot:
5252
"""
5353

5454
amount: float
55-
owner: object
5655
open: bool = True
5756

5857

@@ -90,8 +89,6 @@ def __init__(
9089
self._tts_usd_per_1k_chars = tts_usd_per_1k_chars
9190
self._reserved: float = 0.0
9291
self._pending = False
93-
# Identity of the invocation that currently owns the open guard hold.
94-
self._reservation_owner: object | None = None
9592
self._active: _TurnSlot | None = None
9693
# FIFO of turns that may still emit LLMMetrics (including early-released).
9794
self._slots: deque[_TurnSlot] = deque()
@@ -109,7 +106,6 @@ async def _guarded_llm_node(*args, **kwargs):
109106
# slot so delayed metrics cannot steal this turn's hold.
110107
if self._pending:
111108
self._early_release_active()
112-
owner = object()
113109
try:
114110
amount = self._guard.reserve()
115111
except BudgetExceeded as exc:
@@ -119,10 +115,9 @@ async def _guarded_llm_node(*args, **kwargs):
119115
raise
120116
await self._on_budget_exceeded(exc)
121117
return
122-
slot = _TurnSlot(amount=amount, owner=owner)
118+
slot = _TurnSlot(amount=amount)
123119
self._slots.append(slot)
124120
self._active = slot
125-
self._reservation_owner = owner
126121
self._reserved = amount
127122
self._pending = True
128123
try:
@@ -132,9 +127,10 @@ async def _guarded_llm_node(*args, **kwargs):
132127
async for chunk in stream:
133128
yield chunk
134129
except BaseException:
135-
# Release only if this invocation still owns the open hold —
136-
# a later turn may already have early-released ours and reserved.
137-
if self._reservation_owner is owner:
130+
# Release only if this invocation still owns the open hold — a
131+
# later turn may already have early-released ours and reserved
132+
# its own, making its slot (not ours) the active one.
133+
if self._active is slot:
138134
self._early_release_active()
139135
raise
140136

@@ -144,7 +140,6 @@ async def _guarded_llm_node(*args, **kwargs):
144140

145141
def _clear_active(self) -> None:
146142
self._active = None
147-
self._reservation_owner = None
148143
self._reserved = 0.0
149144
self._pending = False
150145

0 commit comments

Comments
 (0)