Skip to content

Commit 5d6e56f

Browse files
committed
feat: make AssistantMessageParser an experimental feature
- Add assistantMessageParser experiment flag (disabled by default) - When experiment is off, use the original parseAssistantMessage function - When experiment is on, use the new AssistantMessageParser class - Ensures backward compatibility with no behavior changes when disabled - All existing tests pass with the experimental flag off
1 parent c099d41 commit 5d6e56f

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

packages/types/src/experiment.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type { Keys, Equals, AssertEqual } from "./type-fu.js"
66
* ExperimentId
77
*/
88

9-
export const experimentIds = ["powerSteering", "multiFileApplyDiff", "preventFocusDisruption"] as const
9+
export const experimentIds = ["powerSteering", "multiFileApplyDiff", "preventFocusDisruption", "assistantMessageParser"] as const
1010

1111
export const experimentIdsSchema = z.enum(experimentIds)
1212

@@ -20,6 +20,7 @@ export const experimentsSchema = z.object({
2020
powerSteering: z.boolean().optional(),
2121
multiFileApplyDiff: z.boolean().optional(),
2222
preventFocusDisruption: z.boolean().optional(),
23+
assistantMessageParser: z.boolean().optional(),
2324
})
2425

2526
export type Experiments = z.infer<typeof experimentsSchema>

src/core/task/Task.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ import { ToolRepetitionDetector } from "../tools/ToolRepetitionDetector"
7272
import { FileContextTracker } from "../context-tracking/FileContextTracker"
7373
import { RooIgnoreController } from "../ignore/RooIgnoreController"
7474
import { RooProtectedController } from "../protect/RooProtectedController"
75-
import { type AssistantMessageContent, presentAssistantMessage } from "../assistant-message"
75+
import { type AssistantMessageContent, presentAssistantMessage, parseAssistantMessage } from "../assistant-message"
7676
import { AssistantMessageParser } from "../assistant-message/AssistantMessageParser"
7777
import { truncateConversationIfNeeded } from "../sliding-window"
7878
import { ClineProvider } from "../webview/ClineProvider"
@@ -261,7 +261,8 @@ export class Task extends EventEmitter<TaskEvents> {
261261
didRejectTool = false
262262
didAlreadyUseTool = false
263263
didCompleteReadingStream = false
264-
assistantMessageParser = new AssistantMessageParser()
264+
assistantMessageParser?: AssistantMessageParser
265+
isAssistantMessageParserEnabled = false
265266

266267
constructor({
267268
provider,
@@ -1536,7 +1537,9 @@ export class Task extends EventEmitter<TaskEvents> {
15361537
this.didAlreadyUseTool = false
15371538
this.presentAssistantMessageLocked = false
15381539
this.presentAssistantMessageHasPendingUpdates = false
1539-
this.assistantMessageParser.reset()
1540+
if (this.assistantMessageParser) {
1541+
this.assistantMessageParser.reset()
1542+
}
15401543

15411544
await this.diffViewProvider.reset()
15421545

@@ -1573,7 +1576,12 @@ export class Task extends EventEmitter<TaskEvents> {
15731576

15741577
// Parse raw assistant message chunk into content blocks.
15751578
const prevLength = this.assistantMessageContent.length
1576-
this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
1579+
if (this.isAssistantMessageParserEnabled && this.assistantMessageParser) {
1580+
this.assistantMessageContent = this.assistantMessageParser.processChunk(chunk.text)
1581+
} else {
1582+
// Use the old parsing method when experiment is disabled
1583+
this.assistantMessageContent = parseAssistantMessage(assistantMessage)
1584+
}
15771585

15781586
if (this.assistantMessageContent.length > prevLength) {
15791587
// New content we need to present, reset to
@@ -1694,8 +1702,13 @@ export class Task extends EventEmitter<TaskEvents> {
16941702
// this.assistantMessageContent.forEach((e) => (e.partial = false))
16951703

16961704
// Now that the stream is complete, finalize any remaining partial content blocks
1697-
this.assistantMessageParser.finalizeContentBlocks()
1698-
this.assistantMessageContent = this.assistantMessageParser.getContentBlocks()
1705+
if (this.isAssistantMessageParserEnabled && this.assistantMessageParser) {
1706+
this.assistantMessageParser.finalizeContentBlocks()
1707+
this.assistantMessageContent = this.assistantMessageParser.getContentBlocks()
1708+
} else {
1709+
// When using old parser, parse the complete message
1710+
this.assistantMessageContent = parseAssistantMessage(assistantMessage)
1711+
}
16991712

17001713
if (partialBlocks.length > 0) {
17011714
// If there is content to update then it will complete and
@@ -1711,7 +1724,9 @@ export class Task extends EventEmitter<TaskEvents> {
17111724
await this.providerRef.deref()?.postStateToWebview()
17121725

17131726
// Reset parser after each complete conversation round
1714-
this.assistantMessageParser.reset()
1727+
if (this.assistantMessageParser) {
1728+
this.assistantMessageParser.reset()
1729+
}
17151730

17161731
// Now add to apiConversationHistory.
17171732
// Need to save assistant responses to file before proceeding to

src/shared/experiments.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export const EXPERIMENT_IDS = {
44
MULTI_FILE_APPLY_DIFF: "multiFileApplyDiff",
55
POWER_STEERING: "powerSteering",
66
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
7+
ASSISTANT_MESSAGE_PARSER: "assistantMessageParser",
78
} as const satisfies Record<string, ExperimentId>
89

910
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
@@ -18,6 +19,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
1819
MULTI_FILE_APPLY_DIFF: { enabled: false },
1920
POWER_STEERING: { enabled: false },
2021
PREVENT_FOCUS_DISRUPTION: { enabled: false },
22+
ASSISTANT_MESSAGE_PARSER: { enabled: false },
2123
}
2224

2325
export const experimentDefault = Object.fromEntries(

0 commit comments

Comments
 (0)