fix(xai): accept Grok Build code during loopback wait + tiny screenshot guard

xAI's consent page renders the authorization code in-page instead of
redirecting to the loopback callback, so the listener just hangs and the
manual-paste flow demands a callback URL that never contains the token.

- auth.py: poll stdin non-blockingly while waiting for the xAI loopback
  callback; accept a pasted bare Grok Build code and substitute the locally
  generated state (PKCE code_verifier still binds the exchange). No need to
  wait for timeout or re-run with --manual-paste.
- computer_use: parse PNG/JPEG dimensions from base64 and fall back to the
  text/AX/SOM payload when the screenshot is below the provider minimum
  (8x8), which xAI rejects with HTTP 400.
- model_setup_flows.py: xAI credential reuse prompt uses the standard radio
  picker via a shared _prompt_auth_credentials_choice helper.
- main.py: thread a title through _prompt_provider_choice; re-home the helper
  import (flows live in model_setup_flows.py post-decomposition).

Salvaged from #36781 onto current main (contributor's main.py edits re-homed
to model_setup_flows.py, where the flows were extracted since the PR opened).
This commit is contained in:
Ondrej Drapalik
2026-06-09 23:21:24 -07:00
committed by Teknium
parent 095f526b11
commit 1c055a4c58
8 changed files with 330 additions and 50 deletions
+32 -2
View File
@@ -338,7 +338,7 @@ class TestCaptureResponse:
from tools.computer_use.backend import CaptureResult
from tools.computer_use import tool as cu_tool
fake_png = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII="
fake_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAADUlEQVR4nGNgGAUgAAABCAABgukLHQAAAABJRU5ErkJggg=="
class FakeBackend:
def start(self): pass
@@ -372,11 +372,41 @@ class TestCaptureResponse:
assert any(p.get("type") == "image_url" for p in out["content"])
assert any(p.get("type") == "text" for p in out["content"])
def test_capture_tiny_image_returns_text_json(self):
"""Providers can reject <8px images, so placeholders must be omitted."""
from tools.computer_use.backend import CaptureResult, UIElement
from tools.computer_use import tool as cu_tool
tiny_png = "iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAYAAABytg0kAAAAC0lEQVR4nGNgQAcAABIAAXfx+gAAAAAASUVORK5CYII="
cap = CaptureResult(
mode="som",
width=0,
height=0,
png_b64=tiny_png,
elements=[
UIElement(index=1, role="AXButton", label="Continue", bounds=(10, 20, 30, 30)),
],
app="Safari",
window_title="Example",
png_bytes_len=68,
)
with patch.object(cu_tool, "_should_route_through_aux_vision",
return_value=False):
out = cu_tool._capture_response(cap)
parsed = json.loads(out)
assert parsed["width"] == 2
assert parsed["height"] == 2
assert "screenshot omitted" in parsed["summary"]
assert parsed["elements"][0]["label"] == "Continue"
def test_capture_som_with_elements_formats_index(self):
from tools.computer_use.backend import CaptureResult, UIElement
from tools.computer_use import tool as cu_tool
fake_png = "iVBORw0KGgo="
fake_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAADUlEQVR4nGNgGAUgAAABCAABgukLHQAAAABJRU5ErkJggg=="
class FakeBackend:
def start(self): pass