Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
"""Serializer-warning leak regression tests (#82xxx).
|
||||
|
||||
The Anthropic SDK's streaming accumulator builds ``ParsedMessage`` snapshots
|
||||
whose content blocks (``ParsedTextBlock``) don't match the generic union
|
||||
pydantic expects at serialization time. ``model_dump()`` on those objects
|
||||
emits ``PydanticSerializationUnexpectedValue`` UserWarnings that leak
|
||||
straight into the user's terminal mid-response.
|
||||
|
||||
Every Hermes serialization helper that dumps arbitrary SDK models must pass
|
||||
``warnings=False`` (with a TypeError fallback for duck-typed models). These
|
||||
tests pin that contract for all four helpers.
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
import pytest
|
||||
|
||||
anthropic = pytest.importorskip("anthropic")
|
||||
|
||||
from anthropic._models import build
|
||||
from anthropic.lib.streaming._messages import accumulate_event
|
||||
from anthropic.lib.streaming._types import ParsedMessageStopEvent
|
||||
from anthropic.types import (
|
||||
Message,
|
||||
RawContentBlockDeltaEvent,
|
||||
RawContentBlockStartEvent,
|
||||
RawMessageStartEvent,
|
||||
Usage,
|
||||
)
|
||||
|
||||
|
||||
def _accumulated_stop_event():
|
||||
"""Build a message_stop event exactly the way the SDK stream does."""
|
||||
start = RawMessageStartEvent(
|
||||
type="message_start",
|
||||
message=Message(
|
||||
id="msg_1",
|
||||
content=[],
|
||||
model="claude-x",
|
||||
role="assistant",
|
||||
stop_reason=None,
|
||||
stop_sequence=None,
|
||||
type="message",
|
||||
usage=Usage(input_tokens=1, output_tokens=0),
|
||||
),
|
||||
)
|
||||
cb_start = RawContentBlockStartEvent(
|
||||
type="content_block_start", index=0, content_block={"type": "text", "text": ""}
|
||||
)
|
||||
cb_delta = RawContentBlockDeltaEvent(
|
||||
type="content_block_delta",
|
||||
index=0,
|
||||
delta={"type": "text_delta", "text": "hello"},
|
||||
)
|
||||
snapshot = None
|
||||
for event in (start, cb_start, cb_delta):
|
||||
snapshot = accumulate_event(event=event, current_snapshot=snapshot)
|
||||
return build(ParsedMessageStopEvent, type="message_stop", message=snapshot), snapshot
|
||||
|
||||
|
||||
def _pydantic_warnings(recorded):
|
||||
return [w for w in recorded if "Pydantic serializer warnings" in str(w.message)]
|
||||
|
||||
|
||||
def test_stop_event_dump_actually_warns_without_suppression():
|
||||
"""Precondition: the fixture really trips the SDK/pydantic warning.
|
||||
|
||||
If a future SDK/pydantic release stops warning here, the other tests
|
||||
pass vacuously — this test tells us the guard can be simplified.
|
||||
"""
|
||||
stop_event, _ = _accumulated_stop_event()
|
||||
with warnings.catch_warnings(record=True) as recorded:
|
||||
warnings.simplefilter("always")
|
||||
stop_event.model_dump()
|
||||
assert _pydantic_warnings(recorded), (
|
||||
"fixture no longer reproduces the pydantic serializer warning; "
|
||||
"the warnings=False guards may be removable"
|
||||
)
|
||||
|
||||
|
||||
def test_relay_llm_jsonable_no_warning_leak():
|
||||
from agent.relay_llm import _jsonable
|
||||
|
||||
stop_event, _ = _accumulated_stop_event()
|
||||
with warnings.catch_warnings(record=True) as recorded:
|
||||
warnings.simplefilter("always")
|
||||
payload = _jsonable(stop_event)
|
||||
assert not _pydantic_warnings(recorded)
|
||||
assert payload["message"]["content"][0]["text"] == "hello"
|
||||
|
||||
|
||||
def test_relay_tools_jsonable_no_warning_leak():
|
||||
from agent.relay_tools import _jsonable
|
||||
|
||||
stop_event, _ = _accumulated_stop_event()
|
||||
with warnings.catch_warnings(record=True) as recorded:
|
||||
warnings.simplefilter("always")
|
||||
payload = _jsonable(stop_event)
|
||||
assert not _pydantic_warnings(recorded)
|
||||
assert payload["message"]["content"][0]["text"] == "hello"
|
||||
|
||||
|
||||
def test_anthropic_to_plain_data_no_warning_leak():
|
||||
from agent.anthropic_adapter import _to_plain_data
|
||||
|
||||
_, snapshot = _accumulated_stop_event()
|
||||
with warnings.catch_warnings(record=True) as recorded:
|
||||
warnings.simplefilter("always")
|
||||
payload = _to_plain_data(snapshot)
|
||||
assert not _pydantic_warnings(recorded)
|
||||
assert payload["content"][0]["text"] == "hello"
|
||||
|
||||
|
||||
def test_hook_jsonable_no_warning_leak():
|
||||
from run_agent import AIAgent
|
||||
|
||||
stop_event, _ = _accumulated_stop_event()
|
||||
with warnings.catch_warnings(record=True) as recorded:
|
||||
warnings.simplefilter("always")
|
||||
payload = AIAgent._hook_jsonable(stop_event)
|
||||
assert not _pydantic_warnings(recorded)
|
||||
assert payload["message"]["content"][0]["text"] == "hello"
|
||||
|
||||
|
||||
def test_duck_typed_model_dump_fallback():
|
||||
"""Non-pydantic objects with a bare model_dump() must still serialize."""
|
||||
from agent.relay_llm import _jsonable as rl_jsonable
|
||||
from agent.relay_tools import _jsonable as rt_jsonable
|
||||
from agent.anthropic_adapter import _to_plain_data
|
||||
|
||||
class Duck:
|
||||
def model_dump(self): # no mode/warnings kwargs
|
||||
return {"quack": True}
|
||||
|
||||
assert rl_jsonable(Duck()) == {"quack": True}
|
||||
assert rt_jsonable(Duck()) == {"quack": True}
|
||||
assert _to_plain_data(Duck()) == {"quack": True}
|
||||
Reference in New Issue
Block a user