Skip to content

Commit 92f5f36

Browse files
authored
feat (core): add extractReasoningMiddleware (#4541)
1 parent 3e65901 commit 92f5f36

File tree

11 files changed

+616
-0
lines changed

11 files changed

+616
-0
lines changed

.changeset/itchy-pumpkins-punch.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'ai': patch
3+
---
4+
5+
feat (core): add extractReasoningMiddleware

content/docs/03-ai-sdk-core/45-middleware.mdx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,29 @@ const result = streamText({
4040
});
4141
```
4242

43+
## Built-in Middleware
44+
45+
### Extract Reasoning
46+
47+
Some providers and models expose reasoning information in the generated text using special tags,
48+
e.g. <think> and </think>.
49+
50+
The `extractReasoningMiddleware` function can be used to extract this reasoning information and expose it as a `reasoning` property on the result.
51+
52+
```ts
53+
import {
54+
experimental_wrapLanguageModel as wrapLanguageModel,
55+
extractReasoningMiddleware,
56+
} from 'ai';
57+
58+
const model = wrapLanguageModel({
59+
model: yourModel,
60+
middleware: extractReasoningMiddleware({ tagName: 'think' }),
61+
});
62+
```
63+
64+
You can then use that enhanced model in functions like `generateText` and `streamText`.
65+
4366
## Implementing Language Model Middleware
4467

4568
<Note>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
title: extractReasoningMiddleware
3+
description: Middleware that extracts XML-tagged reasoning sections from generated text
4+
---
5+
6+
# `extractReasoningMiddleware()`
7+
8+
`extractReasoningMiddleware` is a middleware function that extracts XML-tagged reasoning sections from generated text and exposes them separately from the main text content. This is particularly useful when you want to separate an AI model's reasoning process from its final output.
9+
10+
```ts
11+
import { extractReasoningMiddleware } from 'ai';
12+
13+
const middleware = extractReasoningMiddleware({
14+
tagName: 'reasoning',
15+
separator: '\n',
16+
});
17+
```
18+
19+
## Import
20+
21+
<Snippet
22+
text={`import { extractReasoningMiddleware } from "ai"`}
23+
prompt={false}
24+
/>
25+
26+
## API Signature
27+
28+
### Parameters
29+
30+
<PropertiesTable
31+
content={[
32+
{
33+
name: 'tagName',
34+
type: 'string',
35+
isOptional: false,
36+
description:
37+
'The name of the XML tag to extract reasoning from (without angle brackets)',
38+
},
39+
{
40+
name: 'separator',
41+
type: 'string',
42+
isOptional: true,
43+
description:
44+
'The separator to use between reasoning and text sections. Defaults to "\\n"',
45+
},
46+
]}
47+
/>
48+
49+
### Returns
50+
51+
Returns a middleware object that:
52+
53+
- Processes both streaming and non-streaming responses
54+
- Extracts content between specified XML tags as reasoning
55+
- Removes the XML tags and reasoning from the main text
56+
- Adds a `reasoning` property to the result containing the extracted content
57+
- Maintains proper separation between text sections using the specified separator
58+
59+
### Type Parameters
60+
61+
The middleware works with the `LanguageModelV1StreamPart` type for streaming responses.

content/docs/07-reference/01-ai-sdk-core/index.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ It also contains the following helper functions:
8787
'Creates a ReadableStream that emits values with configurable delays.',
8888
href: '/docs/reference/ai-sdk-core/simulate-readable-stream',
8989
},
90+
{
91+
title: 'wrapLanguageModel()',
92+
description: 'Wraps a language model with middleware.',
93+
href: '/docs/reference/ai-sdk-core/wrap-language-model',
94+
},
95+
{
96+
title: 'extractReasoningMiddleware()',
97+
description:
98+
'Extracts reasoning from the generated text and exposes it as a `reasoning` property on the result.',
99+
href: '/docs/reference/ai-sdk-core/extract-reasoning-middleware',
100+
},
90101
{
91102
title: 'smoothStream()',
92103
description: 'Smooths text streaming output.',
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { groq } from '@ai-sdk/groq';
2+
import {
3+
experimental_wrapLanguageModel,
4+
extractReasoningMiddleware,
5+
streamText,
6+
} from 'ai';
7+
import 'dotenv/config';
8+
9+
async function main() {
10+
const result = streamText({
11+
model: experimental_wrapLanguageModel({
12+
model: groq('deepseek-r1-distill-llama-70b'),
13+
middleware: extractReasoningMiddleware({ tagName: 'think' }),
14+
}),
15+
prompt: 'Invent a new holiday and describe its traditions.',
16+
});
17+
18+
let enteredReasoning = false;
19+
let enteredText = false;
20+
for await (const part of result.fullStream) {
21+
if (part.type === 'reasoning') {
22+
if (!enteredReasoning) {
23+
enteredReasoning = true;
24+
console.log('\nSTREAMING REASONING:\n');
25+
}
26+
process.stdout.write(part.textDelta);
27+
} else if (part.type === 'text-delta') {
28+
if (!enteredText) {
29+
enteredText = true;
30+
console.log('\nSTREAMING TEXT:\n');
31+
}
32+
process.stdout.write(part.textDelta);
33+
}
34+
}
35+
36+
console.log();
37+
console.log('\nFINAL REASONING:\n', await result.reasoning);
38+
console.log('\nFINAL TEXT:\n', await result.text);
39+
40+
console.log();
41+
console.log('Token usage:', await result.usage);
42+
console.log('Finish reason:', await result.finishReason);
43+
}
44+
45+
main().catch(console.error);

0 commit comments

Comments
 (0)