| 66 | } |
| 67 | |
| 68 | export class InMemoryVectorIndexProvider implements VectorIndexProvider { |
| 69 | private readonly documents = new Map<string, VectorIndexDocument>(); |
| 70 | |
| 71 | async upsert(documents: readonly VectorIndexDocument[]): Promise<void> { |
| 72 | for (const document of documents) { |
| 73 | this.documents.set(document.id, document); |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | async deleteByEntityIds(entityIds: readonly string[]): Promise<void> { |
| 78 | const entityIdSet = new Set(entityIds); |
| 79 | for (const [id, document] of this.documents.entries()) { |
| 80 | if (entityIdSet.has(document.entityId)) { |
| 81 | this.documents.delete(id); |
| 82 | } |
| 83 | } |
| 84 | } |
| 85 | |
| 86 | async search(input: VectorSearchInput): Promise<readonly VectorSearchHit[]> { |
| 87 | const allowedKinds = input.kinds ? new Set(input.kinds) : null; |
| 88 | const allowedTiers = input.tiers ? new Set(input.tiers) : null; |
| 89 | |
| 90 | return [...this.documents.values()] |
| 91 | .filter((document) => document.spaceId === input.spaceId) |
| 92 | .filter((document) => (input.threadId ? !document.threadId || document.threadId === input.threadId : true)) |
| 93 | .filter((document) => (allowedKinds ? allowedKinds.has(document.kind) : true)) |
| 94 | .filter((document) => (allowedTiers ? allowedTiers.has(document.tier) : true)) |
| 95 | .map((document) => ({ |
| 96 | id: document.id, |
| 97 | entityId: document.entityId, |
| 98 | kind: document.kind, |
| 99 | score: lexicalSimilarity(input.query, document.text), |
| 100 | tier: document.tier, |
| 101 | metadata: document.metadata, |
| 102 | })) |
| 103 | .filter((hit) => hit.score > 0) |
| 104 | .toSorted((left, right) => right.score - left.score) |
| 105 | .slice(0, input.topK); |
| 106 | } |
| 107 | } |
nothing calls this directly
no outgoing calls
no test coverage detected