Skip to content

Commit fa2e8c2

Browse files
committed
fix(ci,review): format tests; estimate interrupted-stream usage; dedupe extras pins
- ruff format tests/ (CI checks src/ + tests/) - _synthesize_stream_result: estimate output tokens/cost from streamed text so an interrupted streaming generation stays visible to budget tracking (Greptile P2) - pyproject extras: unversioned aliases instead of duplicating the core version pins, removing drift risk; dependencies stays authoritative (Greptile P2)
1 parent 2c9162f commit fa2e8c2

6 files changed

Lines changed: 29 additions & 31 deletions

File tree

pyproject.toml

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,14 @@ voice = [
6363
"hive-agent[audio,hotkeys]",
6464
]
6565
# Provider/CLI extras. These SDKs ship in core `dependencies` today (so existing
66-
# installs are unchanged), but the extras let you pin/opt into individual pieces
67-
# and pave the way for a slimmer core later. Missing ones raise a clear
68-
# MissingDependencyError pointing at the matching extra.
69-
anthropic = [
70-
"anthropic>=0.40,<1",
71-
]
72-
openai = [
73-
"openai>=1.30,<3",
74-
]
75-
mcp = [
76-
"mcp>=1.27,<2",
77-
]
78-
cli = [
79-
"typer>=0.12",
80-
]
66+
# installs are unchanged) -- the extras are unversioned aliases that name the
67+
# grouping without duplicating (and risking drift from) the version pins in
68+
# `dependencies`, which stays authoritative. They pave the way for a slimmer core
69+
# later; a missing one raises a clear MissingDependencyError naming the extra.
70+
anthropic = ["anthropic"]
71+
openai = ["openai"]
72+
mcp = ["mcp"]
73+
cli = ["typer"]
8174

8275
[project.scripts]
8376
hive = "hive.cli.main:app"

src/hive/runtime/agent.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from hive.logging.models import DecisionLog, ToolLog
1414
from hive.models.base import BaseProvider
15+
from hive.models.registry import estimate_cost
1516
from hive.runtime.instructions import InstructionLike, Instructions
1617
from hive.runtime.memory import ConversationMemory, PersistentMemory
1718
from hive.runtime.persona import Persona
@@ -408,11 +409,20 @@ async def _generate_message(
408409
def _synthesize_stream_result(self, text: str) -> GenerateResult:
409410
"""Build a GenerateResult from text already streamed to ``on_text``.
410411
411-
Used when a stream is interrupted after emitting text but before a DONE
412-
event -- the partial text can't be replayed, so we return it as the turn's
413-
result (no tool calls; usage/cost unknown).
412+
Used when a stream is interrupted after emitting text but before the DONE
413+
event that carries usage. The partial text can't be replayed, so we return
414+
it as the turn's result (no tool calls). Real usage is unknown, so output
415+
tokens and cost are *estimated* from the streamed text (~4 chars/token) --
416+
otherwise the generation would be invisible to budget tracking and a
417+
near-limit agent could overshoot by a whole interrupted generation.
414418
"""
415-
return GenerateResult(message=Message.assistant(text), model=self._model.model)
419+
output_est = max(1, len(text) // 4) if text else 0
420+
return GenerateResult(
421+
message=Message.assistant(text),
422+
model=self._model.model,
423+
output_tokens=output_est,
424+
cost_usd=estimate_cost(self._model.model, 0, output_est),
425+
)
416426

417427
async def run(self, task: Task) -> TaskResult:
418428
"""Execute a task using the ReAct loop."""

tests/models/test_providers.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,7 @@ def test_top_level_import_needs_no_provider_sdk(self) -> None:
289289
mod = importlib.import_module("hive")
290290
assert hasattr(mod, "Agent")
291291

292-
def test_missing_openai_raises_with_extras_hint(
293-
self, monkeypatch: pytest.MonkeyPatch
294-
) -> None:
292+
def test_missing_openai_raises_with_extras_hint(self, monkeypatch: pytest.MonkeyPatch) -> None:
295293
monkeypatch.setitem(sys.modules, "openai", None)
296294
with pytest.raises(MissingDependencyError, match=r"hive-agent\[openai\]"):
297295
with _patch_env():

tests/runtime/test_structured.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,8 +144,7 @@ def test_string_field_contains_braces(self) -> None:
144144

145145
def test_nested_object(self) -> None:
146146
content = (
147-
'prefix {"title": "T", "author": '
148-
'{"name": "Ann", "age": 9, "active": true}} suffix'
147+
'prefix {"title": "T", "author": {"name": "Ann", "age": 9, "active": true}} suffix'
149148
)
150149
result = parse_structured_response(content, NestedModel)
151150
assert result.author.name == "Ann"

tests/test_auto_resume.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,9 +243,7 @@ async def test_corrupt_suffering_snapshot_falls_back_to_fresh(
243243
await _seed_agent(store, "agent-corrupt")
244244

245245
suffering = SufferingState(agent_id="agent-corrupt")
246-
suffering.add_stressor(
247-
StressorType.FUTILITY, "stuck", "finish", initial_severity=0.5
248-
)
246+
suffering.add_stressor(StressorType.FUTILITY, "stuck", "finish", initial_severity=0.5)
249247
cp_mgr = CheckpointManager(hive_dir)
250248
ctx = ExecutionContext(
251249
store=store,

uv.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)