Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
9 changes: 9 additions & 0 deletions .changeset/groq-summarize-adapter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@tanstack/ai-groq': minor
---

feat: add groqSummarize and createGroqSummarize adapters

Groq now exposes tree-shakeable summarize factories that wrap `GroqTextAdapter`
in `ChatStreamSummarizeAdapter`, matching the pattern used by OpenAI, Anthropic,
Gemini, Ollama, Grok, and OpenRouter.
30 changes: 30 additions & 0 deletions docs/adapters/groq.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,24 @@ const stream = chat({

> If you previously passed `temperature` / `topP` / `maxTokens` at the root of `chat()`, see [Moving Sampling Options into modelOptions](../migration/sampling-options-to-model-options).

## Summarization

Summarize long text content:

```typescript
import { summarize } from "@tanstack/ai";
import { groqSummarize } from "@tanstack/ai-groq";

const result = await summarize({
adapter: groqSummarize("llama-3.3-70b-versatile"),
text: "Your long text to summarize...",
maxLength: 100,
style: "concise", // "concise" | "bullet-points" | "paragraph"
});

console.log(result.summary);
```

### Reasoning

Enable reasoning for models that support it (e.g., `openai/gpt-oss-120b`, `qwen/qwen3-32b`). This allows the model to show its reasoning process, which is streamed as `thinking` chunks:
Expand Down Expand Up @@ -202,6 +220,18 @@ Creates a Groq chat adapter with an explicit API key.

**Returns:** A Groq chat adapter instance.

### `groqSummarize(model, config?)`

Creates a Groq summarization adapter using environment variables.

**Returns:** A Groq summarize adapter instance.

### `createGroqSummarize(model, apiKey, config?)`

Creates a Groq summarization adapter with an explicit API key.

**Returns:** A Groq summarize adapter instance.

## Limitations

- **Text-to-Speech**: Groq does not currently expose a TTS adapter. Use OpenAI, Gemini, ElevenLabs, or fal for speech generation.
Expand Down
3 changes: 2 additions & 1 deletion docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,8 @@
{
"label": "Groq",
"to": "adapters/groq",
"addedAt": "2026-04-15"
"addedAt": "2026-04-15",
"updatedAt": "2026-06-18"
Comment thread
harshlocham marked this conversation as resolved.
Outdated
},
{
"label": "ElevenLabs",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"test:build": "nx affected --target=test:build --exclude=examples/**,testing/**",
"test:types": "nx affected --targets=test:types --exclude=examples/**,testing/**",
"test:knip": "knip",
"test:docs": "node scripts/verify-links.ts",
"test:docs": "tsx scripts/verify-links.ts",
"test:kiira": "kiira check",
"test:react-native": "pnpm --filter @tanstack/ai-react-native-smoke smoke",
"test:e2e": "pnpm --filter @tanstack/ai-e2e test:e2e",
Expand Down Expand Up @@ -82,4 +82,4 @@
"vite": "^7.3.3",
"vitest": "^4.0.14"
}
}
}
1 change: 1 addition & 0 deletions packages/ai-groq/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const adapter = createGroqText('llama-3.3-70b-versatile', 'gsk_api_key')
- ✅ Structured output (JSON Schema)
- ✅ Function/tool calling
- ✅ Multimodal input (text + images for vision models)
- ✅ Summarization (`groqSummarize`)
- ❌ Embeddings (not supported by Groq)
- ❌ Image generation (not supported by Groq)

Expand Down
77 changes: 77 additions & 0 deletions packages/ai-groq/src/adapters/summarize.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { ChatStreamSummarizeAdapter } from '@tanstack/ai/adapters'
import { getGroqApiKeyFromEnv } from '../utils'
import { GroqTextAdapter } from './text'
import type { InferTextProviderOptions } from '@tanstack/ai/adapters'
import type { GROQ_CHAT_MODELS } from '../model-meta'
import type { GroqClientConfig } from '../utils'

/**
* Configuration for Groq summarize adapter
*/
export interface GroqSummarizeConfig extends GroqClientConfig {}

/** Model type for Groq summarization */
export type GroqSummarizeModel = (typeof GROQ_CHAT_MODELS)[number]

/**
* Creates a Groq summarize adapter with explicit API key.
* Type resolution happens here at the call site.
*
* @param model - The model name (e.g., 'llama-3.3-70b-versatile')
* @param apiKey - Your Groq API key
* @param config - Optional additional configuration
* @returns Configured Groq summarize adapter instance with resolved types
*
* @example
* ```typescript
* const adapter = createGroqSummarize('llama-3.3-70b-versatile', "gsk_...");
* ```
*/
export function createGroqSummarize<TModel extends GroqSummarizeModel>(
model: TModel,
apiKey: string,
config?: Omit<GroqSummarizeConfig, 'apiKey'>,
): ChatStreamSummarizeAdapter<
TModel,
InferTextProviderOptions<GroqTextAdapter<TModel>>
> {
return new ChatStreamSummarizeAdapter(
new GroqTextAdapter({ apiKey, ...config }, model),
model,
'groq',
)
}

