Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 8 additions & 6 deletions src/adapters/openai-responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2132,6 +2132,7 @@ const MUSE_SPARK_WEB_SEARCH_STRICT_MODELS = new Set([
const MUSE_SPARK_WEB_SEARCH_STRICT_RESPONSE_URLS = new Set([
"https://opencode.ai/zen/v1/responses",
"https://opencode.ai/zen/go/v1/responses",
"https://api.meta.ai/v1/responses",
]);

const MUSE_SPARK_UNSUPPORTED_WEB_SEARCH_FIELDS = [
Expand All @@ -2140,12 +2141,13 @@ const MUSE_SPARK_UNSUPPORTED_WEB_SEARCH_FIELDS = [
] as const;

/**
* OpenCode Zen / Go Muse Spark Responses gateway refuses a short list of Codex
* `web_search` fields. `web_search_preview` keeps its accepted shape, and Luna
* remains untouched. Match the exact effective request URL; malformed, credentialed,
* or parameterized destinations keep their original body instead of assuming this
* gateway contract. Keep the rejected names together so a newly identified field is
* a one-line compatibility update rather than another bespoke rewrite.
* OpenCode Zen / Go and the direct Meta Muse Spark Responses gateways refuse a
* short list of Codex `web_search` fields. `web_search_preview` keeps its accepted
* shape, and Luna remains untouched. Match the exact effective request URL;
* malformed, credentialed, or parameterized destinations keep their original body
* instead of assuming this gateway contract. Keep the rejected names together so a
* newly identified field is a one-line compatibility update rather than another
* bespoke rewrite.
*/
function stripMuseSparkUnsupportedWebSearchFields(
body: unknown,
Expand Down
40 changes: 37 additions & 3 deletions tests/providers/muse-spark-web-search-compat.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ const META_PROVIDER = {
baseUrl: "https://api.meta.ai/v1",
};

const META_PATH_PROVIDER = {
...ZEN_PROVIDER,
baseUrl: "https://api.meta.ai",
responsesPath: "/v1/responses",
};

/** A Codex web_search declaration exactly as `hosted_spec.rs` emits it for TextAndImage. */
function webSearchTool(): Record<string, unknown> {
return {
Expand Down Expand Up @@ -219,7 +225,14 @@ describe("#2617/#3378 Muse Spark web_search compatibility", () => {
}
});

test("direct Meta preserves its web_search fields at both tool positions", () => {
/**
* #3456 scoped the guard to OpenCode Zen/Go URLs on the assumption that
* `https://api.meta.ai/v1` accepted Codex's extra web_search fields. Direct
* Meta still 400s `tools[].search_content_types` on ordinary `web_search`
* (live 2026-09-07 against muse-spark-1.3-contributor). Same model-id set,
* same field drop, same preview preservation.
*/
test("direct Meta strips rejected web_search fields at both tool positions", () => {
const body = buildForProvider(META_PROVIDER, "muse-spark-1.3-contributor", {
tools: [webSearchTool()],
input: [{ type: "additional_tools", tools: [webSearchTool()] }],
Expand All @@ -228,8 +241,29 @@ describe("#2617/#3378 Muse Spark web_search compatibility", () => {
const item = (body.input as Array<Record<string, unknown>>)[0]!;
const nested = (item.tools as Array<Record<string, unknown>>)[0]!;
for (const declaration of [tool, nested]) {
expect(declaration.search_content_types).toEqual(["text", "image"]);
expect(declaration.indexed_web_access).toBe(true);
expect(declaration.type).toBe("web_search");
expect(declaration.search_context_size).toBe("medium");
expect(Object.hasOwn(declaration, "search_content_types")).toBe(false);
expect(Object.hasOwn(declaration, "indexed_web_access")).toBe(false);
}
});

test("direct Meta keeps the field on web_search_preview", () => {
const body = buildForProvider(META_PROVIDER, "muse-spark-1.3-contributor", {
tools: [{ ...webSearchTool(), type: "web_search_preview" }],
});
const tool = toolsOf(body)[0]!;
expect(tool.type).toBe("web_search_preview");
expect(tool.search_content_types).toEqual(["text", "image"]);
expect(tool.indexed_web_access).toBe(true);
});

test("split Meta baseUrl and responsesPath derives the same strict destination", () => {
const body = buildForProvider(META_PATH_PROVIDER, "muse-spark-1.3-contributor", {
tools: [webSearchTool()],
});
const tool = toolsOf(body)[0]!;
expect(Object.hasOwn(tool, "search_content_types")).toBe(false);
expect(Object.hasOwn(tool, "indexed_web_access")).toBe(false);
});
});
Loading