mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 07:12:12 +03:00
merge(F6): backend REST routes (memory + settings/qdrant)
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { deleteMemory, getMemory } from "@/lib/memory/store";
|
||||
import { deleteMemory, getMemory, updateMemory } from "@/lib/memory/store";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { MemoryUpdatePutSchema } from "@/shared/schemas/memory";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function DELETE(request: Request, props: { params: Promise<{ id: string }> }) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
@@ -14,8 +17,8 @@ export async function DELETE(request: Request, props: { params: Promise<{ id: st
|
||||
}
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (err: unknown) {
|
||||
const error = err instanceof Error ? err.message : String(err);
|
||||
return NextResponse.json({ error }, { status: 500 });
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +34,41 @@ export async function GET(request: Request, props: { params: Promise<{ id: strin
|
||||
}
|
||||
return NextResponse.json({ memory });
|
||||
} catch (err: unknown) {
|
||||
const error = err instanceof Error ? err.message : String(err);
|
||||
return NextResponse.json({ error }, { status: 500 });
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: Request, props: { params: Promise<{ id: string }> }) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(MemoryUpdatePutSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const { id } = await props.params;
|
||||
const existing = await getMemory(id);
|
||||
if (!existing) {
|
||||
return NextResponse.json({ error: { message: "Memory not found" } }, { status: 404 });
|
||||
}
|
||||
|
||||
await updateMemory(id, validation.data);
|
||||
return NextResponse.json({ success: true });
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
18
src/app/api/memory/embedding-providers/route.ts
Normal file
18
src/app/api/memory/embedding-providers/route.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { listEmbeddingProviders } from "@/lib/memory/embedding";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const providers = await listEmbeddingProviders();
|
||||
return NextResponse.json({ providers });
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
18
src/app/api/memory/engine-status/route.ts
Normal file
18
src/app/api/memory/engine-status/route.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { engineStatus } from "@/lib/memory/retrieval";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function GET(request: Request) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const status = await engineStatus();
|
||||
return NextResponse.json(status);
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
54
src/app/api/memory/reindex/route.ts
Normal file
54
src/app/api/memory/reindex/route.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { MemoryReindexSchema } from "@/shared/schemas/memory";
|
||||
import { runReindexBatch, getReindexPending } from "@/lib/memory/reindex";
|
||||
import { markAllMemoriesNeedReindex } from "@/lib/localDb";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
import { logger } from "@omniroute/open-sse/utils/logger.ts";
|
||||
|
||||
const log = logger("MEMORY_REINDEX_ROUTE");
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(MemoryReindexSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
const { force } = validation.data;
|
||||
|
||||
try {
|
||||
if (force) {
|
||||
markAllMemoriesNeedReindex();
|
||||
}
|
||||
|
||||
const pending = getReindexPending();
|
||||
|
||||
// Dispatch batch in background — do NOT await (returns immediate response).
|
||||
setImmediate(() => {
|
||||
runReindexBatch(100).catch((err: unknown) => {
|
||||
log.error("memory.reindex.background.fail", {
|
||||
error: sanitizeErrorMessage(err instanceof Error ? err.message : String(err)),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return NextResponse.json({ started: true, pending });
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
58
src/app/api/memory/retrieve-preview/route.ts
Normal file
58
src/app/api/memory/retrieve-preview/route.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { RetrievePreviewSchema } from "@/shared/schemas/memory";
|
||||
import { retrievePreview } from "@/lib/memory/retrieval";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(RetrievePreviewSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
const { query, strategy, maxTokens, apiKeyId, limit } = validation.data;
|
||||
|
||||
try {
|
||||
const bundle = await retrievePreview(apiKeyId ?? null, query, {
|
||||
strategy,
|
||||
maxTokens,
|
||||
limit,
|
||||
});
|
||||
|
||||
const memories = bundle.items.map((item) => ({
|
||||
id: item.memory.id,
|
||||
type: item.memory.type,
|
||||
key: item.memory.key ?? "",
|
||||
content: item.memory.content,
|
||||
score: item.score,
|
||||
tokens: item.tokens,
|
||||
tier: item.tier,
|
||||
vecScore: item.vecScore,
|
||||
ftsScore: item.ftsScore,
|
||||
}));
|
||||
|
||||
return NextResponse.json({
|
||||
memories,
|
||||
resolution: bundle.resolution,
|
||||
totalTokensUsed: bundle.totalTokens,
|
||||
budgetMaxTokens: bundle.budgetMaxTokens,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { MemoryType } from "@/lib/memory/types";
|
||||
import { parsePaginationParams, buildPaginatedResponse } from "@/shared/types/pagination";
|
||||
import { z } from "zod";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
const createMemorySchema = z.object({
|
||||
content: z.string().min(1),
|
||||
@@ -78,8 +79,8 @@ export async function GET(request: Request) {
|
||||
stats,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const error = err instanceof Error ? err.message : String(err);
|
||||
return NextResponse.json({ error }, { status: 500 });
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ export async function POST(request: Request) {
|
||||
const memoryId = await createMemory(validation.data);
|
||||
return NextResponse.json({ success: true, id: memoryId });
|
||||
} catch (err: unknown) {
|
||||
const error = err instanceof Error ? err.message : String(err);
|
||||
return NextResponse.json({ error }, { status: 400 });
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 400 });
|
||||
}
|
||||
}
|
||||
|
||||
42
src/app/api/memory/summarize/route.ts
Normal file
42
src/app/api/memory/summarize/route.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { MemorySummarizeSchema } from "@/shared/schemas/memory";
|
||||
import { summarizeMemoriesOlderThan } from "@/lib/memory/summarization";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
if (authError) return authError;
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(MemorySummarizeSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
const { apiKeyId, olderThanDays, dryRun } = validation.data;
|
||||
|
||||
try {
|
||||
const result = await summarizeMemoriesOlderThan(apiKeyId, olderThanDays, dryRun);
|
||||
return NextResponse.json({
|
||||
candidates: result.candidates,
|
||||
totalTokens: result.totalTokens,
|
||||
deletedCount: result.deletedCount,
|
||||
summaryId: result.summaryId,
|
||||
dryRun: result.dryRun,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,23 +1,14 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { getSettings, updateSettings } from "@/lib/localDb";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import { MemorySettingsExtendedSchema } from "@/shared/schemas/memory";
|
||||
import {
|
||||
invalidateMemorySettingsCache,
|
||||
normalizeMemorySettings,
|
||||
toMemorySettingsUpdates,
|
||||
} from "@/lib/memory/settings";
|
||||
|
||||
const memorySettingsUpdateSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
maxTokens: z.number().int().min(0).max(16000).optional(),
|
||||
retentionDays: z.number().int().min(1).max(365).optional(),
|
||||
strategy: z.enum(["recent", "semantic", "hybrid"]).optional(),
|
||||
skillsEnabled: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
@@ -27,8 +18,9 @@ export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const settings = (await getSettings()) as Record<string, unknown>;
|
||||
return NextResponse.json(normalizeMemorySettings(settings));
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: String(error) }, { status: 500 });
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,25 +29,29 @@ export async function PUT(request: NextRequest) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json({ error: "Invalid JSON body" }, { status: 400 });
|
||||
}
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(memorySettingsUpdateSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return validation.response;
|
||||
}
|
||||
const validation = validateBody(MemorySettingsExtendedSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
try {
|
||||
const updates = toMemorySettingsUpdates(validation.data);
|
||||
const settings = (await updateSettings(updates)) as Record<string, unknown>;
|
||||
invalidateMemorySettingsCache();
|
||||
|
||||
return NextResponse.json(normalizeMemorySettings(settings));
|
||||
} catch (error) {
|
||||
return NextResponse.json({ error: String(error) }, { status: 500 });
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
26
src/app/api/settings/qdrant/cleanup/route.ts
Normal file
26
src/app/api/settings/qdrant/cleanup/route.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { cleanupSemanticMemoryPoints } from "@/lib/memory/qdrant";
|
||||
import { getMemorySettings } from "@/lib/memory/settings";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const memorySettings = await getMemorySettings();
|
||||
const result = await cleanupSemanticMemoryPoints({
|
||||
retentionDays: memorySettings.retentionDays,
|
||||
});
|
||||
return NextResponse.json({
|
||||
ok: result.ok,
|
||||
deletedCount: result.deletedCount,
|
||||
retentionDays: memorySettings.retentionDays,
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
18
src/app/api/settings/qdrant/health/route.ts
Normal file
18
src/app/api/settings/qdrant/health/route.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { checkQdrantHealth } from "@/lib/memory/qdrant";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await checkQdrantHealth();
|
||||
return NextResponse.json(result);
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
86
src/app/api/settings/qdrant/route.ts
Normal file
86
src/app/api/settings/qdrant/route.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { QdrantSettingsUpdateSchema } from "@/shared/schemas/qdrant";
|
||||
import { getQdrantConfig, normalizeQdrantConfig } from "@/lib/memory/qdrant";
|
||||
import { updateSettings, getSettings } from "@/lib/localDb";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
function maskApiKey(apiKey: string | null): { hasApiKey: boolean; apiKeyMasked: string | null } {
|
||||
if (!apiKey || apiKey.trim().length === 0) {
|
||||
return { hasApiKey: false, apiKeyMasked: null };
|
||||
}
|
||||
const trimmed = apiKey.trim();
|
||||
const last4 = trimmed.slice(-4);
|
||||
return { hasApiKey: true, apiKeyMasked: `***${last4}` };
|
||||
}
|
||||
|
||||
function buildQdrantSettingsResponse(settings: Record<string, unknown>) {
|
||||
const cfg = normalizeQdrantConfig(settings);
|
||||
const { hasApiKey, apiKeyMasked } = maskApiKey(cfg.apiKey);
|
||||
return {
|
||||
enabled: cfg.enabled,
|
||||
host: cfg.host,
|
||||
port: cfg.port,
|
||||
collection: cfg.collection,
|
||||
embeddingModel: cfg.embeddingModel,
|
||||
hasApiKey,
|
||||
apiKeyMasked,
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
try {
|
||||
const settings = (await getSettings()) as Record<string, unknown>;
|
||||
return NextResponse.json(buildQdrantSettingsResponse(settings));
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(QdrantSettingsUpdateSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
const body = validation.data;
|
||||
|
||||
try {
|
||||
const updates: Record<string, unknown> = {};
|
||||
if (body.enabled !== undefined) updates.qdrantEnabled = body.enabled;
|
||||
if (body.host !== undefined) updates.qdrantHost = body.host;
|
||||
if (body.port !== undefined) updates.qdrantPort = body.port;
|
||||
if (body.collection !== undefined) updates.qdrantCollection = body.collection;
|
||||
if (body.embeddingModel !== undefined) updates.qdrantEmbeddingModel = body.embeddingModel;
|
||||
if (body.apiKey !== undefined) {
|
||||
// Empty string = remove key
|
||||
updates.qdrantApiKey = body.apiKey === "" ? null : body.apiKey;
|
||||
}
|
||||
|
||||
const newSettings = (await updateSettings(updates)) as Record<string, unknown>;
|
||||
return NextResponse.json(buildQdrantSettingsResponse(newSettings));
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
40
src/app/api/settings/qdrant/search/route.ts
Normal file
40
src/app/api/settings/qdrant/search/route.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { validateBody, isValidationFailure } from "@/shared/validation/helpers";
|
||||
import { QdrantSearchSchema } from "@/shared/schemas/qdrant";
|
||||
import { searchSemanticMemory } from "@/lib/memory/qdrant";
|
||||
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error.ts";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return NextResponse.json(
|
||||
{ error: { message: "Invalid JSON body", details: [] } },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const validation = validateBody(QdrantSearchSchema, rawBody);
|
||||
if (isValidationFailure(validation)) {
|
||||
return NextResponse.json(validation.error, { status: 400 });
|
||||
}
|
||||
|
||||
const { query, topK } = validation.data;
|
||||
|
||||
try {
|
||||
const result = await searchSemanticMemory(query, topK);
|
||||
return NextResponse.json({
|
||||
ok: result.ok,
|
||||
results: result.results ?? [],
|
||||
});
|
||||
} catch (err: unknown) {
|
||||
const message = sanitizeErrorMessage(err instanceof Error ? err.message : String(err));
|
||||
return NextResponse.json({ error: { message } }, { status: 500 });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user