Skip to content

Commit 43c1b99

Browse files
Bill LeoutsakosBill Leoutsakos
authored andcommitted
fix(pi): compact plan mode event streams
1 parent 63365b0 commit 43c1b99

4 files changed

Lines changed: 46 additions & 5 deletions

File tree

apps/sim/executor/handlers/pi/cloud/event-filter-source.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ function asString(value) {
4646
return typeof value === 'string' ? value : undefined
4747
}
4848
49+
function compactAssistantContent(value) {
50+
if (!Array.isArray(value)) return undefined
51+
const content = []
52+
for (const valueBlock of value) {
53+
const block = asRecord(valueBlock)
54+
if (block?.type === 'text' && typeof block.text === 'string') {
55+
content.push({ type: 'text', text: block.text })
56+
}
57+
}
58+
return content.length > 0 ? content : undefined
59+
}
60+
4961
function compactUsage(value) {
5062
const usage = asRecord(value)
5163
if (!usage) return null
@@ -64,6 +76,7 @@ function compactAssistantMessage(value) {
6476
if (!message || message.role !== 'assistant') return null
6577
return {
6678
role: 'assistant',
79+
content: compactAssistantContent(message.content),
6780
stopReason: asString(message.stopReason),
6881
errorMessage: asString(message.errorMessage),
6982
}

apps/sim/executor/handlers/pi/cloud/plan/backend.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ vi.mock('@/executor/handlers/pi/core/keys', () => ({
3030
mapThinkingLevel: () => 'high',
3131
}))
3232

33+
import {
34+
PI_EVENT_FILTER_PATH,
35+
PI_EVENT_FILTER_SOURCE,
36+
} from '@/executor/handlers/pi/cloud/event-filter-source'
3337
import { runCloudPlanPi } from '@/executor/handlers/pi/cloud/plan/backend'
3438
import type { PiCloudPlanRunParams } from '@/executor/handlers/pi/core/backend'
3539
import {
@@ -99,6 +103,7 @@ describe('runCloudPlanPi', () => {
99103

100104
const [piCommand, piOptions] = mockRun.mock.calls[1]
101105
expect(piCommand).toContain('--no-extensions --no-prompt-templates --no-skills --no-approve')
106+
expect(piCommand).toContain(`| node ${PI_EVENT_FILTER_PATH}`)
102107
expect(piCommand).not.toContain('git commit')
103108
expect(piCommand).not.toContain('git push')
104109
expect(piOptions.envs.ANTHROPIC_API_KEY).toBe('sk-model-secret')
@@ -116,6 +121,18 @@ describe('runCloudPlanPi', () => {
116121
})
117122
)
118123
expect(mockWriteFile).toHaveBeenCalledWith('/workspace/pi-prompt.txt', 'PLAN PROMPT')
124+
expect(mockWriteFile).toHaveBeenCalledWith(PI_EVENT_FILTER_PATH, PI_EVENT_FILTER_SOURCE)
125+
const filterWrites = mockWriteFile.mock.calls.filter(([path]) => path === PI_EVENT_FILTER_PATH)
126+
expect(filterWrites).toHaveLength(1)
127+
const filterWrite = mockWriteFile.mock.calls.findIndex(
128+
([path]) => path === PI_EVENT_FILTER_PATH
129+
)
130+
expect(mockRun.mock.invocationCallOrder[0]).toBeLessThan(
131+
mockWriteFile.mock.invocationCallOrder[filterWrite]
132+
)
133+
expect(mockWriteFile.mock.invocationCallOrder[filterWrite]).toBeLessThan(
134+
mockRun.mock.invocationCallOrder[1]
135+
)
119136
expect(onEvent).toHaveBeenCalledWith({ type: 'text', text: '# Plan\nDo it' })
120137
expect(result.totals.finalText).toBe('# Plan\nDo it')
121138
expect(result).not.toHaveProperty('changedFiles')

apps/sim/executor/handlers/pi/cloud/plan/backend.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
import { createLogger } from '@sim/logger'
99
import { withPiSandbox } from '@/lib/execution/remote-sandbox'
1010
import { resolvePiRunLifetimeMs } from '@/lib/execution/remote-sandbox/pi-lifetime'
11+
import {
12+
PI_EVENT_FILTER_PATH,
13+
PI_EVENT_FILTER_SOURCE,
14+
} from '@/executor/handlers/pi/cloud/event-filter-source'
1115
import {
1216
buildPiScript,
1317
CLONE_TIMEOUT_MS,
@@ -97,6 +101,7 @@ export const runCloudPlanPi: PiBackendRun<PiCloudPlanRunParams> = async (params,
97101
}
98102

99103
await runner.writeFile(PROMPT_PATH, prompt)
104+
await runner.writeFile(PI_EVENT_FILTER_PATH, PI_EVENT_FILTER_SOURCE)
100105
if (params.search) {
101106
await runner.writeFile(PI_SEARCH_EXTENSION_PATH, PI_SEARCH_EXTENSION_SOURCE)
102107
}

apps/sim/executor/handlers/pi/cloud/shared.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,18 @@ describe('PI_EVENT_FILTER_SOURCE', () => {
207207
event.messages.length > 0 &&
208208
(event.messages[0] as { stopReason?: string }).stopReason === 'stop'
209209
)
210-
// Reduced to the one message `normalizePiEvent` inspects, and to the three fields it reads off
211-
// it. The transcript, the thinking block, and the final text all go: the run's text reaches
212-
// Sim through the deltas, so carrying it again here would be the whole answer twice.
210+
// Reduced to the one message `normalizePiEvent` inspects and only the fields it reads. Keeping
211+
// the final text once lets Plan replace streamed progress with the authoritative final answer;
212+
// the transcript, thinking blocks, and tool payloads still go.
213213
expect(completed).toEqual({
214214
type: 'agent_end',
215-
messages: [{ role: 'assistant', stopReason: 'stop' }],
215+
messages: [
216+
{
217+
role: 'assistant',
218+
content: [{ type: 'text', text: 'final answer' }],
219+
stopReason: 'stop',
220+
},
221+
],
216222
})
217223
expect(output.some((event) => event.type === 'tool_execution_update')).toBe(false)
218224
})
@@ -297,6 +303,6 @@ describe('PI_EVENT_FILTER_SOURCE', () => {
297303
expect(text).toHaveLength(totalCharacters)
298304
expect(events).toContainEqual({ type: 'usage', inputTokens: 10, outputTokens: 20 })
299305
expect(events).toContainEqual({ type: 'tool_end', toolName: 'read', isError: false })
300-
expect(events).toContainEqual({ type: 'final' })
306+
expect(events).toContainEqual({ type: 'final', text: 'x'.repeat(totalCharacters) })
301307
})
302308
})

0 commit comments

Comments
 (0)