Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions assistant/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,15 +331,6 @@ sessions
console.log('Qdrant collection not found or not reachable (skipped)');
}

// Restart the daemon so its in-memory Qdrant client drops the stale
// collectionReady flag and will re-create the collection on next use.
const status = getDaemonStatus();
if (status.running) {
await stopDaemon();
await startDaemon();
console.log('Daemon restarted');
}

console.log('Done.');
Comment thread
siddseethepalli marked this conversation as resolved.
});

Expand Down
122 changes: 99 additions & 23 deletions assistant/src/memory/qdrant-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,20 +146,43 @@ export class VellumQdrantClient {
const existing = await this.findByTarget(targetType, targetId);
const pointId = existing ?? uuid();

await this.client.upsert(this.collection, {
wait: true,
points: [
{
id: pointId,
vector,
payload: {
target_type: targetType,
target_id: targetId,
...payload,
try {
await this.client.upsert(this.collection, {
wait: true,
points: [
{
id: pointId,
vector,
payload: {
target_type: targetType,
target_id: targetId,
...payload,
},
},
},
],
});
],
});
} catch (err) {
if (this.isCollectionMissing(err)) {
this.collectionReady = false;
await this.ensureCollection();
await this.client.upsert(this.collection, {
wait: true,
points: [
{
id: pointId,
vector,
payload: {
target_type: targetType,
target_id: targetId,
...payload,
},
},
],
});
} else {
throw err;
}
}

return pointId;
}
Expand All @@ -171,13 +194,30 @@ export class VellumQdrantClient {
): Promise<QdrantSearchResult[]> {
await this.ensureCollection();

const results = await this.client.search(this.collection, {
vector,
limit,
with_payload: true,
score_threshold: 0.0,
filter: filter as Parameters<QdrantRestClient['search']>[1]['filter'],
});
let results;
try {
results = await this.client.search(this.collection, {
vector,
limit,
with_payload: true,
score_threshold: 0.0,
filter: filter as Parameters<QdrantRestClient['search']>[1]['filter'],
});
} catch (err) {
if (this.isCollectionMissing(err)) {
this.collectionReady = false;
await this.ensureCollection();
results = await this.client.search(this.collection, {
vector,
limit,
with_payload: true,
score_threshold: 0.0,
filter: filter as Parameters<QdrantRestClient['search']>[1]['filter'],
});
} else {
throw err;
}
}

return results.map((result) => ({
id: typeof result.id === 'string' ? result.id : String(result.id),
Expand Down Expand Up @@ -235,7 +275,7 @@ export class VellumQdrantClient {
async deleteByTarget(targetType: string, targetId: string): Promise<void> {
await this.ensureCollection();

await this.client.delete(this.collection, {
const doDelete = () => this.client.delete(this.collection, {
wait: true,
filter: {
must: [
Expand All @@ -244,12 +284,35 @@ export class VellumQdrantClient {
],
},
});

try {
await doDelete();
} catch (err) {
if (this.isCollectionMissing(err)) {
this.collectionReady = false;
await this.ensureCollection();
await doDelete();
} else {
throw err;
}
}
}

async count(): Promise<number> {
await this.ensureCollection();
const result = await this.client.count(this.collection, { exact: false });
return result.count;

try {
const result = await this.client.count(this.collection, { exact: false });
return result.count;
} catch (err) {
if (this.isCollectionMissing(err)) {
this.collectionReady = false;
await this.ensureCollection();
const result = await this.client.count(this.collection, { exact: false });
return result.count;
}
throw err;
}
}

async deleteCollection(): Promise<boolean> {
Expand All @@ -265,6 +328,19 @@ export class VellumQdrantClient {
}
}

/**
* Detect "collection not found" errors from Qdrant so callers can
* reset collectionReady and retry after an external deletion
* (e.g. `vellum sessions clear`).
*/
private isCollectionMissing(err: unknown): boolean {
if (err && typeof err === 'object' && 'status' in err && (err as { status: number }).status === 404) {
return true;
}
const msg = err instanceof Error ? err.message : String(err);
return msg.includes('Not found') || msg.includes('doesn\'t exist') || msg.includes('not found');
}

private async findByTarget(targetType: string, targetId: string): Promise<string | null> {
try {
const results = await this.client.scroll(this.collection, {
Expand Down