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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user