- remove legacy contexts surfaces from the app, MCP bridge, standalone server, and docs - keep getContext and retrieveQueryContext while aligning the simplified graph-only contract - harden dev startup migration behavior and disable the accidental chat surface in open source Generated with Codex
131 lines
4.2 KiB
TypeScript
131 lines
4.2 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
import { getSQLiteClient } from '@/services/database/sqlite-client';
|
|
import { chunkService } from '@/services/database/chunks';
|
|
|
|
export async function GET() {
|
|
try {
|
|
const sqlite = getSQLiteClient();
|
|
// Test basic database connection
|
|
const connectionTest = await sqlite.testConnection();
|
|
if (!connectionTest) {
|
|
return NextResponse.json({
|
|
status: 'error',
|
|
message: 'Database connection failed',
|
|
details: null
|
|
});
|
|
}
|
|
|
|
// Check if vector extension is loaded
|
|
const vectorExtensionTest = await sqlite.checkVectorExtension();
|
|
let vectorStats = null;
|
|
let chunkStats = null;
|
|
let vectorHealth = vectorExtensionTest ? 'healthy' : 'unavailable';
|
|
|
|
try {
|
|
const totalChunks = await chunkService.getChunkCount();
|
|
chunkStats = {
|
|
total_chunks: totalChunks,
|
|
vectorized_chunks: null,
|
|
missing_embeddings: null,
|
|
coverage_percentage: null,
|
|
};
|
|
|
|
if (vectorExtensionTest) {
|
|
try {
|
|
const chunksWithoutEmbeddings = await chunkService.getChunksWithoutEmbeddings();
|
|
const vectorizedCount = totalChunks - chunksWithoutEmbeddings.length;
|
|
const result = sqlite.query('SELECT COUNT(*) as count FROM vec_chunks');
|
|
const vecCount = Number(result.rows[0].count);
|
|
|
|
chunkStats = {
|
|
total_chunks: totalChunks,
|
|
vectorized_chunks: vectorizedCount,
|
|
missing_embeddings: chunksWithoutEmbeddings.length,
|
|
coverage_percentage: totalChunks > 0 ? Math.round((vectorizedCount / totalChunks) * 100) : 0
|
|
};
|
|
|
|
vectorStats = {
|
|
vec_chunks_count: vecCount,
|
|
matches_chunk_embeddings: vecCount === vectorizedCount
|
|
};
|
|
|
|
vectorHealth = vecCount === vectorizedCount ? 'healthy' : 'inconsistent';
|
|
} catch (vecError: any) {
|
|
vectorHealth = 'corrupted';
|
|
vectorStats = {
|
|
error: vecError.message,
|
|
suggestion: 'Vector table may be corrupted and need recreation'
|
|
};
|
|
}
|
|
} else {
|
|
vectorHealth = 'unavailable';
|
|
vectorStats = {
|
|
extension_loaded: false,
|
|
reason: 'Vector extension unavailable in this environment.',
|
|
};
|
|
}
|
|
|
|
} catch (error: any) {
|
|
return NextResponse.json({
|
|
status: 'error',
|
|
message: 'Failed to collect vector statistics',
|
|
details: error.message
|
|
});
|
|
}
|
|
|
|
return NextResponse.json({
|
|
status: 'success',
|
|
data: {
|
|
database_connected: connectionTest,
|
|
vector_extension_loaded: vectorExtensionTest,
|
|
vector_capability: {
|
|
available: vectorExtensionTest,
|
|
backend: vectorExtensionTest ? 'sqlite-vec' : 'unavailable',
|
|
},
|
|
vector_health: vectorHealth,
|
|
chunk_stats: chunkStats,
|
|
vector_stats: vectorStats,
|
|
recommendations: generateRecommendations(vectorHealth, chunkStats, vectorStats)
|
|
}
|
|
});
|
|
|
|
} catch (error: any) {
|
|
console.error('Vector health check failed:', error);
|
|
return NextResponse.json({
|
|
status: 'error',
|
|
message: 'Health check failed',
|
|
details: error.message
|
|
});
|
|
}
|
|
}
|
|
|
|
function generateRecommendations(
|
|
vectorHealth: string,
|
|
chunkStats: any,
|
|
vectorStats: any
|
|
): string[] {
|
|
const recommendations: string[] = [];
|
|
|
|
if (vectorHealth === 'corrupted') {
|
|
recommendations.push('Vector tables are corrupted - restart the application to trigger automatic healing');
|
|
}
|
|
|
|
if (vectorHealth === 'unavailable') {
|
|
recommendations.push('Semantic/vector search is unavailable. Install sqlite-vec for your platform or switch to Qdrant.');
|
|
}
|
|
|
|
if (chunkStats && typeof chunkStats.coverage_percentage === 'number' && chunkStats.coverage_percentage < 95) {
|
|
recommendations.push(`${chunkStats.missing_embeddings} chunks missing embeddings - consider running embedding generation`);
|
|
}
|
|
|
|
if (vectorStats && !vectorStats.matches_chunk_embeddings) {
|
|
recommendations.push('Vector count does not match chunk embeddings - database inconsistency detected');
|
|
}
|
|
|
|
if (recommendations.length === 0) {
|
|
recommendations.push('Vector search system is healthy');
|
|
}
|
|
|
|
return recommendations;
|
|
}
|