feat(session): define explicit fork boundaries
This commit is contained in:
parent
2a85c861e0
commit
fc11ed3838
29 changed files with 821 additions and 386 deletions
|
|
@ -199,7 +199,7 @@ describe("Session.create", () => {
|
|||
yield* session.synthetic({ sessionID: parent.id, text: "parent note", resume: false })
|
||||
yield* SessionPending.promote(db, bus, parent.id, "steer")
|
||||
|
||||
const forked = yield* session.fork({ sessionID: parent.id })
|
||||
const forked = yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } })
|
||||
const parentContext = yield* session.context(parent.id)
|
||||
const forkContext = yield* session.context(forked.id)
|
||||
const history = Array.from(yield* Stream.runCollect(logEvents(session, forked.id)))
|
||||
|
|
@ -252,6 +252,17 @@ describe("Session.create", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("rejects forking an empty session", () =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
const parent = yield* session.create({ location })
|
||||
|
||||
expect(
|
||||
yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } }).pipe(Effect.flip),
|
||||
).toMatchObject({ _tag: "Session.ForkEmptyError", sessionID: parent.id })
|
||||
}),
|
||||
)
|
||||
|
||||
it.effect("forks before the selected boundary message", () =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* Session.Service
|
||||
|
|
@ -286,16 +297,27 @@ describe("Session.create", () => {
|
|||
tokens: { input: 6, output: 3, reasoning: 1, cache: { read: 2, write: 1 } },
|
||||
})
|
||||
|
||||
const forked = yield* session.fork({ sessionID: parent.id, messageID: second.id })
|
||||
const beforeFirst = yield* session.fork({ sessionID: parent.id, messageID: first.id })
|
||||
const complete = yield* session.fork({ sessionID: parent.id })
|
||||
const forked = yield* session.fork({
|
||||
sessionID: parent.id,
|
||||
boundary: { type: "before", messageID: second.id },
|
||||
})
|
||||
const beforeFirst = yield* session.fork({
|
||||
sessionID: parent.id,
|
||||
boundary: { type: "before", messageID: first.id },
|
||||
})
|
||||
const complete = yield* session.fork({ sessionID: parent.id, boundary: { type: "through" } })
|
||||
|
||||
const context = yield* session.context(forked.id)
|
||||
const history = Array.from(yield* Stream.runCollect(logEvents(session, forked.id)))
|
||||
expect(forked.fork).toEqual({ sessionID: parent.id, messageID: second.id })
|
||||
expect(forked.fork).toEqual({
|
||||
sessionID: parent.id,
|
||||
boundary: { type: "before", messageID: second.id },
|
||||
})
|
||||
expect(context).toMatchObject([{ text: "First" }])
|
||||
expect(context[0]?.id).not.toBe(first.id)
|
||||
expect(history[0]).toMatchObject({ data: { from: second.id } })
|
||||
expect(history[0]).toMatchObject({
|
||||
data: { boundary: { type: "before", messageID: second.id } },
|
||||
})
|
||||
expect(forked).toMatchObject({ cost: 0, tokens: { input: 0, output: 0, reasoning: 0 } })
|
||||
expect(yield* session.context(beforeFirst.id)).toEqual([])
|
||||
expect(beforeFirst).toMatchObject({ cost: 0, tokens: { input: 0, output: 0, reasoning: 0 } })
|
||||
|
|
|
|||
|
|
@ -1107,7 +1107,7 @@ describe("SessionRunnerLLM", () => {
|
|||
systemBaseline = "Latest context"
|
||||
yield* runPrompt(session, "Third")
|
||||
|
||||
const forked = yield* session.fork({ sessionID, messageID: second.id })
|
||||
const forked = yield* session.fork({ sessionID, boundary: { type: "before", messageID: second.id } })
|
||||
expect(
|
||||
yield* (yield* Database.Service).db
|
||||
.select()
|
||||
|
|
@ -1115,14 +1115,13 @@ describe("SessionRunnerLLM", () => {
|
|||
.where(eq(InstructionStateTable.session_id, forked.id))
|
||||
.get(),
|
||||
).toMatchObject({
|
||||
initial_values: { "test/context": Instructions.hash("Initial context") },
|
||||
initial_values: { "test/context": Instructions.hash("Changed context") },
|
||||
current_values: { "test/context": Instructions.hash("Changed context") },
|
||||
})
|
||||
yield* session.prompt({ sessionID: forked.id, text: "Forked", resume: false })
|
||||
yield* session.resume(forked.id)
|
||||
|
||||
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual([defaultSystem, "Initial context"])
|
||||
expect(systemTexts(requests.at(-1)!)).toContain("Changed context")
|
||||
expect(requests.at(-1)?.system.map((part) => part.text)).toEqual([defaultSystem, "Changed context"])
|
||||
expect(systemTexts(requests.at(-1)!)).toContain("Latest context")
|
||||
|
||||
const { db } = yield* Database.Service
|
||||
|
|
@ -1151,19 +1150,22 @@ describe("SessionRunnerLLM", () => {
|
|||
}),
|
||||
)
|
||||
|
||||
it.effect("caps nested fork instruction ancestry at the selected message", () =>
|
||||
it.effect("keeps nested forks self-contained", () =>
|
||||
Effect.gen(function* () {
|
||||
const session = yield* setup
|
||||
yield* runPrompt(session, "First")
|
||||
systemBaseline = "Changed context"
|
||||
const second = yield* runPrompt(session, "Second")
|
||||
|
||||
const child = yield* session.fork({ sessionID, messageID: second.id })
|
||||
const child = yield* session.fork({ sessionID, boundary: { type: "before", messageID: second.id } })
|
||||
const inheritedFirst = (yield* session.messages({ sessionID: child.id })).find(
|
||||
(message) => message.type === "user" && message.text === "First",
|
||||
)
|
||||
if (!inheritedFirst) return yield* Effect.die(new Error("Nested fork boundary message not found"))
|
||||
const grandchild = yield* session.fork({ sessionID: child.id, messageID: inheritedFirst.id })
|
||||
const grandchild = yield* session.fork({
|
||||
sessionID: child.id,
|
||||
boundary: { type: "before", messageID: inheritedFirst.id },
|
||||
})
|
||||
|
||||
expect(
|
||||
yield* (yield* Database.Service).db
|
||||
|
|
@ -1172,8 +1174,8 @@ describe("SessionRunnerLLM", () => {
|
|||
.where(eq(InstructionStateTable.session_id, grandchild.id))
|
||||
.get(),
|
||||
).toMatchObject({
|
||||
initial_values: { "test/context": Instructions.hash("Initial context") },
|
||||
current_values: { "test/context": Instructions.hash("Initial context") },
|
||||
initial_values: { "test/context": Instructions.hash("Changed context") },
|
||||
current_values: { "test/context": Instructions.hash("Changed context") },
|
||||
})
|
||||
return undefined
|
||||
}),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue