mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-01 02:01:40 +00:00
fix(tools): escape delimiters in makeTurnKey to prevent cache collision
This commit is contained in:
parent
c927c98f2c
commit
0537e8ca34
2 changed files with 43 additions and 1 deletions
|
|
@ -5,6 +5,9 @@ import type { MemoryMode } from "./types"
|
|||
* Generic memory cache for storing per-turn memories to avoid redundant API calls.
|
||||
* Used to cache memory retrieval results during tool-call loops within the same turn.
|
||||
*/
|
||||
const escapeKeySegment = (segment: string): string =>
|
||||
segment.replace(/%/g, "%25").replace(/:/g, "%3A")
|
||||
|
||||
export class MemoryCache<T = string> {
|
||||
private cache: LRUCache<string, T> = new LRUCache({ max: 100 })
|
||||
|
||||
|
|
@ -25,7 +28,9 @@ export class MemoryCache<T = string> {
|
|||
message: string,
|
||||
): string {
|
||||
const normalizedMessage = message.trim().replace(/\s+/g, " ")
|
||||
return `${containerTag}:${threadId || ""}:${mode}:${normalizedMessage}`
|
||||
const safeContainerTag = escapeKeySegment(containerTag)
|
||||
const safeThreadId = escapeKeySegment(threadId ?? "")
|
||||
return `${safeContainerTag}:${safeThreadId}:${mode}:${normalizedMessage}`
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { describe, expect, it } from "vitest"
|
||||
import { makeTurnKey } from "./shared/cache"
|
||||
import {
|
||||
DEFAULT_VALUES,
|
||||
SEARCH_LIMIT_BOUNDS,
|
||||
|
|
@ -130,3 +131,39 @@ describe("deduplicateMemoriesForMode", () => {
|
|||
expect(deduplicated.searchResults).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe("makeTurnKey", () => {
|
||||
it("generates predictable turn key for standard inputs", () => {
|
||||
expect(makeTurnKey("user-123", "thread-456", "full", "hello world")).toBe(
|
||||
"user-123:thread-456:full:hello world",
|
||||
)
|
||||
})
|
||||
|
||||
it("normalizes and collapses whitespace in message", () => {
|
||||
expect(
|
||||
makeTurnKey("user-123", "thread-456", "full", " hello world \n "),
|
||||
).toBe("user-123:thread-456:full:hello world")
|
||||
})
|
||||
|
||||
it("handles undefined threadId cleanly", () => {
|
||||
expect(makeTurnKey("user-123", undefined, "profile", "test")).toBe(
|
||||
"user-123::profile:test",
|
||||
)
|
||||
})
|
||||
|
||||
it("escapes colons to prevent cache key collisions between tag and threadId", () => {
|
||||
const keyA = makeTurnKey("user:123", "456", "profile", "hi")
|
||||
const keyB = makeTurnKey("user", "123:456", "profile", "hi")
|
||||
expect(keyA).toBe("user%3A123:456:profile:hi")
|
||||
expect(keyB).toBe("user:123%3A456:profile:hi")
|
||||
expect(keyA).not.toBe(keyB)
|
||||
})
|
||||
|
||||
it("escapes percent signs to avoid ambiguity with encoded sequences", () => {
|
||||
const keyA = makeTurnKey("user%3A123", "456", "profile", "hi")
|
||||
const keyB = makeTurnKey("user:123", "456", "profile", "hi")
|
||||
expect(keyA).toBe("user%253A123:456:profile:hi")
|
||||
expect(keyB).toBe("user%3A123:456:profile:hi")
|
||||
expect(keyA).not.toBe(keyB)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue