返回 DeepSeek-Reasonix
transcriptMarkdownCache.ts
根目录 / desktop / frontend / src / lib / transcriptMarkdownCache.ts
1 import type { MarkdownBlock } from "./markdownPipeline";
2
3 export interface ParsedMarkdownValue {
4 source: string;
5 blocks: MarkdownBlock[];
6 selectionText: string;
7 selectionRevision: number;
8 /** Source/projection UTF-16 bytes plus the estimated HAST weight. */
9 bytes: number;
10 }
11
12 /** Byte-bounded LRU whose active selection entries may be pinned. */
13 export class TranscriptMarkdownCache {
14 private readonly entries = new Map<string, { value: ParsedMarkdownValue; bytes: number }>();
15 private readonly pins = new Map<string, number>();
16 bytes = 0;
17 evictions = 0;
18
19 constructor(readonly budgetBytes: number) {}
20
21 private key(entryId: string, revision: number): string {
22 return `${entryId}@${revision}`;
23 }
24
25 get(entryId: string, revision: number): ParsedMarkdownValue | undefined {
26 const key = this.key(entryId, revision);
27 const entry = this.entries.get(key);
28 if (!entry) return undefined;
29 this.entries.delete(key);
30 this.entries.set(key, entry);
31 return entry.value;
32 }
33
34 set(entryId: string, revision: number, value: ParsedMarkdownValue): void {
35 const key = this.key(entryId, revision);
36 const previous = this.entries.get(key);
37 if (previous) this.bytes -= previous.bytes;
38 const bytes = Math.max(0, value.bytes);
39 this.entries.set(key, { value, bytes });
40 this.bytes += bytes;
41 this.enforceBudget();
42 }
43
44 pin(entryId: string, revision: number): () => void {
45 const key = this.key(entryId, revision);
46 this.pins.set(key, (this.pins.get(key) ?? 0) + 1);
47 let released = false;
48 return () => {
49 if (released) return;
50 released = true;
51 const count = this.pins.get(key) ?? 0;
52 if (count <= 1) this.pins.delete(key);
53 else this.pins.set(key, count - 1);
54 this.enforceBudget();
55 };
56 }
57
58 size(): number {
59 return this.entries.size;
60 }
61
62 private enforceBudget(): void {
63 while (this.bytes > this.budgetBytes && this.entries.size > 1) {
64 const victimKey = Array.from(this.entries.keys()).find((key) => !this.pins.has(key));
65 if (!victimKey) break;
66 const victim = this.entries.get(victimKey);
67 if (victim) this.bytes -= victim.bytes;
68 this.entries.delete(victimKey);
69 this.evictions += 1;
70 }
71 }
72 }
73
73 lines TYPESCRIPT