Skip to content

Commit d4eaec1

Browse files
committed
feat: support chat reasoning
1 parent 76ef79f commit d4eaec1

5 files changed

Lines changed: 44 additions & 3 deletions

File tree

packages/ai-native/src/browser/chat/chat-model.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
IChatComponent,
77
IChatMarkdownContent,
88
IChatProgress,
9+
IChatReasoning,
910
IChatToolContent,
1011
IChatTreeData,
1112
uuid,
@@ -33,7 +34,8 @@ export type IChatProgressResponseContent =
3334
| IChatAsyncContent
3435
| IChatTreeData
3536
| IChatComponent
36-
| IChatToolContent;
37+
| IChatToolContent
38+
| IChatReasoning;
3739

3840
export class ChatResponseModel extends Disposable {
3941
#responseParts: IChatProgressResponseContent[] = [];
@@ -131,6 +133,18 @@ export class ChatResponseModel extends Disposable {
131133
};
132134
}
133135

136+
this.#updateResponseText();
137+
} else if (progress.kind === 'reasoning') {
138+
const lastResponsePart = this.#responseParts[responsePartLength];
139+
if (!lastResponsePart || lastResponsePart.kind !== 'reasoning') {
140+
// 去掉开头的 <think> 标签
141+
this.#responseParts.push({ content: progress.content.replace(/^<think>/, ''), kind: 'reasoning' });
142+
} else {
143+
this.#responseParts[responsePartLength] = {
144+
content: lastResponsePart.content + progress.content,
145+
kind: 'reasoning',
146+
};
147+
}
134148
this.#updateResponseText();
135149
} else if (progress.kind === 'asyncContent') {
136150
// Add a new resolving part
@@ -181,6 +195,9 @@ export class ChatResponseModel extends Disposable {
181195
if (part.kind === 'toolCall') {
182196
return part.content.function.name;
183197
}
198+
if (part.kind === 'reasoning') {
199+
return '';
200+
}
184201
return part.content.value;
185202
})
186203
.join('\n\n');
@@ -387,7 +404,7 @@ export class ChatModel extends Disposable implements IChatModel {
387404

388405
const { kind } = progress;
389406

390-
const basicKind = ['content', 'markdownContent', 'asyncContent', 'treeData', 'component', 'toolCall'];
407+
const basicKind = ['content', 'markdownContent', 'asyncContent', 'treeData', 'component', 'toolCall', 'reasoning'];
391408

392409
if (basicKind.includes(kind)) {
393410
request.response.updateContent(progress, quiet);

packages/ai-native/src/browser/components/ChatReply.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import cls from 'classnames';
12
import React, {
23
Fragment,
34
ReactNode,
@@ -313,6 +314,12 @@ export const ChatReply = (props: IChatReplyProps) => {
313314
node = <ComponentRender component={item.component} value={item.value} messageId={msgId} />;
314315
} else if (item.kind === 'toolCall') {
315316
node = <ToolCallRender toolCall={item.content} messageId={msgId} />;
317+
} else if (item.kind === 'reasoning') {
318+
node = (
319+
<div className={cls(styles.reasoning, { [styles.collapse]: request.response.isComplete })}>
320+
{item.content}
321+
</div>
322+
);
316323
} else {
317324
node = renderMarkdown(item.content);
318325
}

packages/ai-native/src/browser/components/components.module.less

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -541,3 +541,9 @@
541541
}
542542
}
543543
}
544+
545+
.reasoning {
546+
padding-left: 12px;
547+
border-left: 2px solid var(--design-text-foreground);
548+
color: var(--design-text-foreground);
549+
}

packages/ai-native/src/node/base-language-model.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,11 @@ export abstract class BaseLanguageModel {
185185
});
186186
} else if (chunk.type === 'error') {
187187
chatReadableStream.emitError(new Error(chunk.error as string));
188+
} else if (chunk.type === 'reasoning') {
189+
chatReadableStream.emitData({
190+
kind: 'reasoning',
191+
content: chunk.textDelta,
192+
});
188193
}
189194
}
190195

packages/core-common/src/types/ai-native/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,13 +401,19 @@ export interface IChatComponent {
401401
kind: 'component';
402402
}
403403

404+
export interface IChatReasoning {
405+
content: string;
406+
kind: 'reasoning';
407+
}
408+
404409
export type IChatProgress =
405410
| IChatContent
406411
| IChatMarkdownContent
407412
| IChatAsyncContent
408413
| IChatTreeData
409414
| IChatComponent
410-
| IChatToolContent;
415+
| IChatToolContent
416+
| IChatReasoning;
411417

412418
export interface IChatMessage {
413419
role: ChatMessageRole;

0 commit comments

Comments
 (0)