diff --git a/.changeset/grok-summarize-adapter-assignability.md b/.changeset/grok-summarize-adapter-assignability.md new file mode 100644 index 000000000..7f3a327fc --- /dev/null +++ b/.changeset/grok-summarize-adapter-assignability.md @@ -0,0 +1,16 @@ +--- +'@tanstack/ai-grok': patch +--- + +Fix `grokSummarize`/`createGrokSummarize` not being assignable to `summarize()`'s +`adapter` param for any current Grok model (`grok-4.3`, `grok-build-0.1`). + +`GrokTextProviderOptions` was declared as an `interface` extending +`Record`, giving it an explicit index signature. Under +`strictFunctionTypes`, the `SummarizeAdapter` constraint is checked +contravariantly, which requires `object` to be assignable to the provider +options — but `object` is not assignable to an index-signature type, so the +check failed (Grok-only; OpenAI's all-optional, no-index-signature options +passed). The options are now a type-alias intersection matching the OpenAI +shape, and the text adapter's provider-options constraint is widened to +`Record` to mirror `OpenAITextAdapter`. diff --git a/packages/ai-grok/src/adapters/text.ts b/packages/ai-grok/src/adapters/text.ts index 1e8e3fb15..366676a45 100644 --- a/packages/ai-grok/src/adapters/text.ts +++ b/packages/ai-grok/src/adapters/text.ts @@ -43,8 +43,11 @@ export type { ExternalTextProviderOptions as GrokTextProviderOptions } from '../ */ export class GrokTextAdapter< TModel extends (typeof GROK_CHAT_MODELS)[number], - TProviderOptions extends Record = - ResolveProviderOptions, + // Use `Record` (not `unknown`) to match the OpenAI text + // adapter: the resolved Grok provider options are a type-alias intersection + // with no explicit index signature, which is assignable to + // `Record` but not `Record`. See issue #821. + TProviderOptions extends Record = ResolveProviderOptions, TInputModalities extends ReadonlyArray = ResolveInputModalities, TToolCapabilities extends ReadonlyArray = diff --git a/packages/ai-grok/src/text/text-provider-options.ts b/packages/ai-grok/src/text/text-provider-options.ts index 9cc319df4..dd430ac96 100644 --- a/packages/ai-grok/src/text/text-provider-options.ts +++ b/packages/ai-grok/src/text/text-provider-options.ts @@ -29,11 +29,9 @@ export interface GrokBaseOptions { } /** - * Grok-specific provider options for text/chat - * Based on xAI Responses API options + * Sampling and response controls for Grok text/chat models. */ -export interface GrokTextProviderOptions - extends GrokBaseOptions, Record { +export interface GrokSamplingOptions { /** * Temperature for response generation (0-2) * Higher values make output more random, lower values more focused @@ -62,6 +60,21 @@ export interface GrokTextProviderOptions reasoning?: GrokReasoning } +/** + * Grok-specific provider options for text/chat + * Based on xAI Responses API options. + * + * Declared as a type-alias intersection of interfaces with all-optional props + * (matching the OpenAI text adapter), NOT an `interface ... extends + * Record`. An explicit index signature makes `object` + * un-assignable to these options, which breaks the contravariantly-checked + * `summarize()` adapter constraint (`SummarizeAdapter`); see + * issue #821. Without the index signature these options no longer satisfy a + * `Record` constraint, so the text adapter's provider-options + * generic is widened to `Record` to match OpenAI. + */ +export type GrokTextProviderOptions = GrokBaseOptions & GrokSamplingOptions + export type GrokBuildProviderOptions = Omit< GrokTextProviderOptions, 'reasoning' diff --git a/packages/ai-grok/tests/grok-adapter.test.ts b/packages/ai-grok/tests/grok-adapter.test.ts index 4b50cb898..72cbbfba7 100644 --- a/packages/ai-grok/tests/grok-adapter.test.ts +++ b/packages/ai-grok/tests/grok-adapter.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest' import { resolveDebugOption } from '@tanstack/ai/adapter-internals' -import { EventType } from '@tanstack/ai' +import { EventType, summarize } from '@tanstack/ai' import { createGrokText, grokText } from '../src/adapters/text' import { createGrokImage, grokImage } from '../src/adapters/image' import { createGrokSummarize, grokSummarize } from '../src/adapters/summarize' @@ -498,5 +498,18 @@ describe('Grok adapters', () => { 'XAI_API_KEY is required', ) }) + + it('grokSummarize is assignable to summarize() adapter param for every model (#821)', () => { + // Type-level regression guard: the SummarizeAdapter constraint only + // instantiates at the summarize() call site, so constructing the adapter + // (covered above) is not enough. This closure is type-checked but never + // executed — passing CI's test:types is the assertion. + const _typeCheck = () => { + void summarize({ adapter: grokSummarize('grok-4.3'), text: '' }) + void summarize({ adapter: grokSummarize('grok-build-0.1'), text: '' }) + } + + expect(_typeCheck).toBeInstanceOf(Function) + }) }) })