test(opencode): stabilize prompt concurrency tests (#33522)

This commit is contained in:
Aiden Cline 2026-06-23 10:45:30 -05:00 committed by GitHub
commit e3edbba3ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -994,57 +994,51 @@ it.instance(
// Cancel semantics // Cancel semantics
it.instance( it.instance("cancel interrupts loop and resolves with an assistant message", () =>
"cancel interrupts loop and resolves with an assistant message", Effect.gen(function* () {
() => const { llm } = yield* useServerConfig(providerCfg)
Effect.gen(function* () { const prompt = yield* SessionPrompt.Service
const { llm } = yield* useServerConfig(providerCfg) const sessions = yield* Session.Service
const prompt = yield* SessionPrompt.Service const chat = yield* sessions.create({ title: "Pinned" })
const sessions = yield* Session.Service yield* seed(chat.id)
const chat = yield* sessions.create({ title: "Pinned" })
yield* seed(chat.id)
yield* llm.hang yield* llm.hang
yield* user(chat.id, "more") yield* user(chat.id, "more")
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild) const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
yield* llm.wait(1) yield* llm.wait(1)
yield* prompt.cancel(chat.id) yield* waitForBusy(chat.id)
const exit = yield* Fiber.await(fiber) yield* prompt.cancel(chat.id)
expect(Exit.isSuccess(exit)).toBe(true) const exit = yield* Fiber.await(fiber)
if (Exit.isSuccess(exit)) { expect(Exit.isSuccess(exit)).toBe(true)
expect(exit.value.info.role).toBe("assistant") if (Exit.isSuccess(exit)) {
expect(exit.value.info.role).toBe("assistant")
}
}))
it.instance("cancel records MessageAbortedError on interrupted process", () =>
Effect.gen(function* () {
const { llm } = yield* useServerConfig(providerCfg)
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const chat = yield* sessions.create({ title: "Pinned" })
yield* llm.hang
yield* user(chat.id, "hello")
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
yield* llm.wait(1)
yield* waitForBusy(chat.id)
yield* prompt.cancel(chat.id)
const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) {
const info = exit.value.info
if (info.role === "assistant") {
expect(info.error?.name).toBe("MessageAbortedError")
} }
}), }
3_000, }))
)
it.instance(
"cancel records MessageAbortedError on interrupted process",
() =>
Effect.gen(function* () {
const { llm } = yield* useServerConfig(providerCfg)
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const chat = yield* sessions.create({ title: "Pinned" })
yield* llm.hang
yield* user(chat.id, "hello")
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
yield* llm.wait(1)
yield* prompt.cancel(chat.id)
const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) {
const info = exit.value.info
if (info.role === "assistant") {
expect(info.error?.name).toBe("MessageAbortedError")
}
}
}),
3_000,
)
raceNoLLMServer.instance( raceNoLLMServer.instance(
"finalizes assistant when cancelled before processor creation completes", "finalizes assistant when cancelled before processor creation completes",
@ -1262,125 +1256,115 @@ noLLMServer.instance("concurrent loop callers get same result", () =>
}), }),
) )
it.instance( it.instance("concurrent loop callers all receive same error result", () =>
"concurrent loop callers all receive same error result", Effect.gen(function* () {
() => const { llm } = yield* useServerConfig(providerCfg)
Effect.gen(function* () { const prompt = yield* SessionPrompt.Service
const { llm } = yield* useServerConfig(providerCfg) const sessions = yield* Session.Service
const prompt = yield* SessionPrompt.Service const chat = yield* sessions.create({ title: "Pinned" })
const sessions = yield* Session.Service
const chat = yield* sessions.create({ title: "Pinned" })
yield* llm.fail("boom") yield* llm.fail("boom")
yield* user(chat.id, "hello") yield* user(chat.id, "hello")
const [a, b] = yield* Effect.all([prompt.loop({ sessionID: chat.id }), prompt.loop({ sessionID: chat.id })], { const [a, b] = yield* Effect.all([prompt.loop({ sessionID: chat.id }), prompt.loop({ sessionID: chat.id })], {
concurrency: "unbounded", concurrency: "unbounded",
})
expect(a.info.id).toBe(b.info.id)
expect(a.info.role).toBe("assistant")
}))
it.instance("prompt submitted during an active run is included in the next LLM input", () =>
Effect.gen(function* () {
const { llm } = yield* useServerConfig(providerCfg)
const gate = yield* Deferred.make<void>()
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const chat = yield* sessions.create({ title: "Pinned" })
yield* llm.hold("first", deferredAsPromise(gate))
yield* llm.text("second")
const a = yield* prompt
.prompt({
sessionID: chat.id,
agent: "build",
model: ref,
parts: [{ type: "text", text: "first" }],
}) })
expect(a.info.id).toBe(b.info.id) .pipe(Effect.forkChild)
expect(a.info.role).toBe("assistant")
}),
3_000,
)
it.instance( yield* llm.wait(1)
"prompt submitted during an active run is included in the next LLM input", yield* waitForBusy(chat.id)
() =>
Effect.gen(function* () {
const { llm } = yield* useServerConfig(providerCfg)
const gate = yield* Deferred.make<void>()
const prompt = yield* SessionPrompt.Service
const sessions = yield* Session.Service
const chat = yield* sessions.create({ title: "Pinned" })
yield* llm.hold("first", deferredAsPromise(gate)) const id = MessageID.ascending()
yield* llm.text("second") const b = yield* prompt
.prompt({
sessionID: chat.id,
messageID: id,
agent: "build",
model: ref,
parts: [{ type: "text", text: "second" }],
})
.pipe(Effect.forkChild)
const a = yield* prompt yield* pollWithTimeout(
.prompt({ sessions
sessionID: chat.id, .messages({ sessionID: chat.id })
agent: "build", .pipe(
model: ref, Effect.map((msgs) =>
parts: [{ type: "text", text: "first" }], msgs.some((msg) => msg.info.role === "user" && msg.info.id === id) ? true : undefined,
})
.pipe(Effect.forkChild)
yield* llm.wait(1)
const id = MessageID.ascending()
const b = yield* prompt
.prompt({
sessionID: chat.id,
messageID: id,
agent: "build",
model: ref,
parts: [{ type: "text", text: "second" }],
})
.pipe(Effect.forkChild)
yield* pollWithTimeout(
sessions
.messages({ sessionID: chat.id })
.pipe(
Effect.map((msgs) =>
msgs.some((msg) => msg.info.role === "user" && msg.info.id === id) ? true : undefined,
),
), ),
"timed out waiting for second prompt to save", ),
) "timed out waiting for second prompt to save",
)
yield* Deferred.succeed(gate, void 0) yield* Deferred.succeed(gate, void 0)
const [ea, eb] = yield* Effect.all([Fiber.await(a), Fiber.await(b)]) const [ea, eb] = yield* Effect.all([Fiber.await(a), Fiber.await(b)])
expect(Exit.isSuccess(ea)).toBe(true) expect(Exit.isSuccess(ea)).toBe(true)
expect(Exit.isSuccess(eb)).toBe(true) expect(Exit.isSuccess(eb)).toBe(true)
expect(yield* llm.calls).toBe(2) expect(yield* llm.calls).toBe(2)
const msgs = yield* sessions.messages({ sessionID: chat.id }) const msgs = yield* sessions.messages({ sessionID: chat.id })
const assistants = msgs.filter((msg) => msg.info.role === "assistant") const assistants = msgs.filter((msg) => msg.info.role === "assistant")
expect(assistants).toHaveLength(2) expect(assistants).toHaveLength(2)
const last = assistants.at(-1) const last = assistants.at(-1)
if (!last || last.info.role !== "assistant") throw new Error("expected second assistant") if (!last || last.info.role !== "assistant") throw new Error("expected second assistant")
expect(last.info.parentID).toBe(id) expect(last.info.parentID).toBe(id)
expect(last.parts.some((part) => part.type === "text" && part.text === "second")).toBe(true) expect(last.parts.some((part) => part.type === "text" && part.text === "second")).toBe(true)
const inputs = yield* llm.inputs const inputs = yield* llm.inputs
expect(inputs).toHaveLength(2) expect(inputs).toHaveLength(2)
const messages = inputs.at(-1)?.messages const messages = inputs.at(-1)?.messages
if (!Array.isArray(messages)) throw new Error("expected LLM messages") if (!Array.isArray(messages)) throw new Error("expected LLM messages")
expect(messages.at(-1)).toEqual({ role: "user", content: "second" }) expect(messages.at(-1)).toEqual({ role: "user", content: "second" })
}), }))
3_000,
)
it.instance( it.instance("assertNotBusy fails with BusyError when loop running", () =>
"assertNotBusy fails with BusyError when loop running", Effect.gen(function* () {
() => const { llm } = yield* useServerConfig(providerCfg)
Effect.gen(function* () { const prompt = yield* SessionPrompt.Service
const { llm } = yield* useServerConfig(providerCfg) const run = yield* SessionRunState.Service
const prompt = yield* SessionPrompt.Service const sessions = yield* Session.Service
const run = yield* SessionRunState.Service yield* llm.hang
const sessions = yield* Session.Service
yield* llm.hang
const chat = yield* sessions.create({}) const chat = yield* sessions.create({})
yield* user(chat.id, "hi") yield* user(chat.id, "hi")
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild) const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
yield* llm.wait(1) yield* llm.wait(1)
yield* waitForBusy(chat.id)
const exit = yield* run.assertNotBusy(chat.id).pipe(Effect.exit) const exit = yield* run.assertNotBusy(chat.id).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true) expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isFailure(exit)) { if (Exit.isFailure(exit)) {
expect(Cause.squash(exit.cause)).toBeInstanceOf(Session.BusyError) expect(Cause.squash(exit.cause)).toBeInstanceOf(Session.BusyError)
expect(Cause.squash(exit.cause)).toMatchObject({ _tag: "SessionBusyError", sessionID: chat.id }) expect(Cause.squash(exit.cause)).toMatchObject({ _tag: "SessionBusyError", sessionID: chat.id })
} }
yield* prompt.cancel(chat.id) yield* prompt.cancel(chat.id)
yield* Fiber.await(fiber) yield* Fiber.await(fiber)
}), }))
3_000,
)
noLLMServer.instance("assertNotBusy succeeds when idle", () => noLLMServer.instance("assertNotBusy succeeds when idle", () =>
Effect.gen(function* () { Effect.gen(function* () {
@ -1395,32 +1379,29 @@ noLLMServer.instance("assertNotBusy succeeds when idle", () =>
// Shell semantics // Shell semantics
it.instance( it.instance("shell rejects with BusyError when loop running", () =>
"shell rejects with BusyError when loop running", Effect.gen(function* () {
() => const { llm } = yield* useServerConfig(providerCfg)
Effect.gen(function* () { const prompt = yield* SessionPrompt.Service
const { llm } = yield* useServerConfig(providerCfg) const sessions = yield* Session.Service
const prompt = yield* SessionPrompt.Service const chat = yield* sessions.create({ title: "Pinned" })
const sessions = yield* Session.Service yield* llm.hang
const chat = yield* sessions.create({ title: "Pinned" }) yield* user(chat.id, "hi")
yield* llm.hang
yield* user(chat.id, "hi")
const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild) const fiber = yield* prompt.loop({ sessionID: chat.id }).pipe(Effect.forkChild)
yield* llm.wait(1) yield* llm.wait(1)
yield* waitForBusy(chat.id)
const exit = yield* prompt.shell({ sessionID: chat.id, agent: "build", command: "echo hi" }).pipe(Effect.exit) const exit = yield* prompt.shell({ sessionID: chat.id, agent: "build", command: "echo hi" }).pipe(Effect.exit)
expect(Exit.isFailure(exit)).toBe(true) expect(Exit.isFailure(exit)).toBe(true)
if (Exit.isFailure(exit)) { if (Exit.isFailure(exit)) {
expect(Cause.squash(exit.cause)).toBeInstanceOf(Session.BusyError) expect(Cause.squash(exit.cause)).toBeInstanceOf(Session.BusyError)
expect(Cause.squash(exit.cause)).toMatchObject({ _tag: "SessionBusyError", sessionID: chat.id }) expect(Cause.squash(exit.cause)).toMatchObject({ _tag: "SessionBusyError", sessionID: chat.id })
} }
yield* prompt.cancel(chat.id) yield* prompt.cancel(chat.id)
yield* Fiber.await(fiber) yield* Fiber.await(fiber)
}), }))
3_000,
)
unixNoLLMServer( unixNoLLMServer(
"shell captures stdout and stderr in completed tool output", "shell captures stdout and stderr in completed tool output",
@ -2132,46 +2113,43 @@ it.instance("does not loop empty assistant turns for a simple reply", () =>
}), }),
) )
it.instance( it.instance("records aborted errors when prompt is cancelled mid-stream", () =>
"records aborted errors when prompt is cancelled mid-stream", Effect.gen(function* () {
() => const { llm } = yield* useServerConfig(providerCfg)
Effect.gen(function* () { const prompt = yield* SessionPrompt.Service
const { llm } = yield* useServerConfig(providerCfg) const sessions = yield* Session.Service
const prompt = yield* SessionPrompt.Service const session = yield* sessions.create({ title: "Prompt cancel regression" })
const sessions = yield* Session.Service
const session = yield* sessions.create({ title: "Prompt cancel regression" })
yield* llm.hang yield* llm.hang
const fiber = yield* prompt const fiber = yield* prompt
.prompt({ .prompt({
sessionID: session.id, sessionID: session.id,
agent: "build", agent: "build",
parts: [{ type: "text", text: "Cancel me" }], parts: [{ type: "text", text: "Cancel me" }],
}) })
.pipe(Effect.forkChild) .pipe(Effect.forkChild)
yield* llm.wait(1) yield* llm.wait(1)
yield* prompt.cancel(session.id) yield* waitForBusy(session.id)
yield* prompt.cancel(session.id)
const exit = yield* Fiber.await(fiber) const exit = yield* Fiber.await(fiber)
expect(Exit.isSuccess(exit)).toBe(true) expect(Exit.isSuccess(exit)).toBe(true)
if (Exit.isSuccess(exit)) { if (Exit.isSuccess(exit)) {
expect(exit.value.info.role).toBe("assistant") expect(exit.value.info.role).toBe("assistant")
if (exit.value.info.role === "assistant") { if (exit.value.info.role === "assistant") {
expect(exit.value.info.error?.name).toBe("MessageAbortedError") expect(exit.value.info.error?.name).toBe("MessageAbortedError")
}
} }
}
const msgs = yield* sessions.messages({ sessionID: session.id }) const msgs = yield* sessions.messages({ sessionID: session.id })
const last = msgs.findLast((msg) => msg.info.role === "assistant") const last = msgs.findLast((msg) => msg.info.role === "assistant")
expect(last?.info.role).toBe("assistant") expect(last?.info.role).toBe("assistant")
if (last?.info.role === "assistant") { if (last?.info.role === "assistant") {
expect(last.info.error?.name).toBe("MessageAbortedError") expect(last.info.error?.name).toBe("MessageAbortedError")
} }
}), }))
3_000,
)
// Agent variant // Agent variant