Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 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
5 changes: 5 additions & 0 deletions .changeset/strong-pianos-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@ai-sdk/xai': patch
---

feat: xai server-side tool calling
171 changes: 171 additions & 0 deletions content/providers/01-ai-sdk-providers/01-xai.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ first argument is the model id, e.g. `grok-3`.
const model = xai('grok-3');
```

By default, `xai(modelId)` uses the Chat API. To use the Responses API with server-side agentic tools, explicitly use `xai.responses(modelId)`.

### Example

You can use xAI language models to generate text with the `generateText` function:
Expand Down Expand Up @@ -124,6 +126,175 @@ The following optional provider options are available for xAI chat models:

Reasoning effort for reasoning models. Only supported by `grok-3-mini` and `grok-3-mini-fast` models.

## Responses API (Agentic Tools)

You can use the xAI Responses API with the `xai.responses(modelId)` factory method for server-side agentic tool calling. This enables the model to autonomously orchestrate tool calls and research on xAI's servers.

```ts
const model = xai.responses('grok-4-fast');
```

The Responses API provides server-side tools that the model can autonomously execute during its reasoning process:

- **web_search**: Real-time web search and page browsing
- **x_search**: Search X (Twitter) posts, users, and threads
- **code_execution**: Execute Python code for calculations and data analysis

### Web Search Tool

The web search tool enables autonomous web research with optional domain filtering and image understanding:

```ts
import { xai } from '@ai-sdk/xai';
import { generateText } from 'ai';

const { text, sources } = await generateText({
model: xai.responses('grok-4-fast'),
prompt: 'What are the latest developments in AI?',
tools: {
web_search: xai.tools.webSearch({
allowedDomains: ['arxiv.org', 'openai.com'],
enableImageUnderstanding: true,
}),
},
});

console.log(text);
console.log('Citations:', sources);
```

#### Web Search Parameters

- **allowedDomains** _string[]_

Only search within specified domains (max 5). Cannot be used with `excludedDomains`.

- **excludedDomains** _string[]_

Exclude specified domains from search (max 5). Cannot be used with `allowedDomains`.

- **enableImageUnderstanding** _boolean_

Enable the model to view and analyze images found during search. Increases token usage.

### X Search Tool

The X search tool enables searching X (Twitter) for posts, with filtering by handles and date ranges:

```ts
const { text, sources } = await generateText({
model: xai.responses('grok-4-fast'),
prompt: 'What are people saying about AI on X this week?',
tools: {
x_search: xai.tools.xSearch({
allowedXHandles: ['elonmusk', 'xai'],
fromDate: '2025-10-23',
toDate: '2025-10-30',
enableImageUnderstanding: true,
enableVideoUnderstanding: true,
}),
},
});
```

#### X Search Parameters

- **allowedXHandles** _string[]_

Only search posts from specified X handles (max 10). Cannot be used with `excludedXHandles`.

- **excludedXHandles** _string[]_

Exclude posts from specified X handles (max 10). Cannot be used with `allowedXHandles`.

- **fromDate** _string_

Start date for posts in ISO8601 format (`YYYY-MM-DD`).

- **toDate** _string_

End date for posts in ISO8601 format (`YYYY-MM-DD`).

- **enableImageUnderstanding** _boolean_

Enable the model to view and analyze images in X posts.

- **enableVideoUnderstanding** _boolean_

Enable the model to view and analyze videos in X posts.

### Code Execution Tool

The code execution tool enables the model to write and execute Python code for calculations and data analysis:

```ts
const { text } = await generateText({
model: xai.responses('grok-4-fast'),
prompt:
'Calculate the compound interest for $10,000 at 5% annually for 10 years',
tools: {
code_execution: xai.tools.codeExecution(),
},
});
```

### Multiple Tools

You can combine multiple server-side tools for comprehensive research:

```ts
import { xai } from '@ai-sdk/xai';
import { streamText } from 'ai';

const { fullStream } = streamText({
model: xai.responses('grok-4-fast'),
prompt: 'Research AI safety developments and calculate risk metrics',
tools: {
web_search: xai.tools.webSearch(),
x_search: xai.tools.xSearch(),
code_execution: xai.tools.codeExecution(),
},
});

for await (const part of fullStream) {
if (part.type === 'text-delta') {
process.stdout.write(part.text);
} else if (part.type === 'source' && part.sourceType === 'url') {
console.log('\nSource:', part.url);
}
}
```

### Provider Options

The Responses API supports the following provider options:

```ts
import { xai } from '@ai-sdk/xai';
import { generateText } from 'ai';

