rename popup chat to floating chat

This commit is contained in:
2026-03-08 22:53:31 +01:00
parent 0bd46937d3
commit 34f01234c9
8 changed files with 102 additions and 102 deletions

View File

@@ -44,7 +44,7 @@ from app.core.agent_runner import trigger_pending_runs
from app.core.device_manager import device_manager
from app.core.memory_middleware import MemoryMiddleware
from app.core.orchestrator import orchestrate_v3_stream
from app.core.output_formatter import HomeFormatter, PopupFormatter
from app.core.output_formatter import HomeFormatter, FloatingFormatter
from app.core.ws_context import clear_client_executor, set_client_executor
from app.db import async_session
from app.models import AgentRunLog
@@ -183,9 +183,9 @@ async def _message_loop(websocket: WebSocket, user_id: str) -> None:
_handle_home_request(websocket, user_id, frame)
)
elif frame_type == WsFrameType.popup_request:
elif frame_type == WsFrameType.floating_request:
asyncio.create_task(
_handle_popup_request(websocket, user_id, frame)
_handle_floating_request(websocket, user_id, frame)
)
elif frame_type == "pong":
@@ -257,12 +257,12 @@ async def _handle_home_request(
)
async def _handle_popup_request(
async def _handle_floating_request(
websocket: WebSocket,
user_id: str,
frame: dict,
) -> None:
"""Handle a popup_request frame — streams PopupFormatter output back on the socket."""
"""Handle a floating_request frame — streams FloatingFormatter output back on the socket."""
request_id = frame.get("request_id") or str(uuid4())
message: str = frame.get("message", "")
session_id: str = frame.get("session_id") or str(uuid4())
@@ -280,14 +280,14 @@ async def _handle_popup_request(
response_chunks: list[str] = []
try:
token_stream = orchestrate_v3_stream(user_id, message, context)
formatter = PopupFormatter(request_id=request_id)
formatter = FloatingFormatter(request_id=request_id)
async for ws_frame in formatter.format(token_stream):
await websocket.send_text(ws_frame.model_dump_json())
if ws_frame.type == "stream_text": # type: ignore[union-attr]
response_chunks.append(ws_frame.chunk) # type: ignore[union-attr]
except Exception as exc:
logger.error(
"device_ws: popup_request failed user=%s req=%s: %s",
"device_ws: floating_request failed user=%s req=%s: %s",
user_id, request_id, exc,
)
finally:

View File

@@ -166,7 +166,7 @@ async def orchestrate_v3_stream(
"""v3 streaming orchestration — yields (agent_name, token) pairs.
The first yield always carries the agent_name with an empty token so that
callers (e.g. PopupFormatter) can detect the routing domain before any text
callers (e.g. FloatingFormatter) can detect the routing domain before any text
tokens arrive.
"""
if reg is None:

View File

@@ -1,7 +1,7 @@
"""Output Formatter — transforms orchestrator token streams into WS frame sequences.
HomeFormatter: produces stream_start, stream_text / stream_block, stream_end
PopupFormatter: produces popup_domain, stream_text, stream_end
FloatingFormatter: produces floating_domain, stream_text, stream_end
"""
from __future__ import annotations
@@ -12,7 +12,7 @@ from collections.abc import AsyncGenerator
from typing import Any
from app.schemas import (
WsPopupDomain,
WsFloatingDomain,
WsStreamBlock,
WsStreamEnd,
WsStreamStart,
@@ -24,7 +24,7 @@ logger = logging.getLogger(__name__)
# Valid chart types (matching shadcn/ui Recharts wrappers in Electron)
_VALID_CHART_TYPES = {"area", "bar", "line", "pie", "radar", "radial"}
# Map agent name → popup domain
# Map agent name → floating domain
_AGENT_DOMAIN: dict[str, str] = {
"task_agent": "tasks",
"checkpoint_agent": "checkpoints",
@@ -32,7 +32,7 @@ _AGENT_DOMAIN: dict[str, str] = {
"project_agent": "projects",
}
WsFrame = WsStreamStart | WsStreamText | WsStreamBlock | WsStreamEnd | WsPopupDomain
WsFrame = WsStreamStart | WsStreamText | WsStreamBlock | WsStreamEnd | WsFloatingDomain
class HomeFormatter:
@@ -191,11 +191,11 @@ class HomeFormatter:
return matches if matches else None
class PopupFormatter:
class FloatingFormatter:
"""Parses a token stream from orchestrate_v3_stream and yields WS frames.
Emits popup_domain immediately (from agent_name), then streams all tokens
as plain stream_text — no block parsing for popup context.
Emits floating_domain immediately (from agent_name), then streams all tokens
as plain stream_text — no block parsing for floating context.
"""
def __init__(self, request_id: str) -> None:
@@ -210,7 +210,7 @@ class PopupFormatter:
async for agent_name, token in token_stream:
if not domain_sent:
domain = _AGENT_DOMAIN.get(agent_name, "tasks")
yield WsPopupDomain(
yield WsFloatingDomain(
request_id=self.request_id,
domain=domain, # type: ignore[arg-type]
)

View File

@@ -174,12 +174,12 @@ class WsFrameType(str, Enum):
device_hello = "device_hello"
# ── v3 frame types ─────────────────────────────────────────────────
home_request = "home_request"
popup_request = "popup_request"
floating_request = "floating_request"
stream_start = "stream_start"
stream_text = "stream_text"
stream_block = "stream_block"
stream_end = "stream_end"
popup_domain = "popup_domain"
floating_domain = "floating_domain"
data_request = "data_request"
data_response = "data_response"
mutation = "mutation"
@@ -263,8 +263,8 @@ class WsAgentComplete(BaseModel):
# ── WebSocket v3 Frame Models ─────────────────────────────────────────
class WsPopupScope(BaseModel):
"""Scope for a popup request — narrows the agent to a specific entity."""
class WsFloatingScope(BaseModel):
"""Scope for a floating request — narrows the agent to a specific entity."""
type: Literal["task", "project", "note", "checkpoint"]
id: str | None = None
@@ -278,12 +278,12 @@ class WsHomeRequest(BaseModel):
conversation_history: list[dict[str, Any]] = Field(default_factory=list)
class WsPopupRequest(BaseModel):
"""Client → Server: Popup chat message scoped to an entity."""
class WsFloatingRequest(BaseModel):
"""Client → Server: Floating chat message scoped to an entity."""
type: Literal[WsFrameType.popup_request] = WsFrameType.popup_request
type: Literal[WsFrameType.floating_request] = WsFrameType.floating_request
message: str
scope: WsPopupScope
scope: WsFloatingScope
class WsStreamStart(BaseModel):
@@ -318,10 +318,10 @@ class WsStreamEnd(BaseModel):
mutations: list[dict[str, Any]] = Field(default_factory=list)
class WsPopupDomain(BaseModel):
"""Server → Client: domain determined for a popup request."""
class WsFloatingDomain(BaseModel):
"""Server → Client: domain determined for a floating request."""
type: Literal[WsFrameType.popup_domain] = WsFrameType.popup_domain
type: Literal[WsFrameType.floating_domain] = WsFrameType.floating_domain
request_id: str
domain: Literal["tasks", "checkpoints", "notes", "projects"]