mirror of
https://github.com/stablyai/orca.git
synced 2026-09-22 00:02:31 +00:00
fix(memory): bound native chat enrichment cache
This commit is contained in:
@@ -7,6 +7,8 @@ import {
|
||||
import {
|
||||
clearNativeChatModelEnrichmentForTests,
|
||||
ensureNativeChatModelEnrichment,
|
||||
getNativeChatModelEnrichmentEntryCountForTests,
|
||||
NATIVE_CHAT_MODEL_ENRICHMENT_MAX_ENTRIES,
|
||||
readNativeChatEnrichedModels,
|
||||
resolveNativeChatLaunchSessionOptions,
|
||||
subscribeNativeChatEnrichedModels
|
||||
@@ -27,6 +29,22 @@ describe('native chat session option enrichment', () => {
|
||||
mocks.discoverRuntimeCommitMessageModels.mockReset()
|
||||
})
|
||||
|
||||
it('bounds settled host enrichment entries', async () => {
|
||||
for (let index = 0; index < NATIVE_CHAT_MODEL_ENRICHMENT_MAX_ENTRIES + 4; index += 1) {
|
||||
ensureNativeChatModelEnrichment({
|
||||
agent: 'cursor',
|
||||
hostKey: `ssh:${index}`,
|
||||
discover: async () => []
|
||||
})
|
||||
}
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
expect(getNativeChatModelEnrichmentEntryCountForTests()).toBe(
|
||||
NATIVE_CHAT_MODEL_ENRICHMENT_MAX_ENTRIES
|
||||
)
|
||||
})
|
||||
|
||||
it('keeps reads synchronous while one host-scoped probe is in flight', async () => {
|
||||
let resolveDiscovery: ((models: CatalogModel[]) => void) | undefined
|
||||
const discover = vi.fn(
|
||||
|
||||
@@ -19,6 +19,21 @@ type CatalogEnrichmentEntry = {
|
||||
}
|
||||
|
||||
const enrichmentByAgentHost = new Map<string, CatalogEnrichmentEntry>()
|
||||
export const NATIVE_CHAT_MODEL_ENRICHMENT_MAX_ENTRIES = 256
|
||||
|
||||
function retainEnrichmentEntry(key: string, entry: CatalogEnrichmentEntry): void {
|
||||
enrichmentByAgentHost.delete(key)
|
||||
enrichmentByAgentHost.set(key, entry)
|
||||
while (enrichmentByAgentHost.size > NATIVE_CHAT_MODEL_ENRICHMENT_MAX_ENTRIES) {
|
||||
const evictable = [...enrichmentByAgentHost].find(
|
||||
([, candidate]) => candidate.listeners.size === 0 && candidate.state !== 'pending'
|
||||
)
|
||||
if (!evictable) {
|
||||
return
|
||||
}
|
||||
enrichmentByAgentHost.delete(evictable[0])
|
||||
}
|
||||
}
|
||||
|
||||
function enrichmentKey(agent: AgentType, hostKey: string): string {
|
||||
return JSON.stringify([agent, hostKey])
|
||||
@@ -45,7 +60,7 @@ export function subscribeNativeChatEnrichedModels(
|
||||
listeners: new Set<(models: CatalogModel[]) => void>()
|
||||
}
|
||||
entry.listeners.add(listener)
|
||||
enrichmentByAgentHost.set(key, entry)
|
||||
retainEnrichmentEntry(key, entry)
|
||||
return () => entry.listeners.delete(listener)
|
||||
}
|
||||
|
||||
@@ -90,7 +105,7 @@ export function ensureNativeChatModelEnrichment(args: {
|
||||
listeners: new Set()
|
||||
}
|
||||
entry.state = 'pending'
|
||||
enrichmentByAgentHost.set(key, entry)
|
||||
retainEnrichmentEntry(key, entry)
|
||||
|
||||
// Why: model discovery must never delay rendering or launching; the seed is
|
||||
// immediately usable while this once-per-host probe runs in the background.
|
||||
@@ -98,6 +113,7 @@ export function ensureNativeChatModelEnrichment(args: {
|
||||
.discover()
|
||||
.then((discovered) => {
|
||||
entry.state = 'settled'
|
||||
retainEnrichmentEntry(key, entry)
|
||||
if (!discovered || discovered.length === 0) {
|
||||
return
|
||||
}
|
||||
@@ -113,9 +129,15 @@ export function ensureNativeChatModelEnrichment(args: {
|
||||
})
|
||||
.catch(() => {
|
||||
entry.state = 'settled'
|
||||
retainEnrichmentEntry(key, entry)
|
||||
})
|
||||
}
|
||||
|
||||
export function clearNativeChatModelEnrichmentForTests(): void {
|
||||
enrichmentByAgentHost.clear()
|
||||
}
|
||||
|
||||
/** @internal - exposed for leak-regression tests only. */
|
||||
export function getNativeChatModelEnrichmentEntryCountForTests(): number {
|
||||
return enrichmentByAgentHost.size
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user