refactor article query parameters and improve error messages

This commit is contained in:
ImBenji
2026-04-17 00:27:32 +01:00
parent c80558fd50
commit b298be6108
4 changed files with 337 additions and 20 deletions
+31
View File
@@ -41,6 +41,33 @@ function getRetryDelay(attempt, response) {
return baseDelay + Math.floor(Math.random() * 250);
}
function getErrorCode(error) {
return String(error?.code || error?.cause?.code || '').trim();
}
function isRetryableError(error) {
const code = getErrorCode(error);
const message = String(error?.message || '').toLowerCase();
if (code === 'UNABLE_TO_VERIFY_LEAF_SIGNATURE') {
return false;
}
if (error?.name === 'TimeoutError') {
return true;
}
return [
'UND_ERR_SOCKET',
'UND_ERR_CONNECT_TIMEOUT',
'ECONNRESET',
'ECONNREFUSED',
'ETIMEDOUT',
'EAI_AGAIN',
'ENETUNREACH',
].includes(code) || message.includes('other side closed');
}
async function fetchWithPolicy(url, options = {}) {
const {
timeout = 20000,
@@ -73,6 +100,10 @@ async function fetchWithPolicy(url, options = {}) {
lastError = error;
} catch (error) {
lastError = error;
if (!isRetryableError(error)) {
throw error;
}
}
if (attempt < retries) {
+9 -9
View File
@@ -11,9 +11,9 @@ function buildArticlesQuery(query) {
const params = [];
const includeEmbedding = String(query.include_embedding || '').toLowerCase() === 'true';
if (query.q) {
if (query.keyword) {
conditions.push('(title LIKE ? OR description LIKE ? OR content LIKE ?)');
const keyword = `%${query.q}%`;
const keyword = `%${query.keyword}%`;
params.push(keyword, keyword, keyword);
}
@@ -56,16 +56,16 @@ async function articleRoutes(fastify) {
const query = request.query || {};
if (query.include_embedding) {
reply.code(400);
return { error: 'Embeddings are not returned directly. Use similar_to for vector search.' };
return { error: 'Embeddings are not returned directly. Use similar_to_article for vector search.' };
}
if (query.topic !== undefined) {
if (query.semantic !== undefined) {
const limit = Number.parseInt(query.limit, 10);
const embedding = await getOrCreateQueryEmbedding(query.topic);
const embedding = await getOrCreateQueryEmbedding(query.semantic);
if (!embedding) {
reply.code(400);
return { error: 'Topic must not be empty' };
return { error: 'Semantic query must not be empty' };
}
const neighbors = findArticlesByEmbedding(
@@ -93,9 +93,9 @@ async function articleRoutes(fastify) {
.filter(Boolean);
}
if (query.similar_to) {
if (query.similar_to_article) {
const limit = Number.parseInt(query.limit, 10);
const articleId = Number.parseInt(query.similar_to, 10);
const articleId = Number.parseInt(query.similar_to_article, 10);
const neighbors = findSimilarArticles(
articleId,
Number.isFinite(limit) && limit > 0 ? Math.min(limit, 100) : 20
@@ -134,7 +134,7 @@ async function articleRoutes(fastify) {
fastify.get('/articles/:id', async (request, reply) => {
if (String((request.query || {}).include_embedding || '').toLowerCase() === 'true') {
reply.code(400);
return { error: 'Embeddings are not returned directly. Use similar_to for vector search.' };
return { error: 'Embeddings are not returned directly. Use similar_to_article for vector search.' };
}
const article = db.prepare(`