feat(core): persist v2 session context epochs (#30789)

This commit is contained in:
Kit Langton 2026-06-04 23:26:43 -04:00 committed by GitHub
commit 1af8dafd3e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
45 changed files with 4861 additions and 521 deletions

View file

@ -125,26 +125,65 @@ describe("Anthropic Messages route", () => {
}),
)
it.effect("rejects invalid native chronological system update placement", () =>
it.effect("falls back for unsupported native chronological system update placement", () =>
Effect.gen(function* () {
const placementError = (messages: Parameters<typeof LLM.request>[0]["messages"]) =>
LLMClient.prepare(LLM.request({ model: opus48, messages, cache: "none" })).pipe(Effect.flip)
expect(
(yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
model: opus48,
messages: [Message.assistant("Plain."), Message.system("After plain assistant.")],
cache: "none",
}),
)).body.messages,
).toEqual([
{ role: "assistant", content: [{ type: "text", text: "Plain." }] },
{
role: "user",
content: [{ type: "text", text: "<system-update>\nAfter plain assistant.\n</system-update>" }],
},
])
expect(
(yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({ model: opus48, messages: [Message.system("First.")], cache: "none" }),
)).body.messages,
).toEqual([{ role: "user", content: [{ type: "text", text: "<system-update>\nFirst.\n</system-update>" }] }])
expect(
(yield* LLMClient.prepare<AnthropicMessages.AnthropicMessagesBody>(
LLM.request({
model: opus48,
messages: [Message.user("Before."), Message.system("One."), Message.system("Two.")],
cache: "none",
}),
)).body.messages,
).toEqual([
{
role: "user",
content: [
{ type: "text", text: "Before." },
{ type: "text", text: "<system-update>\nOne.\n</system-update>" },
{ type: "text", text: "<system-update>\nTwo.\n</system-update>" },
],
},
])
}),
)
expect((yield* placementError([Message.system("First.")])).message).toContain("cannot be the first message")
expect(
(yield* placementError([Message.user("Before."), Message.system("One."), Message.system("Two.")])).message,
).toContain("cannot be consecutive")
expect(
(yield* placementError([Message.assistant("Plain."), Message.system("After plain assistant.")])).message,
).toContain("must follow a user message, tool result, or assistant server tool use")
expect(
(yield* placementError([
Message.user("Use the tool."),
Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: {} })]),
Message.system("Too early."),
Message.tool({ id: "call_1", name: "lookup", result: "Done." }),
])).message,
).toContain("cannot appear between a local tool call and its tool result")
it.effect("rejects a system update between a local tool call and its result", () =>
Effect.gen(function* () {
const error = yield* LLMClient.prepare(
LLM.request({
model: opus48,
messages: [
Message.user("Use the tool."),
Message.assistant([ToolCallPart.make({ id: "call_1", name: "lookup", input: {} })]),
Message.system("Too early."),
Message.tool({ id: "call_1", name: "lookup", result: "Done." }),
],
cache: "none",
}),
).pipe(Effect.flip)
expect(error.message).toContain("system updates cannot split a local tool call from its tool result")
}),
)

View file

@ -123,7 +123,11 @@ describeRecordedGoldenScenarios([
prefix: "gemini",
model: gemini,
requires: ["GOOGLE_GENERATIVE_AI_API_KEY"],
scenarios: [{ id: "text", maxTokens: 80 }, "tool-call", { id: "image", maxTokens: 160 }],
scenarios: [
{ id: "text", maxTokens: 80 },
"tool-call",
{ id: "image", maxTokens: 160 },
],
},
{
name: "xAI Grok 3 Mini",

View file

@ -1,5 +1,5 @@
import { expect } from "bun:test"
import { Effect, Schema, Stream } from "effect"
import { Effect, Schema } from "effect"
import {
LLM,
LLMEvent,

View file

@ -2,7 +2,15 @@ import { describe, expect, test } from "bun:test"
import { Schema } from "effect"
import * as OpenAIChat from "../src/protocols/openai-chat"
import * as OpenAIResponses from "../src/protocols/openai-responses"
import { ContentPart, LLMEvent, LLMRequest, Model, ModelID, ProviderID, Usage } from "../src/schema"
import {
ContentPart,
LLMEvent,
LLMRequest,
Model,
ModelID,
ProviderID,
Usage,
} from "../src/schema"
import { ProviderShared } from "../src/protocols/shared"
const model = new Model({
@ -43,17 +51,6 @@ describe("llm schema", () => {
expect(decoded.model.route.id).toBe("openai-responses")
})
test("decodes chronological system messages", () => {
const decoded = decodeLLMRequest({
model,
system: [],
messages: [{ role: "system", content: [{ type: "text", text: "Operator update." }] }],
tools: [],
})
expect(decoded.messages[0]).toMatchObject({ role: "system", content: [{ type: "text", text: "Operator update." }] })
})
test("rejects invalid event type", () => {
expect(() => decodeLLMEvent({ type: "bogus" })).toThrow()
})