/**
* Creates a Groq summarize adapter with automatic API key detection from environment variables.
* Type resolution happens here at the call site.
*
* Looks for `GROQ_API_KEY` in:
* - `process.env` (Node.js)
* - `window.env` (Browser with injected env)
*
* @param model - The model name (e.g., 'llama-3.3-70b-versatile')
* @param config - Optional configuration (excluding apiKey which is auto-detected)
* @returns Configured Groq summarize adapter instance with resolved types
* @throws Error if GROQ_API_KEY is not found in environment
*
* @example
* ```typescript
* // Automatically uses GROQ_API_KEY from environment
* const adapter = groqSummarize('llama-3.3-70b-versatile');
*
* await summarize({
* adapter,
* text: "Long article text..."
* });
* ```
*/
export function groqSummarize<TModel extends GroqSummarizeModel>(
model: TModel,
config?: Omit<GroqSummarizeConfig, 'apiKey'>,
): ChatStreamSummarizeAdapter<
TModel,
InferTextProviderOptions<GroqTextAdapter<TModel>>
> {
return createGroqSummarize(model, getGroqApiKeyFromEnv(), config)
}
8 changes: 8 additions & 0 deletions packages/ai-groq/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export {
type GroqTextProviderOptions,
} from './adapters/text'

// Summarize - thin factory functions over @tanstack/ai's ChatStreamSummarizeAdapter
export {
createGroqSummarize,
groqSummarize,
type GroqSummarizeConfig,
type GroqSummarizeModel,
} from './adapters/summarize'

// Types
export type {
GroqChatModelProviderOptionsByName,
Expand Down
35 changes: 35 additions & 0 deletions packages/ai-groq/tests/groq-adapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import {
createGroqText as _realCreateGroqText,
groqText as _realGroqText,
} from '../src/adapters/text'
import {
createGroqSummarize,
groqSummarize,
} from '../src/adapters/summarize'
import { EventType } from '@tanstack/ai'
import type { StreamChunk, Tool } from '@tanstack/ai'
import type { GroqTextProviderOptions } from '../src/index'
Expand Down Expand Up @@ -188,6 +192,37 @@ describe('Groq adapters', () => {
})
})
})

describe('Summarize adapter', () => {
it('creates a summarize adapter with explicit API key', () => {
const adapter = createGroqSummarize(
'llama-3.3-70b-versatile',
'test-api-key',
)

expect(adapter).toBeDefined()
expect(adapter.kind).toBe('summarize')
expect(adapter.name).toBe('groq')
expect(adapter.model).toBe('llama-3.3-70b-versatile')
})

it('creates a summarize adapter from environment variable', () => {
vi.stubEnv('GROQ_API_KEY', 'env-api-key')

const adapter = groqSummarize('llama-3.3-70b-versatile')

expect(adapter).toBeDefined()
expect(adapter.kind).toBe('summarize')
})

it('throws if GROQ_API_KEY is not set when using groqSummarize', () => {
vi.stubEnv('GROQ_API_KEY', '')

expect(() => groqSummarize('llama-3.3-70b-versatile')).toThrow(
'GROQ_API_KEY is required',
)
})
})
})

describe('Groq AG-UI event emission', () => {
Expand Down
20 changes: 20 additions & 0 deletions packages/ai/tests/summarize-max-length.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,26 @@ describe('ChatStreamSummarizeAdapter — maxLength reaches the wrapped adapter u
expect(opts?.['maxTokens']).toBeUndefined()
})

it('Groq adapter receives maxLength as max_completion_tokens', async () => {
const { textAdapter, lastModelOptions } = createRecordingTextAdapter()
const adapter = new ChatStreamSummarizeAdapter(
textAdapter,
'llama-3.3-70b-versatile',
'groq',
)

await adapter.summarize({
model: 'llama-3.3-70b-versatile',
text: 'hi',
maxLength: 256,
logger,
})

const opts = lastModelOptions()
expect(opts?.['max_completion_tokens']).toBe(256)
expect(opts?.['maxTokens']).toBeUndefined()
})

it('Ollama adapter receives maxLength AND the temperature default nested under options', async () => {
const { textAdapter, lastModelOptions } = createRecordingTextAdapter()
const adapter = new ChatStreamSummarizeAdapter(
Expand Down
2 changes: 2 additions & 0 deletions testing/e2e/src/lib/feature-support.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ export const matrix: Record<Feature, Set<Provider>> = {
'anthropic',
'gemini',
'ollama',
'groq',
'grok',
'openrouter',
]),
Expand All @@ -173,6 +174,7 @@ export const matrix: Record<Feature, Set<Provider>> = {
'anthropic',
'gemini',
'ollama',
'groq',
'grok',
'openrouter',
]),
Expand Down
6 changes: 6 additions & 0 deletions testing/e2e/src/routes/api.summarize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createOpenaiSummarize } from '@tanstack/ai-openai'
import { createAnthropicSummarize } from '@tanstack/ai-anthropic'
import { createGeminiSummarize } from '@tanstack/ai-gemini'
import { createOllamaSummarize } from '@tanstack/ai-ollama'
import { createGroqSummarize } from '@tanstack/ai-groq'
import { createGrokSummarize } from '@tanstack/ai-grok'
import { createOpenRouterSummarize } from '@tanstack/ai-openrouter'
import type { Provider } from '@/lib/types'
Expand Down Expand Up @@ -46,6 +47,11 @@ function createSummarizeAdapter(
httpOptions: { baseUrl: llmockBase(aimockPort), headers },
}),
ollama: () => createOllamaSummarize('mistral', llmockBase(aimockPort)),
groq: () =>
createGroqSummarize('llama-3.3-70b-versatile', DUMMY_KEY, {
baseURL: llmockBase(aimockPort),
defaultHeaders: headers,
}),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
grok: () =>
createGrokSummarize('grok-build-0.1', DUMMY_KEY, {
baseURL: openaiUrl(aimockPort),
Expand Down