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';