const result = await generateText({
model: xai.responses('grok-4-fast'),
providerOptions: {
xai: {
reasoningEffort: 'high',
},
},
// ...
});
```

The following provider options are available:

- **reasoningEffort** _'low' | 'high'_

Control the reasoning effort for the model. Higher effort may produce more thorough results at the cost of increased latency and token usage.

<Note>
The Responses API only supports server-side tools. You cannot mix server-side
tools with client-side function tools in the same request.
</Note>

## Live Search

xAI models support Live Search functionality, allowing them to query real-time data from various sources and include it in responses with citations.
Expand Down
23 changes: 23 additions & 0 deletions examples/ai-core/src/agent/xai-agent-research.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { xai } from '@ai-sdk/xai';
import { ToolLoopAgent } from 'ai';
import { run } from '../lib/run';

const agent = new ToolLoopAgent({
model: xai.responses('grok-4-fast'),
instructions: 'you are a helpful research assistant',
tools: {
web_search: xai.tools.webSearch(),
x_search: xai.tools.xSearch(),
code_execution: xai.tools.codeExecution(),
},
});

run(async () => {
const result = await agent.stream({
prompt: 'research prompt caching in llms and explain how it reduces costs',
});

for await (const textPart of result.textStream) {
process.stdout.write(textPart);
}
});
39 changes: 39 additions & 0 deletions examples/ai-core/src/generate-text/xai-responses-web-search.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { xai } from '@ai-sdk/xai';
import { generateText } from 'ai';
import 'dotenv/config';

async function main() {
const result = await generateText({
model: xai.responses('grok-4-fast'),
tools: {
web_search: xai.tools.webSearch(),
},
prompt:
'What are the latest developments in AI from the past week? Search and summarize.',
});

console.log('Text:', result.text);
console.log();
console.log('Tool calls made:');
for (const content of result.content) {
if (content.type === 'tool-call') {
console.log(
` - ${content.toolName} (${content.providerExecuted ? 'server-side' : 'client-side'})`,
);
}
}

console.log();
console.log('Sources cited:');
for (const content of result.content) {
if (content.type === 'source' && content.sourceType === 'url') {
console.log(` - ${content.url}`);
}
}

console.log();
console.log('Finish reason:', result.finishReason);
console.log('Usage:', result.usage);
}

main().catch(console.error);
34 changes: 34 additions & 0 deletions examples/ai-core/src/stream-text/xai-responses-tools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { xai } from '@ai-sdk/xai';
import { streamText } from 'ai';
import 'dotenv/config';

async function main() {
const { fullStream } = streamText({
model: xai.responses('grok-4-fast'),
tools: {
web_search: xai.tools.webSearch(),
x_search: xai.tools.xSearch(),
code_execution: xai.tools.codeExecution(),
},
prompt: 'Can you research about Vercel AI Gateway?',
});

let toolCallCount = 0;

for await (const event of fullStream) {
if (event.type === 'tool-call') {
toolCallCount++;
console.log(
`\n[Tool Call ${toolCallCount}] ${event.toolName}${event.providerExecuted ? ' (server-side)' : ' (client)'}`,
);
} else if (event.type === 'text-delta') {
process.stdout.write(event.text);
} else if (event.type === 'source' && event.sourceType === 'url') {
console.log(`\n[Citation] ${event.url}`);
}
}

console.log('\n');
}

main().catch(console.error);
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { xai } from '@ai-sdk/xai';
import { streamText } from 'ai';
import 'dotenv/config';

async function main() {
const { fullStream } = streamText({
model: xai.responses('grok-4-fast'),
tools: {
web_search: xai.tools.webSearch({
allowedDomains: ['x.ai'],
enableImageUnderstanding: true,
}),
},
prompt:
'search x.ai website and describe any images you find on the homepage',
});

console.log('searching x.ai with image understanding...\n');

for await (const part of fullStream) {
switch (part.type) {
case 'tool-call':
if (part.providerExecuted) {
console.log(`[tool: ${part.toolName}]`);
}
break;

case 'text-delta':
process.stdout.write(part.text);
break;

case 'source':
if (part.sourceType === 'url') {
console.log(`\n[source: ${part.url}]`);
}
break;
}
}

console.log('\n');
}

main().catch(console.error);
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { xai } from '@ai-sdk/xai';
import { streamText } from 'ai';
import 'dotenv/config';

async function main() {
const { fullStream } = streamText({
model: xai.responses('grok-4-fast'),
tools: {
x_search: xai.tools.xSearch({
allowedXHandles: ['xai', 'elonmusk'],
enableImageUnderstanding: true,
enableVideoUnderstanding: true,
}),
},
prompt:
'what are the latest videos and images from xai showing their products or announcements',
});

console.log('searching x for videos and images from xai...\n');

for await (const part of fullStream) {
switch (part.type) {
case 'tool-call':
if (part.providerExecuted) {
console.log(`[tool: ${part.toolName}]`);
}
break;

case 'text-delta':
process.stdout.write(part.text);
break;

case 'source':
if (part.sourceType === 'url') {
console.log(`\n[source: ${part.url}]`);
}
break;
}
}

console.log('\n');
}

main().catch(console.error);
19 changes: 19 additions & 0 deletions examples/next-openai/agent/xai-web-search-agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { xai } from '@ai-sdk/xai';
import { ToolLoopAgent, InferAgentUIMessage } from 'ai';

export const xaiWebSearchAgent = new ToolLoopAgent({
model: xai.responses('grok-4-fast'),
tools: {
web_search: xai.tools.webSearch({
enableImageUnderstanding: true,
}),
x_search: xai.tools.xSearch({
enableImageUnderstanding: true,
}),
},
onStepFinish: ({ request }) => {
console.dir(request.body, { depth: Infinity });
},
});

export type XaiWebSearchMessage = InferAgentUIMessage<typeof xaiWebSearchAgent>;
Loading
Loading