fix(xai): scope native web_search to swap-only + reconcile composer ctx to 200k
Salvage corrections on top of @XVVH's #44341: - Make native web_search injection a 1:1 swap for an already-present client web_search function, NOT an additive grant. The original unconditionally appended {"type":"web_search"} on every is_xai_responses turn with any tools, force-enabling Grok server-side search even when the user never enabled the web toolset (bypassing Hermes web-provider config + tool-trace plumbing). Now gated on a client web_search actually being present. - Reconcile grok-composer context to 200000 (merged in #47908) rather than 262144; 200k is xAI's published usable context window for Composer 2.5, 262144 is the /v1/responses input+output budget. - Update tests to match scoped behavior + add a no-web-toolset guard test. - AUTHOR_MAP entry for #44341 salvage. Incomplete-guard (server-side *_call items at in_progress no longer flip has_incomplete_items) and preflight built-in-tool allowlist kept as-is.
This commit is contained in:
@@ -142,7 +142,7 @@ class TestDefaultContextLengths:
|
||||
("grok-4", 256000),
|
||||
("grok-4-0709", 256000),
|
||||
("grok-build-0.1", 256000),
|
||||
("grok-composer-2.5-fast", 262144),
|
||||
("grok-composer-2.5-fast", 200000),
|
||||
("grok-code-fast-1", 256000),
|
||||
("grok-3", 131072),
|
||||
("grok-3-mini", 131072),
|
||||
|
||||
@@ -263,12 +263,43 @@ class TestCodexBuildKwargs:
|
||||
# full history.
|
||||
assert "reasoning.encrypted_content" in kw.get("include", [])
|
||||
|
||||
def test_xai_injects_native_web_search_tool(self, transport):
|
||||
"""xAI path declares xAI's native server-side web_search built-in so
|
||||
grok server-side search runs to completion (otherwise the turn stalls
|
||||
as reasoning-with-no-answer -> false 'incomplete' -> 3 retries -> fail).
|
||||
def test_xai_injects_native_web_search_when_client_web_search_present(self, transport):
|
||||
"""xAI path swaps a client-side ``web_search`` function for xAI's
|
||||
native server-side ``web_search`` built-in so grok server-side search
|
||||
runs to completion (otherwise the turn stalls as
|
||||
reasoning-with-no-answer -> false 'incomplete' -> 3 retries -> fail).
|
||||
Non-conflicting client tools are preserved.
|
||||
"""
|
||||
messages = [{"role": "user", "content": "Find current prices."}]
|
||||
kw = transport.build_kwargs(
|
||||
model="grok-composer-2.5-fast", messages=messages,
|
||||
tools=[
|
||||
{"type": "function", "function": {
|
||||
"name": "read_file", "description": "Read a file.",
|
||||
"parameters": {"type": "object",
|
||||
"properties": {"path": {"type": "string"}}}}},
|
||||
{"type": "function", "function": {
|
||||
"name": "web_search", "description": "Search the web.",
|
||||
"parameters": {"type": "object",
|
||||
"properties": {"query": {"type": "string"}}}}},
|
||||
],
|
||||
is_xai_responses=True,
|
||||
)
|
||||
tool_types = [t.get("type") for t in kw.get("tools", [])]
|
||||
assert "web_search" in tool_types, kw.get("tools")
|
||||
# Non-conflicting client-side tools are preserved.
|
||||
names = [t.get("name") for t in kw.get("tools", []) if t.get("type") == "function"]
|
||||
assert "read_file" in names
|
||||
|
||||
def test_xai_does_not_inject_native_web_search_without_client_web_search(self, transport):
|
||||
"""The native ``web_search`` built-in is a 1:1 swap for an
|
||||
already-requested client ``web_search`` — NOT an additive grant. A
|
||||
turn whose toolset has no ``web_search`` (user never enabled the web
|
||||
toolset) must not get Grok server-side search force-injected, which
|
||||
would silently bypass Hermes's web-provider config and tool-trace
|
||||
plumbing for every xai-oauth turn.
|
||||
"""
|
||||
messages = [{"role": "user", "content": "Read this file."}]
|
||||
kw = transport.build_kwargs(
|
||||
model="grok-composer-2.5-fast", messages=messages,
|
||||
tools=[{"type": "function", "function": {
|
||||
@@ -277,10 +308,9 @@ class TestCodexBuildKwargs:
|
||||
"properties": {"path": {"type": "string"}}}}}],
|
||||
is_xai_responses=True,
|
||||
)
|
||||
tool_types = [t.get("type") for t in kw.get("tools", [])]
|
||||
assert "web_search" in tool_types, kw.get("tools")
|
||||
# Non-conflicting client-side tools are preserved.
|
||||
names = [t.get("name") for t in kw.get("tools", []) if t.get("type") == "function"]
|
||||
tools = kw.get("tools", [])
|
||||
assert not any(t.get("type") == "web_search" for t in tools), tools
|
||||
names = [t.get("name") for t in tools if t.get("type") == "function"]
|
||||
assert "read_file" in names
|
||||
|
||||
def test_xai_drops_clientside_web_search_to_avoid_duplicate(self, transport):
|
||||
|
||||
@@ -949,15 +949,18 @@ def test_grok_4_still_resolves_to_256k():
|
||||
assert DEFAULT_CONTEXT_LENGTHS[matched_key] == 256_000
|
||||
|
||||
|
||||
def test_grok_composer_context_length_is_262k():
|
||||
def test_grok_composer_context_length_is_200k():
|
||||
"""grok-composer-2.5-fast is OAuth-only and missing from /v1/models.
|
||||
|
||||
Without a specific entry it fell through to the generic ``grok`` 131k
|
||||
catch-all, under-reporting ~262k enforced on /v1/responses.
|
||||
catch-all. xAI publishes a 200k usable context window for Composer 2.5
|
||||
on Grok Build (SuperGrok / Premium+); /v1/responses additionally caps
|
||||
the input+output budget at ~262144, but the usable context (what we
|
||||
track) is 200k.
|
||||
"""
|
||||
from agent.model_metadata import DEFAULT_CONTEXT_LENGTHS
|
||||
|
||||
assert DEFAULT_CONTEXT_LENGTHS["grok-composer"] == 262_144
|
||||
assert DEFAULT_CONTEXT_LENGTHS["grok-composer"] == 200_000
|
||||
slug = "grok-composer-2.5-fast"
|
||||
matched_key = max(
|
||||
(k for k in DEFAULT_CONTEXT_LENGTHS if k in slug.lower()),
|
||||
@@ -966,7 +969,7 @@ def test_grok_composer_context_length_is_262k():
|
||||
assert matched_key == "grok-composer", (
|
||||
f"Expected longest-first match on grok-composer for {slug}, got {matched_key}"
|
||||
)
|
||||
assert DEFAULT_CONTEXT_LENGTHS[matched_key] == 262_144
|
||||
assert DEFAULT_CONTEXT_LENGTHS[matched_key] == 200_000
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user