Compare commits

...

2 Commits

Author SHA1 Message Date
Trevor Hudson 771a0eafd2 slider 2025-02-24 15:33:11 -08:00
Trevor Hudson 8e22e1ddeb thinking 2025-02-24 14:04:12 -08:00
9 changed files with 226 additions and 47 deletions
+19 -16
View File
@@ -1,17 +1,17 @@
{
"name": "claude-dev",
"version": "3.4.3",
"version": "3.4.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "claude-dev",
"version": "3.4.3",
"version": "3.4.6",
"license": "Apache-2.0",
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.26.0",
"@anthropic-ai/vertex-sdk": "^0.4.1",
"@anthropic-ai/bedrock-sdk": "^0.12.4",
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.7.0",
"@google/generative-ai": "^0.18.0",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.0.1",
@@ -76,11 +76,12 @@
}
},
"node_modules/@anthropic-ai/bedrock-sdk": {
"version": "0.10.2",
"resolved": "https://registry.npmjs.org/@anthropic-ai/bedrock-sdk/-/bedrock-sdk-0.10.2.tgz",
"integrity": "sha512-sGmTzKJQHVwfXexe+yfzPU3rJmUMCygC+GNPkmMsPX/Jr+WKtJ0M71nGyHONr6vcHwUpUWA6o0MRH/oHaE54KA==",
"version": "0.12.4",
"resolved": "https://registry.npmjs.org/@anthropic-ai/bedrock-sdk/-/bedrock-sdk-0.12.4.tgz",
"integrity": "sha512-kraOgWWyVO/Wef3wYbpws77pCZubg/hCzXQ7RGrLRJsRpbTIT+ms+MigGu/0b1qn3o5WuIrumlT3Qtu3esHpzw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0",
"@anthropic-ai/sdk": ">=0.36 <1",
"@aws-crypto/sha256-js": "^4.0.0",
"@aws-sdk/client-bedrock-runtime": "^3.423.0",
"@aws-sdk/credential-providers": "^3.341.0",
@@ -94,9 +95,10 @@
}
},
"node_modules/@anthropic-ai/sdk": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.26.0.tgz",
"integrity": "sha512-vNbZ2rnnMfk8Bf4OdeVy6GA4EXao8tGC0tLEoSAl1NZrip9oOxnEGUkXl3FsPQgeBM5hmpGE1tSLuu9HEVJiHg==",
"version": "0.37.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.37.0.tgz",
"integrity": "sha512-tHjX2YbkUBwEgg0JZU3EFSSAQPoK4qQR/NFYa8Vtzd5UAyXzZksCw2In69Rml4R/TyHPBfRYaLK35XiOe33pjw==",
"license": "MIT",
"dependencies": {
"@types/node": "^18.11.18",
"@types/node-fetch": "^2.6.4",
@@ -117,11 +119,12 @@
}
},
"node_modules/@anthropic-ai/vertex-sdk": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.4.1.tgz",
"integrity": "sha512-RT/2CWzqyAcJDZWxnNc1mXa7XiiHDaQ9aknfW4mIDw6zE+Zj/R2vCKpTb0dIwrmHYNOyKQNaD7Z1ynDt9oXFWA==",
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/@anthropic-ai/vertex-sdk/-/vertex-sdk-0.7.0.tgz",
"integrity": "sha512-zNm3hUXgYmYDTyveIxOyxbcnh5VXFkrLo4bSnG6LAfGzW7k3k2iCNDSVKtR9qZrK2BCid7JtVu7jsEKaZ/9dSw==",
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": ">=0.14 <1",
"@anthropic-ai/sdk": ">=0.35 <1",
"google-auth-library": "^9.4.2"
}
},
+4 -4
View File
@@ -237,9 +237,9 @@
"typescript": "^5.4.5"
},
"dependencies": {
"@anthropic-ai/bedrock-sdk": "^0.10.2",
"@anthropic-ai/sdk": "^0.26.0",
"@anthropic-ai/vertex-sdk": "^0.4.1",
"@anthropic-ai/bedrock-sdk": "^0.12.4",
"@anthropic-ai/sdk": "^0.37.0",
"@anthropic-ai/vertex-sdk": "^0.7.0",
"@google/generative-ai": "^0.18.0",
"@mistralai/mistralai": "^1.5.0",
"@modelcontextprotocol/sdk": "^1.0.1",
@@ -278,4 +278,4 @@
"web-tree-sitter": "^0.22.6",
"zod": "^3.24.2"
}
}
}
+7 -3
View File
@@ -18,9 +18,9 @@ export class AnthropicHandler implements ApiHandler {
}
@withRetry()
async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream {
async *createMessage(systemPrompt: string, thinkingBudget: number, messages: Anthropic.Messages.MessageParam[]): ApiStream {
const model = this.getModel()
let stream: AnthropicStream<Anthropic.Beta.PromptCaching.Messages.RawPromptCachingBetaMessageStreamEvent>
let stream: AnthropicStream<Anthropic.Messages.RawMessageStreamEvent>
const modelId = model.id
switch (modelId) {
// 'latest' alias does not support cache_control
@@ -38,11 +38,15 @@ export class AnthropicHandler implements ApiHandler {
)
const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
stream = await this.client.beta.promptCaching.messages.create(
stream = await this.client.messages.create(
{
model: modelId,
max_tokens: model.info.maxTokens || 8192,
temperature: 0,
thinking: {
type: "enabled",
budget_tokens: 2000,
},
system: [
{
text: systemPrompt,
+1 -1
View File
@@ -556,7 +556,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
await this.postStateToWebview()
}
break
case "togglePlanActMode":
case "changeChatSettings":
if (message.chatSettings) {
await this.togglePlanActModeWithChatSettings(message.chatSettings, message.chatContent)
}
+6 -1
View File
@@ -42,7 +42,12 @@ export async function downloadTask(dateTs: number, conversationHistory: Anthropi
}
export function formatContentBlockToMarkdown(
block: Anthropic.TextBlockParam | Anthropic.ImageBlockParam | Anthropic.ToolUseBlockParam | Anthropic.ToolResultBlockParam,
block:
| Anthropic.TextBlockParam
| Anthropic.ImageBlockParam
| Anthropic.ToolUseBlockParam
| Anthropic.ToolResultBlockParam
| Anthropic.ContentBlockParam,
// messages: Anthropic.MessageParam[]
): string {
switch (block.type) {
+2
View File
@@ -1,7 +1,9 @@
export interface ChatSettings {
mode: "plan" | "act"
thinkingValue?: number // Value from 0-10000
}
export const DEFAULT_CHAT_SETTINGS: ChatSettings = {
mode: "act",
thinkingValue: 0, // Default value
}
+1 -1
View File
@@ -32,7 +32,7 @@ export interface WebviewMessage {
| "deleteMcpServer"
| "autoApprovalSettings"
| "browserSettings"
| "togglePlanActMode"
| "changeChatSettings"
| "checkpointDiff"
| "checkpointRestore"
| "taskCompletionViewChanges"
+41 -21
View File
@@ -19,6 +19,7 @@ import { vscode } from "../../utils/vscode"
import { CODE_BLOCK_BG_COLOR } from "../common/CodeBlock"
import Thumbnails from "../common/Thumbnails"
import Tooltip from "../common/Tooltip"
import ThinkingSlider from "../common/ThinkingSlider"
import ApiOptions, { normalizeApiConfiguration } from "../settings/ApiOptions"
import { MAX_IMAGES_PER_MESSAGE } from "./ChatView"
import ContextMenu from "./ContextMenu"
@@ -655,7 +656,7 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
setTimeout(() => {
const newMode = chatSettings.mode === "plan" ? "act" : "plan"
vscode.postMessage({
type: "togglePlanActMode",
type: "changeChatSettings",
chatSettings: {
mode: newMode,
},
@@ -1131,26 +1132,45 @@ const ChatTextArea = forwardRef<HTMLTextAreaElement, ChatTextAreaProps>(
)}
</ModelContainer>
</ButtonGroup>
<Tooltip
visible={shownTooltipMode !== null}
tipText={`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}
hintText={`Toggle w/ ${metaKeyChar}+Shift+A`}>
<SwitchContainer data-testid="mode-switch" disabled={false} onClick={onModeToggle}>
<Slider isAct={chatSettings.mode === "act"} isPlan={chatSettings.mode === "plan"} />
<SwitchOption
isActive={chatSettings.mode === "plan"}
onMouseOver={() => setShownTooltipMode("plan")}
onMouseLeave={() => setShownTooltipMode(null)}>
Plan
</SwitchOption>
<SwitchOption
isActive={chatSettings.mode === "act"}
onMouseOver={() => setShownTooltipMode("act")}
onMouseLeave={() => setShownTooltipMode(null)}>
Act
</SwitchOption>
</SwitchContainer>
</Tooltip>
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
{apiConfiguration?.apiModelId?.includes("claude-3-7-sonnet") && (
<>
<ThinkingSlider
value={chatSettings.thinkingValue || 0}
onChange={(value) => {
vscode.postMessage({
type: "changeChatSettings",
chatSettings: {
...chatSettings,
thinkingValue: value,
},
})
}}
disabled={textAreaDisabled}
/>
<Tooltip
visible={shownTooltipMode !== null}
tipText={`In ${shownTooltipMode === "act" ? "Act" : "Plan"} mode, Cline will ${shownTooltipMode === "act" ? "complete the task immediately" : "gather information to architect a plan"}`}
hintText={`Toggle w/ ${metaKeyChar}+Shift+A`}>
<SwitchContainer data-testid="mode-switch" disabled={false} onClick={onModeToggle}>
<Slider isAct={chatSettings.mode === "act"} isPlan={chatSettings.mode === "plan"} />
<SwitchOption
isActive={chatSettings.mode === "plan"}
onMouseOver={() => setShownTooltipMode("plan")}
onMouseLeave={() => setShownTooltipMode(null)}>
Plan
</SwitchOption>
<SwitchOption
isActive={chatSettings.mode === "act"}
onMouseOver={() => setShownTooltipMode("act")}
onMouseLeave={() => setShownTooltipMode(null)}>
Act
</SwitchOption>
</SwitchContainer>
</Tooltip>
</>
)}
</div>
</ControlsContainer>
</div>
)
@@ -0,0 +1,145 @@
import React, { useState, useRef, useEffect, useCallback } from "react"
import debounce from "debounce"
import styled from "styled-components"
import { getAsVar, VSC_SIDEBAR_BACKGROUND, VSC_DESCRIPTION_FOREGROUND, VSC_INPUT_BORDER } from "../../utils/vscStyles"
interface ThinkingSliderProps {
value: number
onChange: (value: number) => void
disabled?: boolean
}
const SliderContainer = styled.div`
position: relative;
display: inline-block;
`
const BrainIcon = styled.div<{ disabled?: boolean }>`
font-size: 16px;
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
transform: scale(0.85);
transform-origin: center;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
`
const TooltipBody = styled.div`
position: absolute;
background-color: ${getAsVar(VSC_SIDEBAR_BACKGROUND)};
color: ${getAsVar(VSC_DESCRIPTION_FOREGROUND)};
padding: 10px;
border-radius: 5px;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
margin-bottom: 8px;
z-index: 10;
width: 200px;
border: 1px solid ${getAsVar(VSC_INPUT_BORDER)};
font-size: 0.9em;
// Arrow pointing down
&::after {
content: "";
position: absolute;
bottom: -6px;
left: 50%;
transform: translateX(-50%) rotate(45deg);
width: 10px;
height: 10px;
background: ${getAsVar(VSC_SIDEBAR_BACKGROUND)};
border-right: 1px solid ${getAsVar(VSC_INPUT_BORDER)};
border-bottom: 1px solid ${getAsVar(VSC_INPUT_BORDER)};
}
`
const SliderInput = styled.input`
width: 100%;
margin: 10px 0 5px;
`
const SliderValue = styled.div`
text-align: center;
font-size: 0.9em;
margin-top: 5px;
`
const ThinkingSlider: React.FC<ThinkingSliderProps> = ({ value, onChange, disabled = false }) => {
const [showTooltip, setShowTooltip] = useState(false)
const [localValue, setLocalValue] = useState(value)
const containerRef = useRef<HTMLDivElement>(null)
const tooltipRef = useRef<HTMLDivElement>(null)
useEffect(() => {
setLocalValue(value)
}, [value])
// Create a debounced version of setShowTooltip(false)
const hideTooltipDebounced = useCallback(
debounce(() => {
setShowTooltip(false)
}, 300), // 300ms delay before hiding
[],
)
// Cancel the debounce when component unmounts
useEffect(() => {
return () => {
hideTooltipDebounced.clear()
}
}, [hideTooltipDebounced])
const handleMouseEnter = useCallback(() => {
// Cancel any pending hide operations
hideTooltipDebounced.clear()
// Show immediately
setShowTooltip(true)
}, [hideTooltipDebounced])
const handleMouseLeave = useCallback(() => {
// Delay hiding to give time to move to the tooltip
hideTooltipDebounced()
}, [hideTooltipDebounced])
const handleSliderChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newValue = parseInt(e.target.value, 10)
setLocalValue(newValue)
}
const handleSliderMouseUp = () => {
onChange(localValue)
}
return (
<SliderContainer ref={containerRef}>
<BrainIcon
disabled={disabled}
onMouseEnter={() => !disabled && handleMouseEnter()}
onMouseLeave={() => !disabled && handleMouseLeave()}
className="codicon codicon-lightbulb-sparkle"
/>
{showTooltip && !disabled && (
<TooltipBody ref={tooltipRef} onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
<div>Thinking Budget (tokens)</div>
<SliderInput
type="range"
min="0"
max="10000"
value={localValue}
onChange={handleSliderChange}
onMouseUp={handleSliderMouseUp}
/>
<SliderValue>{localValue}</SliderValue>
</TooltipBody>
)}
</SliderContainer>
)
}
export default ThinkingSlider