Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"""Tests for MoA aggregator-slot exposure used by session cost accounting.
|
||||
|
||||
Regression guard for the ~50% MoA cost undercount: on the MoA path the
|
||||
agent's model/provider are the virtual preset name (e.g. "closed") and "moa",
|
||||
which have no pricing entry. Session cost accounting must price the
|
||||
aggregator's acting turn at its REAL model/provider, read from the MoA
|
||||
client's ``last_aggregator_slot``. Before the fix that slot did not exist and
|
||||
the aggregator's spend (often >50% of the turn) was silently dropped, leaving
|
||||
the session cost as advisor-fan-out only.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def _response(content="ok"):
|
||||
message = SimpleNamespace(content=content, tool_calls=[])
|
||||
choice = SimpleNamespace(message=message, finish_reason="stop")
|
||||
return SimpleNamespace(choices=[choice], usage=None, model="fake")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def moa_config(tmp_path, monkeypatch):
|
||||
home = tmp_path / ".hermes"
|
||||
home.mkdir()
|
||||
(home / "config.yaml").write_text(
|
||||
"""
|
||||
moa:
|
||||
default_preset: closed
|
||||
presets:
|
||||
closed:
|
||||
enabled: true
|
||||
reference_models:
|
||||
- provider: openrouter
|
||||
model: anthropic/claude-opus-4.8
|
||||
- provider: openrouter
|
||||
model: openai/gpt-5.5
|
||||
aggregator:
|
||||
provider: openrouter
|
||||
model: anthropic/claude-opus-4.8
|
||||
""".strip(),
|
||||
encoding="utf-8",
|
||||
)
|
||||
monkeypatch.setenv("HERMES_HOME", str(home))
|
||||
return home
|
||||
|
||||
|
||||
def test_create_populates_last_aggregator_slot(moa_config, monkeypatch):
|
||||
"""After a create() turn, last_aggregator_slot carries the REAL aggregator
|
||||
model/provider — not the virtual preset name."""
|
||||
from agent.moa_loop import MoAChatCompletions
|
||||
|
||||
def fake_call_llm(**kwargs):
|
||||
return _response("acted" if kwargs.get("task") != "moa_reference" else "advice")
|
||||
|
||||
monkeypatch.setattr("agent.moa_loop.call_llm", fake_call_llm)
|
||||
|
||||
facade = MoAChatCompletions("closed")
|
||||
# Slot is unset before any turn runs.
|
||||
assert facade.last_aggregator_slot is None
|
||||
|
||||
facade.create(
|
||||
model="closed",
|
||||
messages=[{"role": "user", "content": "clean the db"}],
|
||||
)
|
||||
|
||||
slot = facade.last_aggregator_slot
|
||||
assert slot is not None
|
||||
# The virtual preset name / "moa" must NOT leak into the priced slot.
|
||||
assert slot["model"] == "anthropic/claude-opus-4.8"
|
||||
assert slot["provider"] == "openrouter"
|
||||
assert slot["model"] != "closed"
|
||||
|
||||
|
||||
def test_client_exposes_last_aggregator_slot(moa_config, monkeypatch):
|
||||
"""MoAClient delegates last_aggregator_slot to its completions facade so
|
||||
session accounting can read it without touching internals."""
|
||||
from agent.moa_loop import MoAClient
|
||||
|
||||
def fake_call_llm(**kwargs):
|
||||
return _response("acted" if kwargs.get("task") != "moa_reference" else "advice")
|
||||
|
||||
monkeypatch.setattr("agent.moa_loop.call_llm", fake_call_llm)
|
||||
|
||||
client = MoAClient("closed")
|
||||
assert client.last_aggregator_slot is None
|
||||
|
||||
client.chat.completions.create(
|
||||
model="closed",
|
||||
messages=[{"role": "user", "content": "clean the db"}],
|
||||
)
|
||||
|
||||
slot = client.last_aggregator_slot
|
||||
assert slot is not None
|
||||
assert slot["model"] == "anthropic/claude-opus-4.8"
|
||||
assert slot["provider"] == "openrouter"
|
||||
Reference in New Issue
Block a user