fix(context-injector): use deterministic synthetic part ID for cache stability

This commit is contained in:
YeonGyu-Kim 2026-02-28 13:30:49 +09:00
parent cc6ab1addc
commit 4d8360c72f
2 changed files with 46 additions and 1 deletions

View File

@ -64,6 +64,51 @@ describe("createContextInjectorMessagesTransformHook", () => {
expect(output.messages[2].parts[1].text).toBe("Second message") expect(output.messages[2].parts[1].text).toBe("Second message")
}) })
it("uses deterministic synthetic part ID across repeated transforms", async () => {
// given
const hook = createContextInjectorMessagesTransformHook(collector)
const sessionID = "ses_transform_deterministic"
const baseMessage = createMockMessage("user", "Stable message", sessionID)
collector.register(sessionID, {
id: "ctx-1",
source: "keyword-detector",
content: "Injected context",
})
const firstOutput = {
messages: [structuredClone(baseMessage)],
}
// when
await hook["experimental.chat.messages.transform"]!({}, firstOutput)
// then
const firstSyntheticPart = firstOutput.messages[0].parts[0]
expect(
"synthetic" in firstSyntheticPart && firstSyntheticPart.synthetic === true
).toBe(true)
// given
collector.register(sessionID, {
id: "ctx-2",
source: "keyword-detector",
content: "Injected context",
})
const secondOutput = {
messages: [structuredClone(baseMessage)],
}
// when
await hook["experimental.chat.messages.transform"]!({}, secondOutput)
// then
const secondSyntheticPart = secondOutput.messages[0].parts[0]
expect(
"synthetic" in secondSyntheticPart && secondSyntheticPart.synthetic === true
).toBe(true)
expect(secondSyntheticPart.id).toBe(firstSyntheticPart.id)
})
it("does nothing when no pending context", async () => { it("does nothing when no pending context", async () => {
// given // given
const hook = createContextInjectorMessagesTransformHook(collector) const hook = createContextInjectorMessagesTransformHook(collector)

View File

@ -148,7 +148,7 @@ export function createContextInjectorMessagesTransformHook(
// synthetic part pattern (minimal fields) // synthetic part pattern (minimal fields)
const syntheticPart = { const syntheticPart = {
id: `synthetic_hook_${Date.now()}`, id: `synthetic_hook_${sessionID}`,
messageID: lastUserMessage.info.id, messageID: lastUserMessage.info.id,
sessionID: (lastUserMessage.info as { sessionID?: string }).sessionID ?? "", sessionID: (lastUserMessage.info as { sessionID?: string }).sessionID ?? "",
type: "text" as const, type: "text" as const,