Skip to content

Commit ceed9a2

Browse files
bhavyausCopilot
andauthored
Emit thinking parts incrementally for Anthropic thinking deltas (#1805)
* Emit thinking parts incrementally for Anthropic thinking deltas * Update src/extension/byok/common/anthropicMessageConverter.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Make tool_call handling part of the if/else chain and remove extra blank line --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 079c458 commit ceed9a2

File tree

2 files changed

+40
-14
lines changed

2 files changed

+40
-14
lines changed

src/extension/byok/common/anthropicMessageConverter.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,24 @@ import { LanguageModelChatMessageRole, LanguageModelDataPart, LanguageModelTextP
1111

1212
function apiContentToAnthropicContent(content: (LanguageModelTextPart | LanguageModelToolResultPart | LanguageModelToolCallPart | LanguageModelDataPart | LanguageModelThinkingPart)[]): ContentBlockParam[] {
1313
const convertedContent: ContentBlockParam[] = [];
14+
1415
for (const part of content) {
1516
if (part instanceof LanguageModelThinkingPart) {
16-
convertedContent.push({
17-
type: 'thinking',
18-
thinking: Array.isArray(part.value) ? part.value.join('') : part.value,
19-
signature: part.metadata?.signature ?? '',
20-
});
17+
// Check if this is a redacted thinking block
18+
if (part.metadata?.redactedData) {
19+
convertedContent.push({
20+
type: 'redacted_thinking',
21+
data: part.metadata.redactedData,
22+
});
23+
} else if (part.metadata?._completeThinking) {
24+
// Only push thinking block when we have the complete thinking marker
25+
convertedContent.push({
26+
type: 'thinking',
27+
thinking: part.metadata._completeThinking,
28+
signature: part.metadata.signature || '',
29+
});
30+
}
31+
// Skip incremental thinking parts - we only care about the complete one
2132
} else if (part instanceof LanguageModelToolCallPart) {
2233
convertedContent.push({
2334
type: 'tool_use',
@@ -75,7 +86,6 @@ function apiContentToAnthropicContent(content: (LanguageModelTextPart | Language
7586
}
7687
}
7788
return convertedContent;
78-
7989
}
8090

8191
export function apiMessageToAnthropicMessage(messages: LanguageModelChatMessage[]): { messages: MessageParam[]; system: TextBlockParam } {
@@ -122,7 +132,6 @@ export function apiMessageToAnthropicMessage(messages: LanguageModelChatMessage[
122132
}
123133
}
124134
}
125-
126135
return { messages: mergedMessages, system: systemMessage };
127136
}
128137

@@ -222,6 +231,11 @@ export function anthropicMessagesToRawMessages(messages: MessageParam[], system:
222231
type: Raw.ChatCompletionContentPartKind.Text,
223232
text: `[THINKING: ${block.thinking}]`
224233
});
234+
} else if (block.type === 'redacted_thinking') {
235+
content.push({
236+
type: Raw.ChatCompletionContentPartKind.Text,
237+
text: '[REDACTED THINKING]'
238+
});
225239
} else if (block.type === 'tool_use') {
226240
// tool_use appears in assistant messages; represent as toolCalls on assistant message
227241
toolCalls ??= [];

src/extension/byok/vscode-node/anthropicProvider.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,9 @@ export class AnthropicLMProvider implements BYOKModelProvider<LanguageModelChatI
372372
thinking?: string;
373373
signature?: string;
374374
} | undefined;
375+
let pendingRedactedThinking: {
376+
data: string;
377+
} | undefined;
375378
let pendingServerToolCall: {
376379
toolId?: string;
377380
name?: string;
@@ -413,6 +416,11 @@ export class AnthropicLMProvider implements BYOKModelProvider<LanguageModelChatI
413416
thinking: '',
414417
signature: ''
415418
};
419+
} else if ('content_block' in chunk && chunk.content_block.type === 'redacted_thinking') {
420+
const redactedBlock = chunk.content_block as Anthropic.Messages.RedactedThinkingBlock;
421+
pendingRedactedThinking = {
422+
data: redactedBlock.data
423+
};
416424
} else if ('content_block' in chunk && chunk.content_block.type === 'web_search_tool_result') {
417425
if (!pendingServerToolCall || !pendingServerToolCall.toolId) {
418426
continue;
@@ -486,6 +494,7 @@ export class AnthropicLMProvider implements BYOKModelProvider<LanguageModelChatI
486494
} else if (chunk.delta.type === 'thinking_delta') {
487495
if (pendingThinking) {
488496
pendingThinking.thinking = (pendingThinking.thinking || '') + (chunk.delta.thinking || '');
497+
progress.report(new LanguageModelThinkingPart(chunk.delta.thinking || ''));
489498
}
490499
} else if (chunk.delta.type === 'signature_delta') {
491500
// Accumulate signature
@@ -529,14 +538,17 @@ export class AnthropicLMProvider implements BYOKModelProvider<LanguageModelChatI
529538
}
530539
pendingToolCall = undefined;
531540
} else if (pendingThinking) {
532-
progress.report(
533-
new LanguageModelThinkingPart(
534-
pendingThinking.thinking || '',
535-
undefined, // id
536-
{ signature: pendingThinking.signature || '' }
537-
)
538-
);
541+
if (pendingThinking.signature) {
542+
const finalThinkingPart = new LanguageModelThinkingPart('');
543+
finalThinkingPart.metadata = {
544+
signature: pendingThinking.signature,
545+
_completeThinking: pendingThinking.thinking
546+
};
547+
progress.report(finalThinkingPart);
548+
}
539549
pendingThinking = undefined;
550+
} else if (pendingRedactedThinking) {
551+
pendingRedactedThinking = undefined;
540552
}
541553
}
542554

0 commit comments

Comments
 (0)