mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 01:07:46 +00:00
DeepSeek V3.2 Thinking Mode Integration (#1134)
This commit is contained in:
@@ -19,12 +19,16 @@ const UNDERLINE_TAG_END_LENGTH = 4; // For "</u>"
|
||||
|
||||
interface RenderInlineProps {
|
||||
text: string;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
const RenderInlineInternal: React.FC<RenderInlineProps> = ({ text }) => {
|
||||
const RenderInlineInternal: React.FC<RenderInlineProps> = ({
|
||||
text,
|
||||
textColor = theme.text.primary,
|
||||
}) => {
|
||||
// Early return for plain text without markdown or URLs
|
||||
if (!/[*_~`<[https?:]/.test(text)) {
|
||||
return <Text color={theme.text.primary}>{text}</Text>;
|
||||
return <Text color={textColor}>{text}</Text>;
|
||||
}
|
||||
|
||||
const nodes: React.ReactNode[] = [];
|
||||
|
||||
@@ -17,6 +17,7 @@ interface MarkdownDisplayProps {
|
||||
isPending: boolean;
|
||||
availableTerminalHeight?: number;
|
||||
terminalWidth: number;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
// Constants for Markdown parsing and rendering
|
||||
@@ -31,6 +32,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
isPending,
|
||||
availableTerminalHeight,
|
||||
terminalWidth,
|
||||
textColor = theme.text.primary,
|
||||
}) => {
|
||||
if (!text) return <></>;
|
||||
|
||||
@@ -116,7 +118,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
addContentBlock(
|
||||
<Box key={key}>
|
||||
<Text wrap="wrap">
|
||||
<RenderInline text={line} />
|
||||
<RenderInline text={line} textColor={textColor} />
|
||||
</Text>
|
||||
</Box>,
|
||||
);
|
||||
@@ -155,7 +157,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
addContentBlock(
|
||||
<Box key={key}>
|
||||
<Text wrap="wrap">
|
||||
<RenderInline text={line} />
|
||||
<RenderInline text={line} textColor={textColor} />
|
||||
</Text>
|
||||
</Box>,
|
||||
);
|
||||
@@ -173,36 +175,36 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
switch (level) {
|
||||
case 1:
|
||||
headerNode = (
|
||||
<Text bold color={theme.text.link}>
|
||||
<RenderInline text={headerText} />
|
||||
<Text bold color={textColor}>
|
||||
<RenderInline text={headerText} textColor={textColor} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
headerNode = (
|
||||
<Text bold color={theme.text.link}>
|
||||
<RenderInline text={headerText} />
|
||||
<Text bold color={textColor}>
|
||||
<RenderInline text={headerText} textColor={textColor} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
case 3:
|
||||
headerNode = (
|
||||
<Text bold color={theme.text.primary}>
|
||||
<RenderInline text={headerText} />
|
||||
<Text bold color={textColor}>
|
||||
<RenderInline text={headerText} textColor={textColor} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
case 4:
|
||||
headerNode = (
|
||||
<Text italic color={theme.text.secondary}>
|
||||
<RenderInline text={headerText} />
|
||||
<Text italic color={textColor}>
|
||||
<RenderInline text={headerText} textColor={textColor} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
default:
|
||||
headerNode = (
|
||||
<Text color={theme.text.primary}>
|
||||
<RenderInline text={headerText} />
|
||||
<Text color={textColor}>
|
||||
<RenderInline text={headerText} textColor={textColor} />
|
||||
</Text>
|
||||
);
|
||||
break;
|
||||
@@ -219,6 +221,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
type="ul"
|
||||
marker={marker}
|
||||
leadingWhitespace={leadingWhitespace}
|
||||
textColor={textColor}
|
||||
/>,
|
||||
);
|
||||
} else if (olMatch) {
|
||||
@@ -232,6 +235,7 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
type="ol"
|
||||
marker={marker}
|
||||
leadingWhitespace={leadingWhitespace}
|
||||
textColor={textColor}
|
||||
/>,
|
||||
);
|
||||
} else {
|
||||
@@ -245,8 +249,8 @@ const MarkdownDisplayInternal: React.FC<MarkdownDisplayProps> = ({
|
||||
} else {
|
||||
addContentBlock(
|
||||
<Box key={key}>
|
||||
<Text wrap="wrap" color={theme.text.primary}>
|
||||
<RenderInline text={line} />
|
||||
<Text wrap="wrap" color={textColor}>
|
||||
<RenderInline text={line} textColor={textColor} />
|
||||
</Text>
|
||||
</Box>,
|
||||
);
|
||||
@@ -367,6 +371,7 @@ interface RenderListItemProps {
|
||||
type: 'ul' | 'ol';
|
||||
marker: string;
|
||||
leadingWhitespace?: string;
|
||||
textColor?: string;
|
||||
}
|
||||
|
||||
const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||
@@ -374,6 +379,7 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||
type,
|
||||
marker,
|
||||
leadingWhitespace = '',
|
||||
textColor = theme.text.primary,
|
||||
}) => {
|
||||
const prefix = type === 'ol' ? `${marker}. ` : `${marker} `;
|
||||
const prefixWidth = prefix.length;
|
||||
@@ -385,11 +391,11 @@ const RenderListItemInternal: React.FC<RenderListItemProps> = ({
|
||||
flexDirection="row"
|
||||
>
|
||||
<Box width={prefixWidth}>
|
||||
<Text color={theme.text.primary}>{prefix}</Text>
|
||||
<Text color={textColor}>{prefix}</Text>
|
||||
</Box>
|
||||
<Box flexGrow={LIST_ITEM_TEXT_FLEX_GROW}>
|
||||
<Text wrap="wrap" color={theme.text.primary}>
|
||||
<RenderInline text={itemText} />
|
||||
<Text wrap="wrap" color={textColor}>
|
||||
<RenderInline text={itemText} textColor={textColor} />
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@@ -102,7 +102,7 @@ describe('resumeHistoryUtils', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('marks tool results as error, skips thought text, and falls back when tool is missing', () => {
|
||||
it('marks tool results as error, captures thought text, and falls back when tool is missing', () => {
|
||||
const conversation = {
|
||||
messages: [
|
||||
{
|
||||
@@ -142,6 +142,11 @@ describe('resumeHistoryUtils', () => {
|
||||
const items = buildResumedHistoryItems(session, makeConfig({}));
|
||||
|
||||
expect(items).toEqual([
|
||||
{
|
||||
id: expect.any(Number),
|
||||
type: 'gemini_thought',
|
||||
text: 'should be skipped',
|
||||
},
|
||||
{ id: expect.any(Number), type: 'gemini', text: 'visible text' },
|
||||
{
|
||||
id: expect.any(Number),
|
||||
|
||||
@@ -17,7 +17,7 @@ import type { HistoryItem, HistoryItemWithoutId } from '../types.js';
|
||||
import { ToolCallStatus } from '../types.js';
|
||||
|
||||
/**
|
||||
* Extracts text content from a Content object's parts.
|
||||
* Extracts text content from a Content object's parts (excluding thought parts).
|
||||
*/
|
||||
function extractTextFromParts(parts: Part[] | undefined): string {
|
||||
if (!parts) return '';
|
||||
@@ -34,6 +34,22 @@ function extractTextFromParts(parts: Part[] | undefined): string {
|
||||
return textParts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts thought text content from a Content object's parts.
|
||||
* Thought parts are identified by having `thought: true`.
|
||||
*/
|
||||
function extractThoughtTextFromParts(parts: Part[] | undefined): string {
|
||||
if (!parts) return '';
|
||||
|
||||
const thoughtParts: string[] = [];
|
||||
for (const part of parts) {
|
||||
if ('text' in part && part.text && 'thought' in part && part.thought) {
|
||||
thoughtParts.push(part.text);
|
||||
}
|
||||
}
|
||||
return thoughtParts.join('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts function calls from a Content object's parts.
|
||||
*/
|
||||
@@ -187,12 +203,28 @@ function convertToHistoryItems(
|
||||
case 'assistant': {
|
||||
const parts = record.message?.parts as Part[] | undefined;
|
||||
|
||||
// Extract thought content
|
||||
const thoughtText = extractThoughtTextFromParts(parts);
|
||||
|
||||
// Extract text content (non-function-call, non-thought)
|
||||
const text = extractTextFromParts(parts);
|
||||
|
||||
// Extract function calls
|
||||
const functionCalls = extractFunctionCalls(parts);
|
||||
|
||||
// If there's thought content, add it as a gemini_thought message
|
||||
if (thoughtText) {
|
||||
// Flush any pending tool group before thought
|
||||
if (currentToolGroup.length > 0) {
|
||||
items.push({
|
||||
type: 'tool_group',
|
||||
tools: [...currentToolGroup],
|
||||
});
|
||||
currentToolGroup = [];
|
||||
}
|
||||
items.push({ type: 'gemini_thought', text: thoughtText });
|
||||
}
|
||||
|
||||
// If there's text content, add it as a gemini message
|
||||
if (text) {
|
||||
// Flush any pending tool group before text
|
||||
|
||||
Reference in New Issue
Block a user