Summary
grokSummarize(...) (and createGrokSummarize(...)) is not assignable to summarize()'s adapter param for any current Grok model (grok-4.3, grok-build-0.1). This is a real type regression introduced by main's provider-options rewrite; it was never caught because the only call sites live in testing/**, which CI excludes from test:types (see #820).
Repro
import { summarize } from '@tanstack/ai'
import { grokSummarize } from '@tanstack/ai-grok/adapters'
summarize({ adapter: grokSummarize('grok-4.3'), text: 'x' }) // ❌ type error
summarize({ adapter: grokSummarize('grok-build-0.1'), text: 'x' }) // ❌ type error
Type 'ChatStreamSummarizeAdapter<"grok-4.3", GrokTextProviderOptions>' is not assignable to
'SummarizeAdapter<string, object>'.
Types of property 'summarize' are incompatible.
Type '(options: SummarizationOptions<GrokTextProviderOptions>) => Promise<SummarizationResult>'
is not assignable to '(options: SummarizationOptions<object>) => Promise<SummarizationResult>'.
... Type 'SummarizationOptions<object>' is not assignable to 'SummarizationOptions<GrokTextProviderOptions>'.
Type 'object' is not assignable to type 'GrokTextProviderOptions'.
OpenAI (openaiSummarize('gpt-5.2')) passes — confirming this is Grok-specific.
Root cause
SummarizeAdapter.summarize is declared as a property (arrow-function type), not a method shorthand (packages/ai/src/activities/summarize/adapter.ts:49). Under strictFunctionTypes, property-position function params are checked contravariantly. So assignability to the constraint SummarizeAdapter<string, object> (the bound on summarize<TAdapter extends SummarizeAdapter<string, object>>) reduces to:
is object assignable to TProviderOptions?
For Grok, TProviderOptions is GrokTextProviderOptions, which extends Record<string, unknown> (packages/ai-grok/src/text/text-provider-options.ts:35-36):
export interface GrokTextProviderOptions
extends GrokBaseOptions, Record<string, unknown> { ... }
object is not assignable to Record<string, unknown> (an index-signature type), so the check fails. grok-build-0.1 resolves to GrokBuildProviderOptions = Omit<GrokTextProviderOptions, 'reasoning'> & { reasoning?: never }, which retains the index signature and fails the same way.
OpenAI's options (ExternalTextProviderOptions) are a pure intersection of interfaces with all-optional named props and no index signature, and object is assignable to an all-optional object type — which is why OpenAI passes. So the index signature on the Grok options is exactly what breaks it; it is not present on the other providers.
Minimal isolation
const r1: Record<string, unknown> = {} as object // ❌ object not assignable to index-sig type
const r2: { a?: number } = {} as object // ✅ object assignable to all-optional type
Fix direction
Make Grok's options match the all-optional, no-index-signature shape the other providers use:
- Drop
extends ... Record<string, unknown> from GrokTextProviderOptions; keep only the named optional props.
- Re-check
GrokBuildProviderOptions's Omit<..., 'reasoning'> — with the index signature gone, Omit will preserve the named keys correctly instead of collapsing them into string.
Confirm afterward that per-model provider-options narrowing (the reasoning?: never distinction for grok-build-0.1) still holds.
Test coverage
- Add a call-site type assertion in
packages/ai-grok/tests/ (an included package) so this can never regress silently again:
summarize({ adapter: grokSummarize('grok-4.3'), text: '' })
summarize({ adapter: grokSummarize('grok-build-0.1'), text: '' })
Constructing the adapter (current coverage) is not enough — the constraint only instantiates at the summarize() call site.
Related
Summary
grokSummarize(...)(andcreateGrokSummarize(...)) is not assignable tosummarize()'sadapterparam for any current Grok model (grok-4.3,grok-build-0.1). This is a real type regression introduced bymain's provider-options rewrite; it was never caught because the only call sites live intesting/**, which CI excludes fromtest:types(see #820).Repro
OpenAI (
openaiSummarize('gpt-5.2')) passes — confirming this is Grok-specific.Root cause
SummarizeAdapter.summarizeis declared as a property (arrow-function type), not a method shorthand (packages/ai/src/activities/summarize/adapter.ts:49). UnderstrictFunctionTypes, property-position function params are checked contravariantly. So assignability to the constraintSummarizeAdapter<string, object>(the bound onsummarize<TAdapter extends SummarizeAdapter<string, object>>) reduces to:For Grok,
TProviderOptionsisGrokTextProviderOptions, which extendsRecord<string, unknown>(packages/ai-grok/src/text/text-provider-options.ts:35-36):objectis not assignable toRecord<string, unknown>(an index-signature type), so the check fails.grok-build-0.1resolves toGrokBuildProviderOptions = Omit<GrokTextProviderOptions, 'reasoning'> & { reasoning?: never }, which retains the index signature and fails the same way.OpenAI's options (
ExternalTextProviderOptions) are a pure intersection of interfaces with all-optional named props and no index signature, andobjectis assignable to an all-optional object type — which is why OpenAI passes. So the index signature on the Grok options is exactly what breaks it; it is not present on the other providers.Minimal isolation
Fix direction
Make Grok's options match the all-optional, no-index-signature shape the other providers use:
extends ... Record<string, unknown>fromGrokTextProviderOptions; keep only the named optional props.GrokBuildProviderOptions'sOmit<..., 'reasoning'>— with the index signature gone,Omitwill preserve the named keys correctly instead of collapsing them intostring.Confirm afterward that per-model provider-options narrowing (the
reasoning?: neverdistinction forgrok-build-0.1) still holds.Test coverage
packages/ai-grok/tests/(an included package) so this can never regress silently again:summarize()call site.Related
test:typesexcludesexamples/**andtesting/**).