chore(contextual): purge residual floating WsFrame defs + output_formatter branch
After M6.5 deletion of run_floating_stream and the frame dispatch, WsFrameType.floating_request/floating_domain, WsFloatingRequest, WsFloatingDomain, WsFloatingScope, WsDomain, and the StreamFormatter's floating_domain branch were left as dead protocol surface. Remove them, along with the corresponding test cases in test_schemas_v3.py and test_output_formatter.py.
This commit is contained in:
@@ -6,7 +6,7 @@ import re
|
|||||||
from collections.abc import AsyncGenerator
|
from collections.abc import AsyncGenerator
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from app.schemas import WsFloatingDomain, WsStreamEnd, WsStreamStart, WsStreamText
|
from app.schemas import WsStreamEnd, WsStreamStart, WsStreamText
|
||||||
|
|
||||||
# Matches <canvas kind="...">...</canvas> blocks (single-line or multiline).
|
# Matches <canvas kind="...">...</canvas> blocks (single-line or multiline).
|
||||||
_CANVAS_BLOCK_RE = re.compile(
|
_CANVAS_BLOCK_RE = re.compile(
|
||||||
@@ -31,7 +31,7 @@ def extract_canvas_block(text: str) -> tuple[str, str | None, str | None]:
|
|||||||
visible = visible.strip()
|
visible = visible.strip()
|
||||||
return visible, canvas_content, canvas_kind
|
return visible, canvas_content, canvas_kind
|
||||||
|
|
||||||
WsFrame = WsStreamStart | WsStreamText | WsStreamEnd | WsFloatingDomain
|
WsFrame = WsStreamStart | WsStreamText | WsStreamEnd
|
||||||
|
|
||||||
|
|
||||||
class StreamFormatter:
|
class StreamFormatter:
|
||||||
@@ -47,14 +47,6 @@ class StreamFormatter:
|
|||||||
started = False
|
started = False
|
||||||
|
|
||||||
async for event_type, data in event_stream:
|
async for event_type, data in event_stream:
|
||||||
if event_type == "floating_domain":
|
|
||||||
if isinstance(data, dict):
|
|
||||||
yield WsFloatingDomain(
|
|
||||||
request_id=self.request_id,
|
|
||||||
domain=data,
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if event_type != "token":
|
if event_type != "token":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|||||||
@@ -73,11 +73,9 @@ class WsFrameType(str, Enum):
|
|||||||
device_hello = "device_hello"
|
device_hello = "device_hello"
|
||||||
# ── v3 frame types ─────────────────────────────────────────────────
|
# ── v3 frame types ─────────────────────────────────────────────────
|
||||||
home_request = "home_request"
|
home_request = "home_request"
|
||||||
floating_request = "floating_request"
|
|
||||||
stream_start = "stream_start"
|
stream_start = "stream_start"
|
||||||
stream_text = "stream_text"
|
stream_text = "stream_text"
|
||||||
stream_end = "stream_end"
|
stream_end = "stream_end"
|
||||||
floating_domain = "floating_domain"
|
|
||||||
data_request = "data_request"
|
data_request = "data_request"
|
||||||
data_response = "data_response"
|
data_response = "data_response"
|
||||||
mutation = "mutation"
|
mutation = "mutation"
|
||||||
@@ -165,13 +163,6 @@ class FormatPrefsModel(BaseModel):
|
|||||||
now_iso: str = ""
|
now_iso: str = ""
|
||||||
|
|
||||||
|
|
||||||
class WsFloatingScope(BaseModel):
|
|
||||||
"""Scope for a floating request — narrows the agent to a specific entity."""
|
|
||||||
|
|
||||||
type: Literal["task", "project", "note", "timeline"]
|
|
||||||
id: str | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class WsHomeRequest(BaseModel):
|
class WsHomeRequest(BaseModel):
|
||||||
"""Client → Server: Home chat message."""
|
"""Client → Server: Home chat message."""
|
||||||
|
|
||||||
@@ -181,15 +172,6 @@ class WsHomeRequest(BaseModel):
|
|||||||
format_prefs: FormatPrefsModel | None = None
|
format_prefs: FormatPrefsModel | None = None
|
||||||
|
|
||||||
|
|
||||||
class WsFloatingRequest(BaseModel):
|
|
||||||
"""Client → Server: Floating chat message scoped to an entity."""
|
|
||||||
|
|
||||||
type: Literal[WsFrameType.floating_request] = WsFrameType.floating_request
|
|
||||||
message: str
|
|
||||||
scope: WsFloatingScope
|
|
||||||
format_prefs: FormatPrefsModel | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class WsBriefRequest(BaseModel):
|
class WsBriefRequest(BaseModel):
|
||||||
"""Client → Server: Request a plain-text brief (home or project)."""
|
"""Client → Server: Request a plain-text brief (home or project)."""
|
||||||
|
|
||||||
@@ -225,22 +207,6 @@ class WsStreamEnd(BaseModel):
|
|||||||
mutations: list[dict[str, Any]] | None = None
|
mutations: list[dict[str, Any]] | None = None
|
||||||
|
|
||||||
|
|
||||||
class WsDomain(BaseModel):
|
|
||||||
"""Structured floating domain payload for UI routing decisions."""
|
|
||||||
|
|
||||||
type: Literal["task", "timeline", "project", "node"]
|
|
||||||
id: str | None = None
|
|
||||||
section: Literal["task", "timeline", "note"] | None = None
|
|
||||||
|
|
||||||
|
|
||||||
class WsFloatingDomain(BaseModel):
|
|
||||||
"""Server → Client: domain determined for a floating request."""
|
|
||||||
|
|
||||||
type: Literal[WsFrameType.floating_domain] = WsFrameType.floating_domain
|
|
||||||
request_id: str
|
|
||||||
domain: WsDomain
|
|
||||||
|
|
||||||
|
|
||||||
# ── Agent Config V2 ───────────────────────────────────────────────────
|
# ── Agent Config V2 ───────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from app.core.output_formatter import StreamFormatter
|
from app.core.output_formatter import StreamFormatter
|
||||||
from app.schemas import WsFloatingDomain, WsStreamEnd, WsStreamStart, WsStreamText
|
from app.schemas import WsStreamEnd, WsStreamStart, WsStreamText
|
||||||
|
|
||||||
|
|
||||||
async def _stream(*events: tuple[str, object]):
|
async def _stream(*events: tuple[str, object]):
|
||||||
@@ -36,29 +36,6 @@ async def test_stream_formatter_text_stream() -> None:
|
|||||||
assert isinstance(frames[-1], WsStreamEnd)
|
assert isinstance(frames[-1], WsStreamEnd)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_stream_formatter_floating_domain_first() -> None:
|
|
||||||
formatter = StreamFormatter(request_id="req-2")
|
|
||||||
frames = await _collect(
|
|
||||||
formatter,
|
|
||||||
_stream(
|
|
||||||
(
|
|
||||||
"floating_domain",
|
|
||||||
{"type": "node", "id": "n-1", "section": None},
|
|
||||||
),
|
|
||||||
("token", "Summary"),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert isinstance(frames[0], WsFloatingDomain)
|
|
||||||
assert frames[0].domain.type == "node"
|
|
||||||
assert frames[0].domain.id == "n-1"
|
|
||||||
assert isinstance(frames[1], WsStreamStart)
|
|
||||||
assert isinstance(frames[2], WsStreamText)
|
|
||||||
assert frames[2].chunk == "Summary"
|
|
||||||
assert isinstance(frames[-1], WsStreamEnd)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_stream_formatter_ignores_unknown_events() -> None:
|
async def test_stream_formatter_ignores_unknown_events() -> None:
|
||||||
formatter = StreamFormatter(request_id="req-3")
|
formatter = StreamFormatter(request_id="req-3")
|
||||||
|
|||||||
@@ -4,12 +4,8 @@ import pytest
|
|||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
|
|
||||||
from app.schemas import (
|
from app.schemas import (
|
||||||
WsDomain,
|
|
||||||
WsFrameType,
|
WsFrameType,
|
||||||
WsHomeRequest,
|
WsHomeRequest,
|
||||||
WsFloatingDomain,
|
|
||||||
WsFloatingRequest,
|
|
||||||
WsFloatingScope,
|
|
||||||
WsStreamEnd,
|
WsStreamEnd,
|
||||||
WsStreamStart,
|
WsStreamStart,
|
||||||
WsStreamText,
|
WsStreamText,
|
||||||
@@ -22,11 +18,9 @@ from app.schemas import (
|
|||||||
def test_v3_frame_types_exist():
|
def test_v3_frame_types_exist():
|
||||||
v3_types = [
|
v3_types = [
|
||||||
"home_request",
|
"home_request",
|
||||||
"floating_request",
|
|
||||||
"stream_start",
|
"stream_start",
|
||||||
"stream_text",
|
"stream_text",
|
||||||
"stream_end",
|
"stream_end",
|
||||||
"floating_domain",
|
|
||||||
"data_request",
|
"data_request",
|
||||||
"data_response",
|
"data_response",
|
||||||
"mutation",
|
"mutation",
|
||||||
@@ -86,51 +80,6 @@ def test_home_request_requires_message():
|
|||||||
WsHomeRequest.model_validate({"type": "home_request"})
|
WsHomeRequest.model_validate({"type": "home_request"})
|
||||||
|
|
||||||
|
|
||||||
# ── WsFloatingRequest ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_request_basic():
|
|
||||||
frame = WsFloatingRequest(
|
|
||||||
message="Summarise",
|
|
||||||
scope=WsFloatingScope(type="task", id="task-123"),
|
|
||||||
)
|
|
||||||
assert frame.type == WsFrameType.floating_request
|
|
||||||
assert frame.scope.type == "task"
|
|
||||||
assert frame.scope.id == "task-123"
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_request_scope_without_id():
|
|
||||||
frame = WsFloatingRequest(
|
|
||||||
message="Show all",
|
|
||||||
scope=WsFloatingScope(type="project"),
|
|
||||||
)
|
|
||||||
assert frame.scope.id is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_request_serializes():
|
|
||||||
frame = WsFloatingRequest(
|
|
||||||
message="Test",
|
|
||||||
scope=WsFloatingScope(type="note", id="n-1"),
|
|
||||||
)
|
|
||||||
data = frame.model_dump()
|
|
||||||
assert data["type"] == "floating_request"
|
|
||||||
assert data["scope"]["type"] == "note"
|
|
||||||
assert data["scope"]["id"] == "n-1"
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_request_invalid_scope_type():
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
WsFloatingRequest(
|
|
||||||
message="X",
|
|
||||||
scope=WsFloatingScope(type="unknown"), # type: ignore[arg-type]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_request_requires_scope():
|
|
||||||
with pytest.raises(ValidationError):
|
|
||||||
WsFloatingRequest.model_validate({"type": "floating_request", "message": "X"})
|
|
||||||
|
|
||||||
|
|
||||||
# ── WsStreamStart ─────────────────────────────────────────────────────
|
# ── WsStreamStart ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
@@ -189,51 +138,3 @@ def test_stream_end_deserializes():
|
|||||||
assert frame.request_id == "r3"
|
assert frame.request_id == "r3"
|
||||||
|
|
||||||
|
|
||||||
# ── WsFloatingDomain ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_domain_tasks():
|
|
||||||
frame = WsFloatingDomain(request_id="r1", domain=WsDomain(type="task"))
|
|
||||||
assert frame.type == WsFrameType.floating_domain
|
|
||||||
assert frame.domain.type == "task"
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_domain_valid_domains():
|
|
||||||
frame = WsFloatingDomain(
|
|
||||||
request_id="r1",
|
|
||||||
domain=WsDomain(type="project", id="213213-312321-312312-421321", section="task"),
|
|
||||||
)
|
|
||||||
assert frame.domain.type == "project"
|
|
||||||
assert frame.domain.id == "213213-312321-312312-421321"
|
|
||||||
assert frame.domain.section == "task"
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_domain_object_valid():
|
|
||||||
frame = WsFloatingDomain(
|
|
||||||
request_id="r1",
|
|
||||||
domain=WsDomain(type="project", id="p1", section="task"),
|
|
||||||
)
|
|
||||||
assert frame.domain.type == "project"
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_domain_serializes():
|
|
||||||
d = WsFloatingDomain(
|
|
||||||
request_id="r1",
|
|
||||||
domain=WsDomain(type="timeline"),
|
|
||||||
).model_dump()
|
|
||||||
assert d == {
|
|
||||||
"type": "floating_domain",
|
|
||||||
"request_id": "r1",
|
|
||||||
"domain": {"type": "timeline", "id": None, "section": None},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def test_floating_domain_deserializes():
|
|
||||||
raw = {
|
|
||||||
"type": "floating_domain",
|
|
||||||
"request_id": "r1",
|
|
||||||
"domain": {"type": "node", "id": "n-1", "section": None},
|
|
||||||
}
|
|
||||||
frame = WsFloatingDomain.model_validate(raw)
|
|
||||||
assert frame.domain.type == "node"
|
|
||||||
assert frame.domain.id == "n-1"
|
|
||||||
|
|||||||
Reference in New Issue
Block a user