95 lines
3.8 KiB
Python
95 lines
3.8 KiB
Python
"""E2E: opencode-native surfaces its live model as a read-only pill.
|
|
|
|
opencode owns its model (the user switches it inside the opencode TUI), but it
|
|
mirrors the live model into the session's ``model_override`` — set at launch and
|
|
updated by the forwarder on every in-TUI switch. The web UI must surface *that*
|
|
in the model pill so the indicator tracks the TUI, even though opencode ships no
|
|
switchable web model list (the dropdown stays empty / display-only).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from urllib.parse import urlparse
|
|
|
|
from playwright.sync_api import Page, Route, expect
|
|
|
|
# Launch-resolved default the runner booted opencode with.
|
|
LAUNCH_MODEL = "openrouter/nemotron"
|
|
# The model the user switched to inside the opencode TUI; the forwarder mirrored
|
|
# it into ``model_override``. This — not the launch default — must show.
|
|
LIVE_TUI_MODEL = "openrouter/llama-3.3-70b-instruct"
|
|
|
|
|
|
def _patch_session_as_opencode_native(page: Page, session_id: str) -> None:
|
|
"""Patch the browser's session snapshot into an opencode-native response.
|
|
|
|
The server fixture seeds a normal ``hello_world`` session so the page can
|
|
boot against the real app/server. This route patch rewrites only the
|
|
``GET /v1/sessions/{session_id}`` response as seen by the browser, mirroring
|
|
the AP snapshot after an opencode-native runner has mirrored its live TUI
|
|
model into ``model_override``. opencode exposes no switchable web model
|
|
list, so ``model_options`` stays absent.
|
|
|
|
:param page: Playwright page before navigation.
|
|
:param session_id: Session id to patch, e.g. ``"conv_abc123"``.
|
|
:returns: None.
|
|
"""
|
|
|
|
def _handle(route: Route) -> None:
|
|
request = route.request
|
|
parsed = urlparse(request.url)
|
|
if parsed.path != f"/v1/sessions/{session_id}" or request.method != "GET":
|
|
route.continue_()
|
|
return
|
|
|
|
response = route.fetch()
|
|
payload = response.json()
|
|
payload["labels"] = {
|
|
**payload.get("labels", {}),
|
|
"omnigent.wrapper": "opencode-native-ui",
|
|
}
|
|
payload["harness"] = "opencode"
|
|
payload["llm_model"] = LAUNCH_MODEL
|
|
payload["model_override"] = LIVE_TUI_MODEL
|
|
route.fulfill(
|
|
status=200,
|
|
headers={**response.headers, "content-type": "application/json"},
|
|
body=json.dumps(payload),
|
|
)
|
|
|
|
page.route("**/v1/sessions/**", _handle)
|
|
|
|
|
|
def test_opencode_native_pill_shows_live_tui_model(
|
|
page: Page,
|
|
seeded_session: tuple[str, str],
|
|
) -> None:
|
|
"""The model pill surfaces opencode's mirrored ``model_override``.
|
|
|
|
Covers the PR's user-facing path: an opencode-native session shows its live
|
|
model (the override the forwarder mirrors from the TUI), not the stale
|
|
launch default, and the harness identity reads "OpenCode".
|
|
|
|
:param page: Playwright page fixture.
|
|
:param seeded_session: ``(base_url, session_id)`` for a real server-backed
|
|
session; the browser snapshot is patched to opencode-native.
|
|
:returns: None.
|
|
"""
|
|
base_url, session_id = seeded_session
|
|
_patch_session_as_opencode_native(page, session_id)
|
|
|
|
page.goto(f"{base_url}/c/{session_id}")
|
|
|
|
# The pill mirrors the live TUI model (the override), NOT the launch default.
|
|
trigger = page.get_by_test_id("agent-picker-trigger")
|
|
expect(trigger).to_contain_text(LIVE_TUI_MODEL, timeout=15_000)
|
|
expect(trigger).not_to_contain_text(LAUNCH_MODEL)
|
|
|
|
# opencode is identified as its own native wrapper in the status tray.
|
|
expect(page.get_by_test_id("composer-harness")).to_contain_text("OpenCode")
|
|
|
|
# Display-only: opencode ships no switchable web model rows. (Switching
|
|
# stays in the opencode TUI; the web pill only reflects it.)
|
|
expect(page.locator('[data-testid="model-picker-item"]')).to_have_count(0)
|