feat(core): deterministic session log replay with synced watermark (#35040)
This commit is contained in:
parent
1aae92c42a
commit
57e9e9771d
16 changed files with 205 additions and 87 deletions
|
|
@ -3,7 +3,7 @@ import { DateTime, Effect, Stream } from "effect"
|
|||
import { HttpClient, HttpClientResponse } from "effect/unstable/http"
|
||||
import { AbsolutePath, Agent, Event, Location, Model, OpenCode, Prompt, Session, SessionMessage } from "../src/effect"
|
||||
|
||||
const caughtUp = { type: "log.caught_up" as const, aggregateID: "ses_test", seq: Event.Seq.make(1) }
|
||||
const synced = { type: "log.synced" as const, aggregateID: "ses_test", seq: Event.Seq.make(1) }
|
||||
|
||||
test("session.get returns the decoded Effect projection", async () => {
|
||||
const httpClient = HttpClient.make((request) =>
|
||||
|
|
@ -70,7 +70,7 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
|
|||
return Effect.succeed(
|
||||
HttpClientResponse.fromWeb(
|
||||
request,
|
||||
new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\ndata: ${JSON.stringify(caughtUp)}\n\n`, {
|
||||
new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\ndata: ${JSON.stringify(synced)}\n\n`, {
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
}),
|
||||
),
|
||||
|
|
@ -149,11 +149,11 @@ test("session methods retain decoded Effect inputs and outputs", async () => {
|
|||
expect(result.context).toEqual([])
|
||||
expect(logQueries[0]).toEqual({ after: "0" })
|
||||
const logged = Array.from(result.log)
|
||||
expect(logged.map((item) => item.type)).toEqual(["session.next.model.switched", "log.caught_up"])
|
||||
expect(logged.map((item) => item.type)).toEqual(["session.next.model.switched", "log.synced"])
|
||||
expect(logged[0]?.type === "session.next.model.switched" && DateTime.toEpochMillis(logged[0].data.timestamp)).toBe(
|
||||
1_717_171_717_000,
|
||||
)
|
||||
expect(logged.at(-1)).toEqual(caughtUp)
|
||||
expect(logged.at(-1)).toEqual(synced)
|
||||
expect(result.message).toEqual(expect.objectContaining({ id: "msg_model", type: "model-switched" }))
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ test("session methods use the public HTTP contract", async () => {
|
|||
})
|
||||
}
|
||||
if (url.includes("/log")) {
|
||||
return new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\ndata: ${JSON.stringify(caughtUp)}\n\n`, {
|
||||
return new Response(`data: ${JSON.stringify(modelSwitchedEvent)}\n\ndata: ${JSON.stringify(synced)}\n\n`, {
|
||||
headers: { "content-type": "text/event-stream" },
|
||||
})
|
||||
}
|
||||
|
|
@ -273,7 +273,7 @@ test("session methods use the public HTTP contract", async () => {
|
|||
expect(created.id).toBe("ses_test")
|
||||
expect(admitted.id).toBe("msg_test")
|
||||
expect(context).toEqual([])
|
||||
expect(log).toEqual([modelSwitchedEvent, caughtUp])
|
||||
expect(log).toEqual([modelSwitchedEvent, synced])
|
||||
expect(message).toEqual(modelSwitchedMessage)
|
||||
expect(requests.map((request) => [request.init?.method, request.url])).toEqual([
|
||||
["GET", "http://localhost:3000/api/session?limit=10&order=desc&parentID=null"],
|
||||
|
|
@ -368,7 +368,7 @@ const modelSwitchedMessage = {
|
|||
model: { id: "claude", providerID: "anthropic" },
|
||||
}
|
||||
|
||||
const caughtUp = { type: "log.caught_up", aggregateID: "ses_test", seq: 1 }
|
||||
const synced = { type: "log.synced", aggregateID: "ses_test", seq: 1 }
|
||||
|
||||
const modelSwitchedEvent = {
|
||||
id: "evt_model",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue