Import source: app/backend/src/routes/rag-gateway
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
import type { FastifyPluginAsync, FastifyRequest } from 'fastify';
|
||||
import { z } from 'zod';
|
||||
import { prisma } from '../db.js';
|
||||
import { checkRateLimit, rateLimitResponse } from '../lib/rate-limit.js';
|
||||
|
||||
function stripHtmlToMarkdown(html: string): string {
|
||||
if (!html) return '';
|
||||
return html
|
||||
.replace(/<h[1-6][^>]*>(.*?)<\/h[1-6]>/gi, '\n## $1\n')
|
||||
.replace(/<p[^>]*>(.*?)<\/p>/gi, '\n$1\n')
|
||||
.replace(/<a[^>]*href="([^"]+)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)')
|
||||
.replace(/<strong[^>]*>(.*?)<\/strong>/gi, '**$1**')
|
||||
.replace(/<em[^>]*>(.*?)<\/em>/gi, '*$1*')
|
||||
.replace(/<[^>]+>/g, '')
|
||||
.replace(/\n\s*\n/g, '\n\n')
|
||||
.trim();
|
||||
}
|
||||
|
||||
export const ragRoutes: FastifyPluginAsync = async (app) => {
|
||||
app.get('/api/v1/rag/:slug', async (req, reply) => {
|
||||
const { slug } = req.params as { slug: string };
|
||||
const article = await prisma.article.findUnique({
|
||||
where: { slug },
|
||||
select: { title: true, summary: true, content: true, dateModified: true }
|
||||
});
|
||||
|
||||
if (!article) return reply.code(404).send('# Not Found');
|
||||
|
||||
reply.header('Content-Type', 'text/markdown; charset=utf-8');
|
||||
|
||||
let markdown = `# ${article.title}\n\n`;
|
||||
if (article.summary) markdown += `> ${article.summary}\n\n`;
|
||||
markdown += `Last Modified: ${article.dateModified?.toISOString()}\n\n`;
|
||||
markdown += stripHtmlToMarkdown(article.content);
|
||||
|
||||
return markdown;
|
||||
});
|
||||
|
||||
app.post('/api/v1/search/semantic', async (req: FastifyRequest, reply) => {
|
||||
const rl = await checkRateLimit(req.ip, 'semantic', 10, 60);
|
||||
if (rl.limited) return rateLimitResponse(reply, rl.retryAfter);
|
||||
|
||||
const schema = z.object({ query: z.string().min(1), limit: z.number().max(20).optional() });
|
||||
const parsed = schema.safeParse(req.body);
|
||||
if (!parsed.success) return reply.code(400).send({ error: 'invalid_input' });
|
||||
|
||||
const { semanticSearch } = await import('../lib/embeddings.js');
|
||||
const results = await semanticSearch(parsed.data.query, parsed.data.limit || 5);
|
||||
return { results };
|
||||
});
|
||||
};
|
||||
Reference in New Issue
Block a user