From ccb6b7c3eac7146c6ed0f319baeb1772023a0a14 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Wed, 8 Jul 2026 17:29:36 -0500 Subject: [PATCH 01/34] fix: improve xai cache hit rate (#35970) --- bun.lock | 20 +- package.json | 2 +- packages/core/package.json | 2 +- .../core/test/provider-xai-responses.test.ts | 32 ++++ packages/opencode/package.json | 2 +- packages/opencode/src/provider/transform.ts | 7 +- .../opencode/test/provider/transform.test.ts | 52 +++++- patches/@ai-sdk%2Fxai@3.0.102.patch | 174 ++++++++++++++++++ patches/@ai-sdk%2Fxai@3.0.82.patch | 99 ---------- 9 files changed, 281 insertions(+), 109 deletions(-) create mode 100644 packages/core/test/provider-xai-responses.test.ts create mode 100644 patches/@ai-sdk%2Fxai@3.0.102.patch delete mode 100644 patches/@ai-sdk%2Fxai@3.0.82.patch diff --git a/bun.lock b/bun.lock index 6af38c1f78..2c91b45ed6 100644 --- a/bun.lock +++ b/bun.lock @@ -308,7 +308,7 @@ "@ai-sdk/provider-utils": "4.0.23", "@ai-sdk/togetherai": "2.0.41", "@ai-sdk/vercel": "2.0.39", - "@ai-sdk/xai": "3.0.82", + "@ai-sdk/xai": "3.0.102", "@aws-sdk/credential-providers": "3.1057.0", "@effect/opentelemetry": "catalog:", "@effect/platform-node": "catalog:", @@ -582,7 +582,7 @@ "@ai-sdk/provider": "3.0.8", "@ai-sdk/togetherai": "2.0.41", "@ai-sdk/vercel": "2.0.39", - "@ai-sdk/xai": "3.0.82", + "@ai-sdk/xai": "3.0.102", "@aws-sdk/credential-providers": "3.1057.0", "@clack/prompts": "1.0.0-alpha.1", "@effect/opentelemetry": "catalog:", @@ -1064,6 +1064,7 @@ ], "patchedDependencies": { "@pierre/trees@1.0.0-beta.4": "patches/@pierre%2Ftrees@1.0.0-beta.4.patch", + "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", "@modelcontextprotocol/sdk@1.29.0": "patches/@modelcontextprotocol%2Fsdk@1.29.0.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", @@ -1073,7 +1074,6 @@ "@tanstack/solid-virtual@3.13.28": "patches/@tanstack%2Fsolid-virtual@3.13.28.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", - "@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch", "@tanstack/virtual-core@3.17.0": "patches/@tanstack%2Fvirtual-core@3.17.0.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", }, @@ -1211,7 +1211,7 @@ "@ai-sdk/vercel": ["@ai-sdk/vercel@2.0.39", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.37", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.21" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-8eu3ljJpkCTP4ppcyYB+NcBrkcBoSOFthCSgk5VnjaxnDaOJFaxnPwfddM7wx3RwMk2CiK1O61Px/LlqNc7QkQ=="], - "@ai-sdk/xai": ["@ai-sdk/xai@3.0.82", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-A0VFMufnVf4wODcT3SPQUUzvYXiIO1VhFuXj9r6z/vP4rlo+QRDPw3WSTchcz93ROQWSfBE3I6Szqz342OHi5w=="], + "@ai-sdk/xai": ["@ai-sdk/xai@3.0.102", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.56", "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-NeQyOR7OCqDMgaLS4uNX/ep/HrwUzzFYLzXQSRoqLy2jsnqxAJhsgltRwAwf+ADjyPBIAKEOestWnIQA+LrLrQ=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -5693,7 +5693,11 @@ "@ai-sdk/vercel/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.21", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-MtFUYI1/8mgDvRmaBDjbLJPFFrMG777AvSgyIFQtZHIMzm88R/12vYBBpnk7pfiWLFE1DSZzY4WDYzGbKAcmiw=="], - "@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], + "@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.56", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@ai-sdk/provider-utils": "4.0.35" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-cQrN6OUn/jvsY3OdsU6Wn+ss7vp1iwIcakZKSlSRMnYqShBfyT7Qht+eqmgxs7w9ttrw6FAG6o11AiBs+iEsTA=="], + + "@ai-sdk/xai/@ai-sdk/provider": ["@ai-sdk/provider@3.0.13", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-ZPtVYt5QIJzOta1kdUiDuCx4HhFkvNPv/rvmZ2b1iXwybYjJsCnNYR4PAw4kW7rgVfDARvHXcU64efWuqNp6bw=="], + + "@ai-sdk/xai/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.35", "", { "dependencies": { "@ai-sdk/provider": "3.0.13", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-bjYld/2KGPLt78kpqbya+fD4LYS7BqVQJyUjE3qAHrYB0FR2Q90BaWEVIBZaguTWXf/A8L6uG1zO1v9TxVlGWg=="], "@astrojs/check/yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], @@ -6143,6 +6147,8 @@ "ai-gateway-provider/@ai-sdk/openai": ["@ai-sdk/openai@3.0.53", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-Wld+Rbc05KaUn08uBt06eEuwcgalcIFtIl32Yp+GxuZXUQwOb6YeAuq+C6da4ch6BurFoqEaLemJVwjBb7x+PQ=="], + "ai-gateway-provider/@ai-sdk/xai": ["@ai-sdk/xai@3.0.82", "", { "dependencies": { "@ai-sdk/openai-compatible": "2.0.41", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-A0VFMufnVf4wODcT3SPQUUzvYXiIO1VhFuXj9r6z/vP4rlo+QRDPw3WSTchcz93ROQWSfBE3I6Szqz342OHi5w=="], + "ai-gateway-provider/@openrouter/ai-sdk-provider": ["@openrouter/ai-sdk-provider@2.8.1", "", { "peerDependencies": { "ai": "^6.0.0", "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Y6j3yivgoEUf/kutD/k5GX/mzZfioRFoSx0gbQ+mIOzMaH/vJv1rCkztiuvlLw5xRYQil7oxHUZvmSfXqOx1NQ=="], "ajv-keywords/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], @@ -6577,6 +6583,8 @@ "@ai-sdk/vercel/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@ai-sdk/xai/@ai-sdk/provider-utils/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@astrojs/check/yargs/cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "@astrojs/check/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -6939,6 +6947,8 @@ "ai-gateway-provider/@ai-sdk/anthropic/@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@4.0.27", "", { "dependencies": { "@ai-sdk/provider": "3.0.10", "@standard-schema/spec": "^1.1.0", "eventsource-parser": "^3.0.8" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ubkAJ+xODouwtmN1tYlvTPphH1hPOBfZaEQe8U7skGvFAnIRs9PPpsq57bC2+Ky/MB4yzhd6YOsxTAx9sGpazw=="], + "ai-gateway-provider/@ai-sdk/xai/@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@2.0.41", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-kNAGINk71AlOXx10Dq/PXw4t/9XjdK8uxfpVElRwtSFMdeSiLVt58p9TPx4/FJD+hxZuVhvxYj9r42osxWq79g=="], + "ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="], "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], diff --git a/package.json b/package.json index e65499f878..0d39401196 100644 --- a/package.json +++ b/package.json @@ -147,7 +147,7 @@ "@silvia-odwyer/photon-node@0.3.4": "patches/@silvia-odwyer%2Fphoton-node@0.3.4.patch", "@standard-community/standard-openapi@0.2.9": "patches/@standard-community%2Fstandard-openapi@0.2.9.patch", "solid-js@1.9.10": "patches/solid-js@1.9.10.patch", - "@ai-sdk/xai@3.0.82": "patches/@ai-sdk%2Fxai@3.0.82.patch", + "@ai-sdk/xai@3.0.102": "patches/@ai-sdk%2Fxai@3.0.102.patch", "gcp-metadata@8.1.2": "patches/gcp-metadata@8.1.2.patch", "pacote@21.5.0": "patches/pacote@21.5.0.patch", "@ai-sdk/google@3.0.73": "patches/@ai-sdk%2Fgoogle@3.0.73.patch", diff --git a/packages/core/package.json b/packages/core/package.json index c0927930af..0680fac03d 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -80,7 +80,7 @@ "@ai-sdk/provider-utils": "4.0.23", "@ai-sdk/togetherai": "2.0.41", "@ai-sdk/vercel": "2.0.39", - "@ai-sdk/xai": "3.0.82", + "@ai-sdk/xai": "3.0.102", "@aws-sdk/credential-providers": "3.1057.0", "@effect/opentelemetry": "catalog:", "@effect/platform-node": "catalog:", diff --git a/packages/core/test/provider-xai-responses.test.ts b/packages/core/test/provider-xai-responses.test.ts new file mode 100644 index 0000000000..d9d674fe16 --- /dev/null +++ b/packages/core/test/provider-xai-responses.test.ts @@ -0,0 +1,32 @@ +import { createXai } from "@ai-sdk/xai" +import { expect, test } from "bun:test" + +test("xAI Responses sends promptCacheKey as prompt_cache_key", async () => { + let body: Record | undefined + const mockFetch = Object.assign( + async (_input: Parameters[0], init?: RequestInit) => { + body = JSON.parse(String(init?.body)) + return Response.json({ + id: "response-1", + created_at: 0, + model: "grok-4", + object: "response", + output: [], + usage: { input_tokens: 1, output_tokens: 0 }, + status: "completed", + }) + }, + { preconnect: fetch.preconnect }, + ) + const model = createXai({ + apiKey: "test", + fetch: mockFetch, + }).responses("grok-4") + + await model.doGenerate({ + prompt: [{ role: "user", content: [{ type: "text", text: "Hello" }] }], + providerOptions: { xai: { promptCacheKey: "session-123" } }, + }) + + expect(body?.prompt_cache_key).toBe("session-123") +}) diff --git a/packages/opencode/package.json b/packages/opencode/package.json index eb05f6e384..401803c37c 100644 --- a/packages/opencode/package.json +++ b/packages/opencode/package.json @@ -73,7 +73,7 @@ "@ai-sdk/provider": "3.0.8", "@ai-sdk/togetherai": "2.0.41", "@ai-sdk/vercel": "2.0.39", - "@ai-sdk/xai": "3.0.82", + "@ai-sdk/xai": "3.0.102", "@aws-sdk/credential-providers": "3.1057.0", "@clack/prompts": "1.0.0-alpha.1", "@effect/opentelemetry": "catalog:", diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index f42e14139f..e50f4b7688 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1127,7 +1127,12 @@ export function options(input: { } } - if (input.model.providerID === "openai" || input.providerOptions?.setCacheKey) { + if ( + input.providerOptions?.setCacheKey !== false && + (input.model.providerID === "openai" || + input.model.api.npm === "@ai-sdk/xai" || + input.providerOptions?.setCacheKey) + ) { result["promptCacheKey"] = input.sessionID } diff --git a/packages/opencode/test/provider/transform.test.ts b/packages/opencode/test/provider/transform.test.ts index 7307e3f60b..8f2be02a67 100644 --- a/packages/opencode/test/provider/transform.test.ts +++ b/packages/opencode/test/provider/transform.test.ts @@ -73,7 +73,7 @@ describe("ProviderTransform.options - setCacheKey", () => { expect(result.promptCacheKey).toBeUndefined() }) - test("should set promptCacheKey for openai provider regardless of setCacheKey", () => { + test("should set promptCacheKey for openai provider by default", () => { const openaiModel = { ...mockModel, providerID: "openai", @@ -87,6 +87,56 @@ describe("ProviderTransform.options - setCacheKey", () => { expect(result.promptCacheKey).toBe(sessionID) }) + test("should not set promptCacheKey for openai when explicitly disabled", () => { + const openaiModel = { + ...mockModel, + providerID: "openai", + api: { + id: "gpt-4", + url: "https://api.openai.com", + npm: "@ai-sdk/openai", + }, + } + const result = ProviderTransform.options({ + model: openaiModel, + sessionID, + providerOptions: { setCacheKey: false }, + }) + expect(result.promptCacheKey).toBeUndefined() + }) + + test("should set promptCacheKey for the xAI SDK by default regardless of provider ID", () => { + const xaiModel = { + ...mockModel, + providerID: "custom-xai", + api: { + id: "grok-4", + url: "https://api.x.ai", + npm: "@ai-sdk/xai", + }, + } + const result = ProviderTransform.options({ model: xaiModel, sessionID, providerOptions: {} }) + expect(result.promptCacheKey).toBe(sessionID) + }) + + test("should not set promptCacheKey for the xAI SDK when explicitly disabled", () => { + const xaiModel = { + ...mockModel, + providerID: "xai", + api: { + id: "grok-4", + url: "https://api.x.ai", + npm: "@ai-sdk/xai", + }, + } + const result = ProviderTransform.options({ + model: xaiModel, + sessionID, + providerOptions: { setCacheKey: false }, + }) + expect(result.promptCacheKey).toBeUndefined() + }) + test("should set store=false for openai provider", () => { const openaiModel = { ...mockModel, diff --git a/patches/@ai-sdk%2Fxai@3.0.102.patch b/patches/@ai-sdk%2Fxai@3.0.102.patch new file mode 100644 index 0000000000..27a46014fc --- /dev/null +++ b/patches/@ai-sdk%2Fxai@3.0.102.patch @@ -0,0 +1,174 @@ +diff --git a/dist/index.d.mts b/dist/index.d.mts +index 266c5ffdd9ee74ff95908ce90858ee4369d4e4ae..990ef4195bc67b6d25f249e1c81cf51710390f9a 100644 +--- a/dist/index.d.mts ++++ b/dist/index.d.mts +@@ -78,6 +78,7 @@ declare const xaiLanguageModelResponsesOptions: z.ZodObject<{ + topLogprobs: z.ZodOptional; + store: z.ZodOptional; + previousResponseId: z.ZodOptional; ++ promptCacheKey: z.ZodOptional; + include: z.ZodOptional>>>; +diff --git a/dist/index.d.ts b/dist/index.d.ts +index 266c5ffdd9ee74ff95908ce90858ee4369d4e4ae..990ef4195bc67b6d25f249e1c81cf51710390f9a 100644 +--- a/dist/index.d.ts ++++ b/dist/index.d.ts +@@ -78,6 +78,7 @@ declare const xaiLanguageModelResponsesOptions: z.ZodObject<{ + topLogprobs: z.ZodOptional; + store: z.ZodOptional; + previousResponseId: z.ZodOptional; ++ promptCacheKey: z.ZodOptional; + include: z.ZodOptional>>>; +diff --git a/dist/index.js b/dist/index.js +index 717b74538f5c8f0d6ab1475ebb2a84a47ccd3950..dd7dbeb3bc307e0d355f4bb4939d06cc8eae7528 100644 +--- a/dist/index.js ++++ b/dist/index.js +@@ -1119,6 +1119,14 @@ async function convertToXaiResponsesInput({ + type: "input_file", + file_url: block.data.toString() + }); ++ } else if (block.mediaType === "application/pdf") { ++ contentParts.push({ ++ type: "input_file", ++ ...typeof block.data === "string" && block.data.startsWith("file-") ? { file_id: block.data } : { ++ filename: block.filename ?? "file", ++ file_data: `data:application/pdf;base64,${(0, import_provider_utils5.convertToBase64)(block.data)}` ++ } ++ }); + } else { + throw new import_provider4.UnsupportedFunctionalityError({ + functionality: `file part media type ${block.mediaType} as inline data (xAI Responses requires a URL or a Files API reference for non-image files)` +@@ -1760,6 +1768,10 @@ var xaiLanguageModelResponsesOptions = import_v47.z.object({ + * The ID of the previous response from the model. + */ + previousResponseId: import_v47.z.string().optional(), ++ /** ++ * A stable key used by xAI to improve prompt cache routing. ++ */ ++ promptCacheKey: import_v47.z.string().optional(), + /** + * Specify additional output data to include in the model response. + * Example values: 'file_search_call.results'. +@@ -2165,6 +2177,9 @@ var XaiResponsesLanguageModel = class { + }, + ...options.previousResponseId != null && { + previous_response_id: options.previousResponseId ++ }, ++ ...options.promptCacheKey != null && { ++ prompt_cache_key: options.promptCacheKey + } + }; + if (xaiTools2 && xaiTools2.length > 0) { +diff --git a/dist/index.mjs b/dist/index.mjs +index a26af109585fc2bd3053b320142aa869c06d36f4..774adaf971b648544317a4fc65d0c56e488d4fc7 100644 +--- a/dist/index.mjs ++++ b/dist/index.mjs +@@ -1122,6 +1122,14 @@ async function convertToXaiResponsesInput({ + type: "input_file", + file_url: block.data.toString() + }); ++ } else if (block.mediaType === "application/pdf") { ++ contentParts.push({ ++ type: "input_file", ++ ...typeof block.data === "string" && block.data.startsWith("file-") ? { file_id: block.data } : { ++ filename: block.filename ?? "file", ++ file_data: `data:application/pdf;base64,${convertToBase642(block.data)}` ++ } ++ }); + } else { + throw new UnsupportedFunctionalityError3({ + functionality: `file part media type ${block.mediaType} as inline data (xAI Responses requires a URL or a Files API reference for non-image files)` +@@ -1763,6 +1771,10 @@ var xaiLanguageModelResponsesOptions = z7.object({ + * The ID of the previous response from the model. + */ + previousResponseId: z7.string().optional(), ++ /** ++ * A stable key used by xAI to improve prompt cache routing. ++ */ ++ promptCacheKey: z7.string().optional(), + /** + * Specify additional output data to include in the model response. + * Example values: 'file_search_call.results'. +@@ -2186,6 +2198,9 @@ var XaiResponsesLanguageModel = class { + }, + ...options.previousResponseId != null && { + previous_response_id: options.previousResponseId ++ }, ++ ...options.promptCacheKey != null && { ++ prompt_cache_key: options.promptCacheKey + } + }; + if (xaiTools2 && xaiTools2.length > 0) { +diff --git a/src/responses/convert-to-xai-responses-input.ts b/src/responses/convert-to-xai-responses-input.ts +index 9a3712990cf646a6f7ba757d3f2893a3c37c6ef9..b018a9b95eb21e6c12794e6bddb3d953f40441e5 100644 +--- a/src/responses/convert-to-xai-responses-input.ts ++++ b/src/responses/convert-to-xai-responses-input.ts +@@ -64,6 +64,17 @@ export async function convertToXaiResponsesInput({ + type: 'input_file', + file_url: block.data.toString(), + }); ++ } else if (block.mediaType === 'application/pdf') { ++ contentParts.push({ ++ type: 'input_file', ++ ...(typeof block.data === 'string' && ++ block.data.startsWith('file-') ++ ? { file_id: block.data } ++ : { ++ filename: block.filename ?? 'file', ++ file_data: `data:application/pdf;base64,${convertToBase64(block.data)}`, ++ }), ++ }); + } else { + throw new UnsupportedFunctionalityError({ + functionality: `file part media type ${block.mediaType} as inline data (xAI Responses requires a URL or a Files API reference for non-image files)`, +diff --git a/src/responses/xai-responses-api.ts b/src/responses/xai-responses-api.ts +index 70483ea4f7c340b84c23766ac1d1df21e052842b..4b4d052860caa019556151e9e46dbfda8dfb18a9 100644 +--- a/src/responses/xai-responses-api.ts ++++ b/src/responses/xai-responses-api.ts +@@ -27,7 +27,13 @@ export type XaiResponsesSystemMessage = { + export type XaiResponsesUserMessageContentPart = + | { type: 'input_text'; text: string } + | { type: 'input_image'; image_url: string } +- | { type: 'input_file'; file_url: string }; ++ | { ++ type: 'input_file'; ++ file_url?: string; ++ file_id?: string; ++ file_data?: string; ++ filename?: string; ++ }; + + export type XaiResponsesUserMessage = { + role: 'user'; +diff --git a/src/responses/xai-responses-language-model.ts b/src/responses/xai-responses-language-model.ts +index f90df62eb9a30154388b1390e9f3acc3ccc022bf..00e61cba6cf048ae0045be692f33cb7e1e6a1188 100644 +--- a/src/responses/xai-responses-language-model.ts ++++ b/src/responses/xai-responses-language-model.ts +@@ -182,6 +182,9 @@ export class XaiResponsesLanguageModel implements LanguageModelV3 { + ...(options.previousResponseId != null && { + previous_response_id: options.previousResponseId, + }), ++ ...(options.promptCacheKey != null && { ++ prompt_cache_key: options.promptCacheKey, ++ }), + }; + + if (xaiTools && xaiTools.length > 0) { +diff --git a/src/responses/xai-responses-options.ts b/src/responses/xai-responses-options.ts +index f8e96c061bf8793a402ababb8cad65bb2ad6aead..15c168892c1e8755453c61d3061e958cfd51ac71 100644 +--- a/src/responses/xai-responses-options.ts ++++ b/src/responses/xai-responses-options.ts +@@ -32,6 +32,10 @@ export const xaiLanguageModelResponsesOptions = z.object({ + * The ID of the previous response from the model. + */ + previousResponseId: z.string().optional(), ++ /** ++ * A stable key used by xAI to improve prompt cache routing. ++ */ ++ promptCacheKey: z.string().optional(), + /** + * Specify additional output data to include in the model response. + * Example values: 'file_search_call.results'. diff --git a/patches/@ai-sdk%2Fxai@3.0.82.patch b/patches/@ai-sdk%2Fxai@3.0.82.patch deleted file mode 100644 index dbe1207bcd..0000000000 --- a/patches/@ai-sdk%2Fxai@3.0.82.patch +++ /dev/null @@ -1,99 +0,0 @@ -diff --git a/dist/index.js b/dist/index.js -index 135b95946139bbd1fc4b62239032931586189da0..7913520ad2f1c26b0f9621e7654f5fb570cba926 100644 ---- a/dist/index.js -+++ b/dist/index.js -@@ -1077,6 +1077,20 @@ async function convertToXaiResponsesInput({ - const mediaType = block.mediaType === "image/*" ? "image/jpeg" : block.mediaType; - const imageUrl = block.data instanceof URL ? block.data.toString() : `data:${mediaType};base64,${(0, import_provider_utils5.convertToBase64)(block.data)}`; - contentParts.push({ type: "input_image", image_url: imageUrl }); -+ } else if (block.mediaType === "application/pdf") { -+ if (block.data instanceof URL) { -+ contentParts.push({ type: "input_file", file_url: block.data.toString() }); -+ } else { -+ contentParts.push({ -+ type: "input_file", -+ ...(typeof block.data === "string" && block.data.startsWith("file-") -+ ? { file_id: block.data } -+ : { -+ filename: block.filename ?? "file", -+ file_data: `data:application/pdf;base64,${(0, import_provider_utils5.convertToBase64)(block.data)}`, -+ }), -+ }); -+ } - } else { - throw new import_provider4.UnsupportedFunctionalityError({ - functionality: `file part media type ${block.mediaType}` -diff --git a/dist/index.mjs b/dist/index.mjs -index 61be60d452682b94dcdda39ffc47cb994eb8bfb1..d928f7f46e91057cd97fade9cc238db611345bcf 100644 ---- a/dist/index.mjs -+++ b/dist/index.mjs -@@ -1080,6 +1080,20 @@ async function convertToXaiResponsesInput({ - const mediaType = block.mediaType === "image/*" ? "image/jpeg" : block.mediaType; - const imageUrl = block.data instanceof URL ? block.data.toString() : `data:${mediaType};base64,${convertToBase642(block.data)}`; - contentParts.push({ type: "input_image", image_url: imageUrl }); -+ } else if (block.mediaType === "application/pdf") { -+ if (block.data instanceof URL) { -+ contentParts.push({ type: "input_file", file_url: block.data.toString() }); -+ } else { -+ contentParts.push({ -+ type: "input_file", -+ ...(typeof block.data === "string" && block.data.startsWith("file-") -+ ? { file_id: block.data } -+ : { -+ filename: block.filename ?? "file", -+ file_data: `data:application/pdf;base64,${convertToBase642(block.data)}`, -+ }), -+ }); -+ } - } else { - throw new UnsupportedFunctionalityError3({ - functionality: `file part media type ${block.mediaType}` -diff --git a/src/responses/convert-to-xai-responses-input.ts b/src/responses/convert-to-xai-responses-input.ts -index 19958d9fd90f7ce61bca70ad2d5f7b89a59fa63b..9329a1d56210af8aa33e5dbcb2916e24847070c1 100644 ---- a/src/responses/convert-to-xai-responses-input.ts -+++ b/src/responses/convert-to-xai-responses-input.ts -@@ -54,6 +54,24 @@ export async function convertToXaiResponsesInput({ - : `data:${mediaType};base64,${convertToBase64(block.data)}`; - - contentParts.push({ type: 'input_image', image_url: imageUrl }); -+ } else if (block.mediaType === 'application/pdf') { -+ if (block.data instanceof URL) { -+ contentParts.push({ -+ type: 'input_file', -+ file_url: block.data.toString(), -+ }); -+ } else { -+ contentParts.push({ -+ type: 'input_file', -+ ...(typeof block.data === 'string' && -+ block.data.startsWith('file-') -+ ? { file_id: block.data } -+ : { -+ filename: block.filename ?? 'file', -+ file_data: `data:application/pdf;base64,${convertToBase64(block.data)}`, -+ }), -+ }); -+ } - } else { - throw new UnsupportedFunctionalityError({ - functionality: `file part media type ${block.mediaType}`, -diff --git a/src/responses/xai-responses-api.ts b/src/responses/xai-responses-api.ts -index df24c42d29fe7fc1dd7649cc2c4712a68c3a536b..00195468a83d43c1bfb50854ea040b55ea352433 100644 ---- a/src/responses/xai-responses-api.ts -+++ b/src/responses/xai-responses-api.ts -@@ -26,7 +26,14 @@ export type XaiResponsesSystemMessage = { - - export type XaiResponsesUserMessageContentPart = - | { type: 'input_text'; text: string } -- | { type: 'input_image'; image_url: string }; -+ | { type: 'input_image'; image_url: string } -+ | { -+ type: 'input_file'; -+ file_url?: string; -+ file_id?: string; -+ file_data?: string; -+ filename?: string; -+ }; - - export type XaiResponsesUserMessage = { - role: 'user'; From 9644a008a45d9b832e274c6546eaae9c2d1b86ef Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" Date: Wed, 8 Jul 2026 22:31:09 +0000 Subject: [PATCH 02/34] chore: generate --- packages/opencode/src/provider/transform.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/opencode/src/provider/transform.ts b/packages/opencode/src/provider/transform.ts index e50f4b7688..8b5bcab611 100644 --- a/packages/opencode/src/provider/transform.ts +++ b/packages/opencode/src/provider/transform.ts @@ -1129,9 +1129,7 @@ export function options(input: { if ( input.providerOptions?.setCacheKey !== false && - (input.model.providerID === "openai" || - input.model.api.npm === "@ai-sdk/xai" || - input.providerOptions?.setCacheKey) + (input.model.providerID === "openai" || input.model.api.npm === "@ai-sdk/xai" || input.providerOptions?.setCacheKey) ) { result["promptCacheKey"] = input.sessionID } From fff6764ed4ae296740650fbb107f50ffff9c0437 Mon Sep 17 00:00:00 2001 From: David Hill <1879069+iamdavidhill@users.noreply.github.com> Date: Wed, 8 Jul 2026 23:48:11 +0100 Subject: [PATCH 03/34] fix(console): update square avatar previews (#35975) --- .../preview-opencode-logo-dark-square.png | Bin 1477 -> 128611 bytes .../preview-opencode-logo-light-square.png | Bin 1467 -> 92706 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/packages/console/app/src/asset/brand/preview-opencode-logo-dark-square.png b/packages/console/app/src/asset/brand/preview-opencode-logo-dark-square.png index 604ad7aa7a87c71d4a3972f18da4044e53f745fe..f30b4036ab035268972897f6fc82f04f8020831a 100644 GIT binary patch literal 128611 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