Skip to content

Commit 7949f9a

Browse files
committed
Merge branch 'anthropic_process_thinking_blocks2' into bedrock_reasoning_2
2 parents 83aa729 + e9a3f97 commit 7949f9a

File tree

2 files changed

+103
-25
lines changed

2 files changed

+103
-25
lines changed

gui/src/components/mainInput/ThinkingBlockPeek.tsx

Lines changed: 102 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,71 @@
11
// src/components/ThinkingBlockPeek.tsx
2-
import {
3-
ChevronDownIcon,
4-
ChevronRightIcon,
5-
LightBulbIcon,
6-
} from "@heroicons/react/24/outline";
2+
import { ChevronDownIcon } from "@heroicons/react/24/outline";
3+
import { ChevronUpIcon } from "@heroicons/react/24/solid";
74
import { ChatHistoryItem } from "core";
8-
import { useState } from "react";
5+
import { useEffect, useState } from "react";
96
import styled from "styled-components";
10-
import { lightGray, vscBackground } from "..";
7+
import { defaultBorderRadius, lightGray, vscBackground } from "..";
8+
import { getFontSize } from "../../util";
119
import StyledMarkdownPreview from "../markdown/StyledMarkdownPreview";
1210

11+
const SpoilerButton = styled.div`
12+
background-color: ${vscBackground};
13+
width: fit-content;
14+
margin: 8px 6px 0px 2px;
15+
font-size: ${getFontSize() - 2}px;
16+
border: 0.5px solid ${lightGray};
17+
border-radius: ${defaultBorderRadius};
18+
padding: 4px 8px;
19+
color: ${lightGray};
20+
cursor: pointer;
21+
box-shadow:
22+
0 4px 6px rgba(0, 0, 0, 0.1),
23+
0 1px 3px rgba(0, 0, 0, 0.08);
24+
transition: box-shadow 0.3s ease;
25+
26+
&:hover {
27+
box-shadow:
28+
0 6px 8px rgba(0, 0, 0, 0.15),
29+
0 3px 6px rgba(0, 0, 0, 0.1);
30+
}
31+
`;
32+
33+
const ButtonContent = styled.div`
34+
display: flex;
35+
align-items: center;
36+
gap: 6px;
37+
`;
38+
39+
export const ThinkingText = styled.span`
40+
position: relative;
41+
padding-right: 12px;
42+
43+
&:after {
44+
content: "...";
45+
position: absolute;
46+
animation: ellipsis 1s steps(4, end) infinite;
47+
width: 0px;
48+
display: inline-block;
49+
overflow: hidden;
50+
}
51+
52+
@keyframes ellipsis {
53+
0%,
54+
100% {
55+
width: 0px;
56+
}
57+
33% {
58+
width: 8px;
59+
}
60+
66% {
61+
width: 16px;
62+
}
63+
90% {
64+
width: 24px;
65+
}
66+
}
67+
`;
68+
1369
const MarkdownWrapper = styled.div`
1470
& > div > *:first-child {
1571
margin-top: 0 !important;
@@ -21,48 +77,69 @@ interface ThinkingBlockPeekProps {
2177
redactedThinking?: string;
2278
index: number;
2379
prevItem: ChatHistoryItem | null;
80+
inProgress?: boolean;
2481
signature?: string;
82+
tokens?: number;
2583
}
2684

2785
function ThinkingBlockPeek({
2886
content,
2987
redactedThinking,
3088
index,
3189
prevItem,
90+
inProgress,
3291
signature,
92+
tokens,
3393
}: ThinkingBlockPeekProps) {
3494
const [open, setOpen] = useState(false);
95+
const [startTime, setStartTime] = useState<number | null>(null);
96+
const [elapsedTime, setElapsedTime] = useState<string>("");
3597

3698
const duplicateRedactedThinkingBlock =
3799
prevItem &&
38100
prevItem.message.role === "thinking" &&
39101
redactedThinking &&
40102
prevItem.message.redactedThinking;
41103

104+
useEffect(() => {
105+
if (inProgress) {
106+
setStartTime(Date.now());
107+
setElapsedTime("");
108+
} else if (startTime) {
109+
const endTime = Date.now();
110+
const diff = endTime - startTime;
111+
const diffString = `${(diff / 1000).toFixed(1)}s`;
112+
setElapsedTime(diffString);
113+
}
114+
}, [inProgress]);
115+
42116
return duplicateRedactedThinkingBlock ? null : (
43117
<div className="thread-message">
44118
<div className="" style={{ backgroundColor: vscBackground }}>
45119
<div
46-
className="flex cursor-pointer items-center justify-start pl-2 text-xs text-gray-300"
47-
onClick={() => setOpen((prev) => !prev)}
120+
className="flex items-center justify-start pl-2 text-xs text-gray-300"
48121
data-testid="thinking-block-peek"
49122
>
50-
<div className="relative mr-1.5 h-4 w-4">
51-
<ChevronRightIcon
52-
className={`absolute h-4 w-4 transition-all duration-200 ease-in-out text-[${lightGray}] ${
53-
open ? "rotate-90 opacity-0" : "rotate-0 opacity-100"
54-
}`}
55-
/>
56-
<ChevronDownIcon
57-
className={`absolute h-4 w-4 transition-all duration-200 ease-in-out text-[${lightGray}] ${
58-
open ? "rotate-0 opacity-100" : "-rotate-90 opacity-0"
59-
}`}
60-
/>
61-
</div>
62-
<LightBulbIcon className="mr-0 h-4 w-4 text-gray-400" />
63-
<span className="ml-1 text-xs text-gray-400 transition-colors duration-200">
64-
{redactedThinking ? "Redacted Thinking" : "AI Reasoning"}
65-
</span>
123+
<SpoilerButton onClick={() => setOpen((prev) => !prev)}>
124+
<ButtonContent>
125+
{inProgress ? (
126+
<ThinkingText>
127+
{redactedThinking ? "Redacted Thinking" : "Thinking"}
128+
</ThinkingText>
129+
) : redactedThinking ? (
130+
"Redacted Thinking"
131+
) : (
132+
"Thought" +
133+
(elapsedTime ? ` for ${elapsedTime}` : "") +
134+
(tokens ? ` (${tokens} tokens)` : "")
135+
)}
136+
{open ? (
137+
<ChevronUpIcon className="h-3 w-3" />
138+
) : (
139+
<ChevronDownIcon className="h-3 w-3" />
140+
)}
141+
</ButtonContent>
142+
</SpoilerButton>
66143
</div>
67144

68145
<div

gui/src/pages/gui/Chat.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ export function Chat() {
455455
redactedThinking={item.message.redactedThinking}
456456
index={index}
457457
prevItem={index > 0 ? history[index - 1] : null}
458+
inProgress={index === history.length - 1}
458459
signature={item.message.signature}
459460
/>
460461
) : (

0 commit comments

Comments
 (0)