mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 10:17:39 +00:00
fix(tools): correct Grok response parsing for xAI Responses API (#13049)
* fix(tools): correct Grok response parsing for xAI Responses API The xAI Responses API returns content in output[0].content[0].text, not in output_text field. Updated GrokSearchResponse type and runGrokSearch to extract content from the correct path. Fixes the 'No response' issue when using Grok web search. * fix(tools): harden Grok web_search parsing (#13049) (thanks @ereid7) --------- Co-authored-by: erai <erai@erais-Mac-mini.local> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
This commit is contained in:
@@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Auth: strip embedded line breaks from pasted API keys and tokens before storing/resolving credentials.
|
- Auth: strip embedded line breaks from pasted API keys and tokens before storing/resolving credentials.
|
||||||
- Web UI: make chat refresh smoothly scroll to the latest messages and suppress new-messages badge flash during manual refresh.
|
- Web UI: make chat refresh smoothly scroll to the latest messages and suppress new-messages badge flash during manual refresh.
|
||||||
- Tools/web_search: include provider-specific settings in the web search cache key, and pass `inlineCitations` for Grok. (#12419) Thanks @tmchow.
|
- Tools/web_search: include provider-specific settings in the web search cache key, and pass `inlineCitations` for Grok. (#12419) Thanks @tmchow.
|
||||||
|
- Tools/web_search: fix Grok response parsing for xAI Responses API output blocks. (#13049) Thanks @ereid7.
|
||||||
- Tools/web_search: normalize direct Perplexity model IDs while keeping OpenRouter model IDs unchanged. (#12795) Thanks @cdorsey.
|
- Tools/web_search: normalize direct Perplexity model IDs while keeping OpenRouter model IDs unchanged. (#12795) Thanks @cdorsey.
|
||||||
- Model failover: treat HTTP 400 errors as failover-eligible, enabling automatic model fallback. (#1879) Thanks @orenyomtov.
|
- Model failover: treat HTTP 400 errors as failover-eligible, enabling automatic model fallback. (#1879) Thanks @orenyomtov.
|
||||||
- Errors: prevent false positive context overflow detection when conversation mentions "context overflow" topic. (#2078) Thanks @sbking.
|
- Errors: prevent false positive context overflow detection when conversation mentions "context overflow" topic. (#2078) Thanks @sbking.
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ const {
|
|||||||
resolveGrokApiKey,
|
resolveGrokApiKey,
|
||||||
resolveGrokModel,
|
resolveGrokModel,
|
||||||
resolveGrokInlineCitations,
|
resolveGrokInlineCitations,
|
||||||
|
extractGrokContent,
|
||||||
} = __testing;
|
} = __testing;
|
||||||
|
|
||||||
describe("web_search perplexity baseUrl defaults", () => {
|
describe("web_search perplexity baseUrl defaults", () => {
|
||||||
@@ -142,3 +143,23 @@ describe("web_search grok config resolution", () => {
|
|||||||
expect(resolveGrokInlineCitations({ inlineCitations: false })).toBe(false);
|
expect(resolveGrokInlineCitations({ inlineCitations: false })).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("web_search grok response parsing", () => {
|
||||||
|
it("extracts content from Responses API output blocks", () => {
|
||||||
|
expect(
|
||||||
|
extractGrokContent({
|
||||||
|
output: [
|
||||||
|
{
|
||||||
|
content: [{ text: "hello from output" }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
).toBe("hello from output");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("falls back to deprecated output_text", () => {
|
||||||
|
expect(extractGrokContent({ output_text: "hello from output_text" })).toBe(
|
||||||
|
"hello from output_text",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -103,7 +103,15 @@ type GrokConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type GrokSearchResponse = {
|
type GrokSearchResponse = {
|
||||||
output_text?: string;
|
output?: Array<{
|
||||||
|
type?: string;
|
||||||
|
role?: string;
|
||||||
|
content?: Array<{
|
||||||
|
type?: string;
|
||||||
|
text?: string;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
output_text?: string; // deprecated field - kept for backwards compatibility
|
||||||
citations?: string[];
|
citations?: string[];
|
||||||
inline_citations?: Array<{
|
inline_citations?: Array<{
|
||||||
start_index: number;
|
start_index: number;
|
||||||
@@ -123,6 +131,15 @@ type PerplexitySearchResponse = {
|
|||||||
|
|
||||||
type PerplexityBaseUrlHint = "direct" | "openrouter";
|
type PerplexityBaseUrlHint = "direct" | "openrouter";
|
||||||
|
|
||||||
|
function extractGrokContent(data: GrokSearchResponse): string | undefined {
|
||||||
|
// xAI Responses API format: output[0].content[0].text
|
||||||
|
const fromResponses = data.output?.[0]?.content?.[0]?.text;
|
||||||
|
if (typeof fromResponses === "string" && fromResponses) {
|
||||||
|
return fromResponses;
|
||||||
|
}
|
||||||
|
return typeof data.output_text === "string" ? data.output_text : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
function resolveSearchConfig(cfg?: OpenClawConfig): WebSearchConfig {
|
function resolveSearchConfig(cfg?: OpenClawConfig): WebSearchConfig {
|
||||||
const search = cfg?.tools?.web?.search;
|
const search = cfg?.tools?.web?.search;
|
||||||
if (!search || typeof search !== "object") {
|
if (!search || typeof search !== "object") {
|
||||||
@@ -476,7 +493,7 @@ async function runGrokSearch(params: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const data = (await res.json()) as GrokSearchResponse;
|
const data = (await res.json()) as GrokSearchResponse;
|
||||||
const content = data.output_text ?? "No response";
|
const content = extractGrokContent(data) ?? "No response";
|
||||||
const citations = data.citations ?? [];
|
const citations = data.citations ?? [];
|
||||||
const inlineCitations = data.inline_citations;
|
const inlineCitations = data.inline_citations;
|
||||||
|
|
||||||
@@ -548,7 +565,7 @@ async function runWebSearch(params: {
|
|||||||
provider: params.provider,
|
provider: params.provider,
|
||||||
model: params.grokModel ?? DEFAULT_GROK_MODEL,
|
model: params.grokModel ?? DEFAULT_GROK_MODEL,
|
||||||
tookMs: Date.now() - start,
|
tookMs: Date.now() - start,
|
||||||
content,
|
content: wrapWebContent(content),
|
||||||
citations,
|
citations,
|
||||||
inlineCitations,
|
inlineCitations,
|
||||||
};
|
};
|
||||||
@@ -713,4 +730,5 @@ export const __testing = {
|
|||||||
resolveGrokApiKey,
|
resolveGrokApiKey,
|
||||||
resolveGrokModel,
|
resolveGrokModel,
|
||||||
resolveGrokInlineCitations,
|
resolveGrokInlineCitations,
|
||||||
|
extractGrokContent,
|
||||||
} as const;
|
} as const;
|
||||||
|
|||||||
Reference in New Issue
Block a user