fix(acp): speed up acp-next warm switches (#29713)

This commit is contained in:
Shoubhit Dash
2026-05-28 15:10:17 +05:30
committed by GitHub
parent 2449b50585
commit 9031ce7b51
3 changed files with 175 additions and 25 deletions

View File

@@ -707,24 +707,35 @@ describe("ACP next service sessions", () => {
expect(results.map((error) => error.code)).toEqual([-32602, -32602, -32602, -32602])
})
it("does not reload providers or commands when switching effort from a warm snapshot", async () => {
let providersCalls = 0
let commandCalls = 0
it("does not refetch providers modes or commands when switching effort from session snapshot", async () => {
const calls = {
providers: 0,
agents: 0,
commands: 0,
skills: 0,
mcpAdds: 0,
}
const sdk = {
config: {
providers: () => {
providersCalls++
calls.providers++
return Promise.resolve({ data: { providers: [provider], default: { test: modelID } } })
},
get: () => Promise.resolve({ data: {} }),
},
app: {
agents: () => Promise.resolve({ data: [{ name: "build", mode: "primary", permission: [], options: {} }] }),
skills: () => Promise.resolve({ data: [] }),
agents: () => {
calls.agents++
return Promise.resolve({ data: [{ name: "build", mode: "primary", permission: [], options: {} }] })
},
skills: () => {
calls.skills++
return Promise.resolve({ data: [] })
},
},
command: {
list: () => {
commandCalls++
calls.commands++
return Promise.resolve({ data: [] })
},
},
@@ -733,14 +744,16 @@ describe("ACP next service sessions", () => {
list: () => Promise.resolve({ data: [] }),
},
mcp: {
add: () => Promise.resolve({ data: {} }),
add: () => {
calls.mcpAdds++
return Promise.resolve({ data: {} })
},
},
} as unknown as OpencodeClient
const service = ACPNextService.make({ sdk })
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
expect(providersCalls).toBe(1)
expect(commandCalls).toBe(1)
expect(calls).toEqual({ providers: 1, agents: 1, commands: 1, skills: 1, mcpAdds: 0 })
await Effect.runPromise(
service.setSessionConfigOption({
@@ -750,8 +763,135 @@ describe("ACP next service sessions", () => {
}),
)
expect(providersCalls).toBe(1)
expect(commandCalls).toBe(1)
expect(calls).toEqual({ providers: 1, agents: 1, commands: 1, skills: 1, mcpAdds: 0 })
})
it("switches model against the warm provider snapshot without refetching", async () => {
const calls = {
providers: 0,
agents: 0,
commands: 0,
skills: 0,
}
const sdk = {
config: {
providers: () => {
calls.providers++
return Promise.resolve({ data: { providers: [provider], default: { test: modelID } } })
},
get: () => Promise.resolve({ data: {} }),
},
app: {
agents: () => {
calls.agents++
return Promise.resolve({ data: [{ name: "build", mode: "primary", permission: [], options: {} }] })
},
skills: () => {
calls.skills++
return Promise.resolve({ data: [] })
},
},
command: {
list: () => {
calls.commands++
return Promise.resolve({ data: [] })
},
},
session: {
create: () => Promise.resolve({ data: { id: "ses_model_fast" } }),
list: () => Promise.resolve({ data: [] }),
},
mcp: {
add: () => Promise.resolve({ data: {} }),
},
} as unknown as OpencodeClient
const service = ACPNextService.make({ sdk })
const session = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
const updated = await Effect.runPromise(
service.setSessionConfigOption({
sessionId: session.sessionId,
configId: "model",
value: "test/second-model",
}),
)
expect(select(updated, "model")?.currentValue).toBe("test/second-model")
expect(calls).toEqual({ providers: 1, agents: 1, commands: 1, skills: 1 })
})
it("reuses the warm directory snapshot for a second new session in the same cwd", async () => {
const calls = {
providers: 0,
config: 0,
agents: 0,
commands: 0,
skills: 0,
sessionList: 0,
messages: 0,
creates: 0,
}
const sdk = {
config: {
providers: () => {
calls.providers++
return Promise.resolve({ data: { providers: [provider], default: { test: modelID } } })
},
get: () => {
calls.config++
return Promise.resolve({ data: {} })
},
},
app: {
agents: () => {
calls.agents++
return Promise.resolve({ data: [{ name: "build", mode: "primary", permission: [], options: {} }] })
},
skills: () => {
calls.skills++
return Promise.resolve({ data: [] })
},
},
command: {
list: () => {
calls.commands++
return Promise.resolve({ data: [] })
},
},
session: {
create: () => {
calls.creates++
return Promise.resolve({ data: { id: `ses_warm_${calls.creates}` } })
},
list: () => {
calls.sessionList++
return Promise.resolve({ data: [] })
},
messages: () => {
calls.messages++
return Promise.resolve({ data: [] })
},
},
mcp: {
add: () => Promise.resolve({ data: {} }),
},
} as unknown as OpencodeClient
const service = ACPNextService.make({ sdk })
const first = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
const second = await Effect.runPromise(service.newSession({ cwd: "/workspace", mcpServers: [] }))
expect(first.sessionId).toBe("ses_warm_1")
expect(second.sessionId).toBe("ses_warm_2")
expect(calls).toEqual({
providers: 1,
config: 1,
agents: 1,
commands: 1,
skills: 1,
sessionList: 0,
messages: 0,
creates: 2,
})
})
it("normal text prompt sends model variant mode and converted parts", async () => {

View File

@@ -42,7 +42,7 @@ describe("opencode acp-next verifier timing diagnostics", () => {
)
cliIt.live(
"warm new session timing diagnostic stays below generous threshold",
"warm new session stays below verifier threshold",
({ home, llm, opencode }) =>
Effect.gen(function* () {
const acp = yield* createAcpNextClient(
@@ -57,15 +57,13 @@ describe("opencode acp-next verifier timing diagnostics", () => {
const durationMs = Math.round(performance.now() - started)
expect(session.sessionId).toBeTruthy()
// TODO: replace this diagnostic assertion with finalFastPathThresholdMs.
expect(durationMs).toBeLessThan(diagnosticFastPathThresholdMs)
expect(finalFastPathThresholdMs).toBe(100)
expect(durationMs).toBeLessThan(finalFastPathThresholdMs)
}),
60_000,
)
cliIt.live(
"model switch timing diagnostic updates currentValue below generous threshold",
"model switch updates currentValue below verifier threshold",
({ home, llm, opencode }) =>
Effect.gen(function* () {
const acp = yield* createAcpNextClient(
@@ -89,14 +87,13 @@ describe("opencode acp-next verifier timing diagnostics", () => {
const durationMs = Math.round(performance.now() - started)
expect(expectSelectOption(updated.configOptions, "model").currentValue).toBe(nextModel)
// TODO: replace this diagnostic assertion with finalFastPathThresholdMs.
expect(durationMs).toBeLessThan(diagnosticFastPathThresholdMs)
expect(durationMs).toBeLessThan(finalFastPathThresholdMs)
}),
60_000,
)
cliIt.live(
"effort switch timing diagnostic updates currentValue below generous threshold",
"effort switch updates currentValue below verifier threshold",
({ home, llm, opencode }) =>
Effect.gen(function* () {
const acp = yield* createAcpNextClient(
@@ -118,8 +115,7 @@ describe("opencode acp-next verifier timing diagnostics", () => {
const durationMs = Math.round(performance.now() - started)
expect(expectSelectOption(updated.configOptions, "effort").currentValue).toBe(nextEffort)
// TODO: replace this diagnostic assertion with finalFastPathThresholdMs.
expect(durationMs).toBeLessThan(diagnosticFastPathThresholdMs)
expect(durationMs).toBeLessThan(finalFastPathThresholdMs)
}),
60_000,
)